【发布时间】:2019-05-21 05:07:49
【问题描述】:
我想知道 Go 中的 len 运算符有多快,我编写了一个简单的基准测试。我的期望是通过避免在每次循环迭代期间调用len,代码会运行得更快,但实际上恰恰相反。
这是基准:
func sumArrayNumber(input []int) int {
var res int
for i, length := 0, len(input); i < length; i += 1 {
res += input[i]
}
return res
}
func sumArrayNumber2(input []int) int {
var res int
for i := 0; i < len(input); i += 1 {
res += input[i]
}
return res
}
var result int
var input = []int{3, 6, 22, 68, 11, -7, 22, 5, 0, 0, 1}
func BenchmarkSumArrayNumber(b *testing.B) {
var r int
for n := 0; n < b.N; n++ {
r = sumArrayNumber(input)
}
result = r
}
func BenchmarkSumArrayNumber2(b *testing.B) {
var r int
for n := 0; n < b.N; n++ {
r = sumArrayNumber2(input)
}
result = r
}
结果如下:
goos: windows
goarch: amd64
BenchmarkSumArrayNumber-8 300000000 4.75 ns/op
BenchmarkSumArrayNumber2-8 300000000 4.67 ns/op
PASS
ok command-line-arguments 4.000s
我通过以下操作确认了抵抗是一致的:
- 将输入数组大小加倍大约会使每个操作的执行时间加倍。速度差异随输入数组的长度而变化。
- 交换测试订单不会影响结果。
为什么每次循环迭代时检查 len() 的代码更快?
【问题讨论】:
-
这是无法回答的:编译器没有义务不重写您的代码,并且可能会或可能不会将两个版本优化为相同的代码,甚至将您认为较慢的版本优化为较快的版本。微基准测试非常难做,甚至更难分析(因此大多是浪费时间)。
-
对 len 的调用编译为切片头中的字段访问。
-
编译器可能会检测到
input的值没有改变,因此len(input)也没有改变,并且可能只评估一次,根本没有性能损失,就像你经历过。
标签: performance go performance-testing