type Item struct {
A int
B [1024]byte
}
func BenchmarkRange1(b *testing.B) {
s := make([]Item, 1024)
for i := 0; i < b.N; i++ {
for _, v := range s {
_ = v.A
}
}
}
func BenchmarkRange2(b *testing.B) {
s := make([]Item, 1024)
for i := 0; i < b.N; i++ {
for i := range s {
_ = s[i].A
}
}
}
现在来看一下基准测试的结果。
go test -bench=BenchmarkRange -benchmem main/copy
goos: darwin
goarch: amd64
pkg: main/copy
cpu: Intel(R) Core(TM) i7-9750H CPU @ 2.60GHz
BenchmarkRange1-12 4577601 260.9 ns/op 0 B/op 0 allocs/op
BenchmarkRange2-12 4697178 254.9 ns/op 0 B/op 0 allocs/op
PASS
ok main/copy 3.391s
当范围按值切片时,不是要复制元素吗?为什么表演是一样的?当我们按值调整切片时,编译器会做什么优化?
当我通过编译选项“-gc频标=-N”优化编译器时,我将得到预期的结果:
go test -bench=BenchmarkRange -benchmem -gcflags=-N main/copy
goos: darwin
goarch: amd64
pkg: main/copy
cpu: Intel(R) Core(TM) i7-9750H CPU @ 2.60GHz
BenchmarkRange1-12 39004 29481 ns/op 27 B/op 0 allocs/op
BenchmarkRange2-12 777356 1572 ns/op 1 B/op 0 allocs/op
PASS
ok main/copy 3.169s
谁能解释编译器是如何优化的。
发布于 2022-03-18 19:20:11
使用默认的优化,BenchmarkRange1
和BenchmarkRange2
中的内环将被编译成一个空循环,循环有1024次迭代,就好像您编写了内环一样:
for i := 0; i < 1024; i++ {
}
在这两个示例中,编译器都足够聪明地认识到您没有在内部循环中执行任何操作(也就是说,没有使用v
、v.A
、s[i]
或s[i].A
)。
go.godbolt.org是查看Go编译器生成的程序集的一个很好的资源。例如,BenchmarkRange1
中的内循环被编译成以下内容(将AX归零,然后循环1024次):
XORL AX, AX
Range1_pc39:
INCQ AX
CMPQ AX, $1024
JLT Range1_pc39
您可以在这里查看完整的输出,以及(通常)解释不同程序集说明的便捷工具提示:https://go.godbolt.org/z/raTPjTrYG
(为了缩短示例,我删除了测试包;//go:nosplit
注释并不是真正需要的,但稍微简化了生成的程序集)。
https://stackoverflow.com/questions/71507307
复制相似问题