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
为什么在字符串很长时在BenchmarkHashingWithNew900000000的情况下有分配但在字符串很小时没有分配。
Sum32:https://gowalker.org/github.com/spaolacci/murmur3
我正在使用go1.6
答案 0 :(得分:2)
您的基准测试正在观察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
的内容复制到新的[]byte
中。但是在这种情况下,编译器可以看到bytStr
返回后永远不再使用Sum32
。因此,它可以在堆栈上分配。但是,由于字符串可以任意大,因此对于分配string
或[]byte
的堆栈,限制设置为32个字节。
值得注意这个小技巧,因为如果您的基准字符串都很短,就很容易欺骗自己相信某些代码不会分配。
答案 1 :(得分:-1)
当您在byte.Buffer
中写入内容时,它会根据需要分配内存。当您调用byte.Buffer.Reset
时,该内存不会被释放,而是保留以供以后重用。而你的代码正是如此。它将缓冲区标记为空,然后再次填充它。
实际上, 正在进行某些分配,但是当迭代50000000次时,它可以忽略不计。但是,如果您将bufByte
的声明移到for
循环中,您将获得一些分配。