亚洲在线久爱草,狠狠天天香蕉网,天天搞日日干久草,伊人亚洲日本欧美

為了賬號安全,請及時綁定郵箱和手機立即綁定
已解決430363個問題,去搜搜看,總會有你想問的

如何在不將所有值清零的情況下初始化一個長Golang數組?

如何在不將所有值清零的情況下初始化一個長Golang數組?

Go
12345678_0001 2023-07-10 16:28:58
在 Go 中創建數組時,即使在初始化后立即設置不同的值,數組似乎也總是會被清零,例如當該值應設置為數組中的索引時。避免這種情況的一種方法是使用數組文字,例如a = [5]int{0,1,2,3,4},但對于長數組來說它變得不切實際。我想知道執行初始化的最佳方法是什么。令人驚訝的是,對于大型數組,命名返回函數的性能優于復合文字初始化。我創建了以下基準來比較性能:package mainimport "testing"const N = 1000000var result [N]intfunc arrayLiteral() [N]int {? ? // Replace the 3 dots with the actual value? ? // I copy-pasted the output of an other program to do this? ? return [N]int{0,1,2,3,...,N-1}}func arrayLoopNamedReturn() (a [N]int) {? ? for i := 0; i < N; i++ {? ? ? ? a[i] = i? ? }? ? return}func arrayLoop() [N]int {? ? var a [N]int? ? for i := 0; i < N; i++ {? ? ? ? a[i] = i? ? }? ? return a}func BenchmarkArrayLoop(b *testing.B) {? ? var r [N]int? ? for n := 0; n < b.N; n++ {? ? ? ? r = arrayLoop()? ? }? ? result = r}func BenchmarkArrayLoopNamedReturn(b *testing.B) {? ? var r [N]int? ? for n := 0; n < b.N; n++ {? ? ? ? r = arrayLoopNamedReturn()? ? }? ? result = r}func BenchmarkArrayLiteral(b *testing.B) {? ? var r [N]int? ? for n := 0; n < b.N; n++ {? ? ? ? r = arrayLiteral()? ? }? ? result = r}結果:N = 10,000BenchmarkArrayLoop-8? ? ? ? ? ? ? ? ? ? ? 200000? ? ? ? ? ? ? 9041 ns/opBenchmarkArrayLoopNamedReturn-8? ? ? ? ? ?200000? ? ? ? ? ? ? 6327 ns/opBenchmarkArrayLiteral-8? ? ? ? ? ? ? ? ? ?300000? ? ? ? ? ? ? 4300 ns/opN = 100,000BenchmarkArrayLoop-8? ? ? ? ? ? ? ? ? ? ? ?10000? ? ? ? ? ? 191582 ns/opBenchmarkArrayLoopNamedReturn-8? ? ? ? ? ? 20000? ? ? ? ? ? ?76125 ns/opBenchmarkArrayLiteral-8? ? ? ? ? ? ? ? ? ? 20000? ? ? ? ? ? ?62714 ns/opN = 1,000,000BenchmarkArrayLoop-8? ? ? ? ? ? ? ? ? ? ? ? ?500? ? ? ? ? ?2635713 ns/opBenchmarkArrayLoopNamedReturn-8? ? ? ? ? ? ?1000? ? ? ? ? ?1537282 ns/opBenchmarkArrayLiteral-8? ? ? ? ? ? ? ? ? ? ?1000? ? ? ? ? ?1854348 ns/op觀察結果:我沒想到命名返回值會對循環產生影響,我認為編譯器肯定會做一些優化。對于 1,000,000,它變得比文字初始化更快。我期望線性縮放,但我不明白為什么這兩種方法都不是這種情況。我不知道如何解釋這一點,盡管它似乎非?;尽S腥魏蜗敕▎??
查看完整描述

2 回答

?
梵蒂岡之花

TA貢獻1900條經驗 獲得超5個贊

結果與數組大小非線性的原因是因為并非獲取新填充數組所涉及的所有操作都與數組大小線性。例如,您需要內存分配,可以選擇將分配的內存清零,循環填充數組,并且必須返回(復制)數組的內存。分配是一個很好的例子,它不應該與大小成線性關系,而且,復制內存也不應該是線性的(應該增加,但不是線性的)。


避免使用冗長的復合文字并詢問需要清零并隨后填充的新數組值的一種方法是準備好該值,然后將其分配給數組變量。


我的意思是有一個包級變量存儲計算/填充的數組(最簡單的填充是一個簡單的循環),當您需要一個新的數組填充相同的數組時,只需分配存儲的值:


var cache [N]int


func init() {

    for i := range cache {

        cache[i] = i

    }

}


// If you now need a new array:

var result = cache

// Or re-init an existing array:

result = cache

如果您將其添加到您的基準中:


func BenchmarkArrayAssign(b *testing.B) {

    var r [N]int

    for n := 0; n < b.N; n++ {

        r = cache

    }

    result = r

}

或者簡單地:


func BenchmarkArrayAssign(b *testing.B) {

    for n := 0; n < b.N; n++ {

        result = cache

    }

}

這將比您迄今為止最快的速度快ArrayLoopNamedReturn 兩倍(當 時N = 1_000_000)。


BenchmarkArrayAssign-4                  1000       1104829 ns/op

BenchmarkArrayLoop-4                     500       3822005 ns/op

BenchmarkArrayLoopNamedReturn-4          500       2326498 ns/op


查看完整回答
反對 回復 2023-07-10
?
慕萊塢森

TA貢獻1810條經驗 獲得超4個贊

正如您所說,您可以使用文字初始化數組,或者數組將具有默認的零值。如果您能夠創建一個數組并稍后設置其內容,那么這兩個時刻之間的任何讀取訪問都將是未定義的(就像在 C 中一樣)。

我同意對大量元素使用數組文字是不切實際的,但這就是內存安全的代價:)


查看完整回答
反對 回復 2023-07-10
  • 2 回答
  • 0 關注
  • 152 瀏覽
慕課專欄
更多

添加回答

舉報

0/150
提交
取消
微信客服

購課補貼
聯系客服咨詢優惠詳情

幫助反饋 APP下載

慕課網APP
您的移動學習伙伴

公眾號

掃描二維碼
關注慕課網微信公眾號