当golang为字符串到字节转换进行分配时
var testString = "ABCDEFGHIJKLMNOPQRSTUVWXYZ" //var testString = "ABCDEFGHIJKLMNOPQRSTUVWXYZABCDEFGHIJKLMNOPQRSTUVWXYZ" func BenchmarkHashing900000000(b *testing.B){ var bufByte = bytes.Buffer{} for i := 0; i < b.N ; i++{ bufByte.WriteString(testString) Sum32(bufByte.Bytes()) bufByte.Reset() } } func BenchmarkHashingWithNew900000000(b *testing.B){ for i := 0; i < b.N ; i++{ bytStr := []byte(testString) Sum32(bytStr) } } 测试结果: With testString = "ABCDEFGHIJKLMNOPQRSTUVWXYZ" BenchmarkHashing900000000-4 50000000 35.2 ns/op 0 B/op 0 allocs/op BenchmarkHashingWithNew900000000-4 50000000 30.9 ns/op 0 B/op 0 allocs/op With testString = "ABCDEFGHIJKLMNOPQRSTUVWXYZABCDEFGHIJKLMNOPQRSTUVWXYZ" BenchmarkHashing900000000-4 30000000 46.6 ns/op 0 B/op 0 allocs/op BenchmarkHashingWithNew900000000-4 20000000 73.0 ns/op 64 B/op 1 allocs/op 为什么在Bench’sHashingWithNew900000000的情况下,当字符串很长时有分配,但是当字符串很小时没有分配. 解决方法
您的基准测试正在观察Golang编译器(版本1.8)的奇怪优化.
你可以在这里看到Dmitry Dvyukov的公关 https://go-review.googlesource.com/c/go/+/3120 不幸的是,从很久以前,当编译器是用C语言编写的时候,我不确定在当前编译器中哪里可以找到优化.但我可以确认它仍然存在,而德米特里的公关描述是准确的. 如果你想要一套更清晰的自包含基准来证明这一点,我在这里有一个要点. https://gist.github.com/fmstephe/f0eb393c4ec41940741376ab08cbdf7e 如果我们只看第二个基准BenchmarkHashingWithNew900000000,我们可以看到它应该分配的明确位置. bytStr := []byte(testString) 该行必须将testString的内容复制到一个新的[]字节中.但是在这种情况下,编译器可以看到在Sum32返回后,从不再使用bytStr.因此,它可以在堆栈上分配.但是,由于字符串可以任意大,因此对于堆栈分配的字符串或[]字节,限制设置为32字节. 值得注意的是这个小技巧,因为如果您的基准字符串都很短,就很容易欺骗自己相信某些代码不会分配. (编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |