这个问题是关于 访问 数组和切片 元素 的速度,而不是关于将它们作为参数传递给函数的效率。
我希望在大多数情况下, 数组 比 切片 更快,因为切片是描述数组连续部分的数据结构,因此访问切片的元素(间接访问其基础数组的元素)时可能涉及额外的步骤。
因此,我编写了一个小测试来对一批简单操作进行基准测试。有4个基准测试功能,前两个测试一个 全局 切片和一个全局数组,其他两个测试一个 本地 切片和一个本地数组:
var gs = make([]byte, 1000) // Global slice var ga [1000]byte // Global array func BenchmarkSliceGlobal(b *testing.B) { for i := 0; i < b.N; i++ { for j, v := range gs { gs[j]++; gs[j] = gs[j] + v + 10; gs[j] += v } } } func BenchmarkArrayGlobal(b *testing.B) { for i := 0; i < b.N; i++ { for j, v := range ga { ga[j]++; ga[j] = ga[j] + v + 10; ga[j] += v } } } func BenchmarkSliceLocal(b *testing.B) { var s = make([]byte, 1000) for i := 0; i < b.N; i++ { for j, v := range s { s[j]++; s[j] = s[j] + v + 10; s[j] += v } } } func BenchmarkArrayLocal(b *testing.B) { var a [1000]byte for i := 0; i < b.N; i++ { for j, v := range a { a[j]++; a[j] = a[j] + v + 10; a[j] += v } } }
我多次运行测试,这是典型的输出(go test -bench .*):
go test -bench .*
BenchmarkSliceGlobal 300000 4210 ns/op BenchmarkArrayGlobal 300000 4123 ns/op BenchmarkSliceLocal 500000 3090 ns/op BenchmarkArrayLocal 500000 3768 ns/op
分析结果:
如我所料,访问全局片比访问全局数组要慢一些: 4210vs 4123ns / op
4210
4123
但是访问本地切片比访问本地阵列要快得多: 3090vs 3768ns / op
3090
3768
我的问题是: 这是什么原因?
笔记
我尝试更改以下内容,但没有改变结果:
byte
int
比较了AMD64组装两者的BenchmarkArrayLocal和BenchmarkSliceLocal(太长,不适合在这个岗位):
BenchmarkArrayLocal
BenchmarkSliceLocal
阵列版本a实际上是在每次阵列访问操作中多次从内存加载地址:
a
LEAQ "".a+1000(SP),BX
从存储器加载一次后,分片版本仅在寄存器上计算:
LEAQ (DX)(SI*1),BX
这不是决定性的,但可能是原因。原因是这两种方法实际上是完全相同的。另一个值得注意的细节是数组版本调用runtime.duffcopy,这是一个相当长的汇编例程,而切片版本则没有。
runtime.duffcopy