我有一个简单的函数,我为此编写了基准。我试图理解为什么一种实现每个操作比其他实现使用更多的字节,却减少了内存分配。
下面的实现每次分配使用疯狂的字节数,我不明白为什么要做的就是对索引进行更新
type Tags []string
BenchmarkTags_Pair2-12 241742 98126 ns/op 850149 B/op 1 allocs/op
func (t Tags) Pair() []string {
if len(t)&1 == 1 {
return t
}
for i := 0; i < len(t)/2; i++ {
// update at index
t[i] = t[i*2] + ":" + t[i*2+1]
}
return t[:len(t)/2]
}
第二个实现几乎相同,但是创建了一个数组,并向其中添加了项
BenchmarkTags_Pair2-12 14231110 71.2 ns/op 32 B/op 2 allocs/op
func (t Tags) Pair() []string {
if len(t)&1 == 1 {
return t
}
// new array created
tag := make([]string, len(t)/2)
for i := 0; i < len(t)/2; i++ {
// update at index
tag[i] = t[i*2] + ":" + t[i*2+1]
}
return tag
}
我希望最上面的实现比第二个实现更快,并且需要更少的分配。
下面的基准功能,使用:
列表项
func BenchmarkTags_Pair2(b *testing.B) {
tags := Tags([]string{"network", "foobar"})
var n []string
_ = n
b.ResetTimer()
for i := 0; i < b.N; i++ {
n = tags.Pair()
}
}
有人知道这里发生了什么吗?我不明白为什么在第一个实现中每个操作的字节会这么高。当我使用pprof进行检查时,我可以看到发生了gb的分配,并且GC的工作非常辛苦。
我没有找到任何原因的解释,但这肯定会发生,因为在第一个工作台中您正在修改方法接收器,而在第二个工作台中您只是在修改堆栈中分配的新切片。
我假设发生这种情况是因为t
在堆中已修改,而tag
在堆中已修改。