在 Go 中创建数组时,即使在初始化后立即设置不同的值,数组似乎也总是会被清零,例如当该值应设置为数组中的索引时。
避免这种情况的一种方法是使用数组文字,例如a = [5]int{0,1,2,3,4}
,但对于长数组来说它变得不切实际。我想知道执行初始化的最佳方法是什么。
令人惊讶的是,对于大型数组,命名返回函数的性能优于复合文字初始化。
我创建了以下基准来比较性能:
package main
import "testing"
const N = 1000000
var result [N]int
func 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,000
BenchmarkArrayLoop-8 200000 9041 ns/op
BenchmarkArrayLoopNamedReturn-8 200000 6327 ns/op
BenchmarkArrayLiteral-8 300000 4300 ns/op
N = 100,000
BenchmarkArrayLoop-8 10000 191582 ns/op
BenchmarkArrayLoopNamedReturn-8 20000 76125 ns/op
BenchmarkArrayLiteral-8 20000 62714 ns/op
N = 1,000,000
BenchmarkArrayLoop-8 500 2635713 ns/op
BenchmarkArrayLoopNamedReturn-8 1000 1537282 ns/op
BenchmarkArrayLiteral-8 1000 1854348 ns/op
观察结果:
我没想到命名返回值会对循环产生影响,我认为编译器肯定会做一些优化。对于 1,000,000,它变得比文字初始化更快。
我期望线性缩放,但我不明白为什么这两种方法都不是这种情况。
我不知道如何解释这一点,尽管它似乎非常基本。有任何想法吗 ?
梵蒂冈之花
慕莱坞森