使用内存分析器运行基准测试时,我看到以下输出
SomeFunc 100 17768876 ns/op 111 B/op 0 allocs/op
我不明白输出-0 allocs / op,但是分配了111 B?知道这意味着什么吗?我的函数是否在堆上分配内存?
答案 0 :(得分:4)
基准测试结果以testing.BenchmarkResult
类型的值收集:
type BenchmarkResult struct {
N int // The number of iterations.
T time.Duration // The total time taken.
Bytes int64 // Bytes processed in one iteration.
MemAllocs uint64 // The total number of memory allocations; added in Go 1.1
MemBytes uint64 // The total number of bytes allocated; added in Go 1.1
}
BencharkResult.AllocedBytesPerOp()
和BenchmarkResult.AllocsPerOp()
返回您为操作分配的内存和分配看到的值。他们记录了返回值是:
AllocedBytesPerOp返回 r.MemBytes / r.N 。
AllocsPerOp返回 r.MemAllocs / r.N 。
所以结果是整数除法。这意味着,如果基准测试函数在不同的调用中执行不同数量的分配,则结果可能不是整数,而是小数部分被丢弃(这是整数除法的工作原理)。
因此,如果平均一个函数执行的分配少于1个,您将看到0 allocs/op
,但是如果平均每个调用至少1个字节,则分配的内存可能大于0。
让我们看一个例子:
var (
counter int
falseCond bool // Always false at runtime
)
func AvgHalfAllocs() {
counter++
if counter%2 == 0 {
return
}
buf := make([]byte, 128)
if falseCond {
fmt.Println(buf)
}
}
func AvgOneAndHalfAllocs() {
for i := 0; i < 3; i++ {
AvgHalfAllocs()
}
}
这里AvgHalfAllocs()
平均每个呼叫分配一半,它是从一半的呼叫返回而没有分配任何东西,而在另一半呼叫中恰好进行1次分配。
AvgOneAndHalfAllocs()
平均每个呼叫进行1.5个分配,因为它调用了AvgHalfAllocs()
3次。
falseCond
变量和fmt.Println()
调用的目的仅仅是为了使编译器不会优化我们的分配,但是fmt.Println()
永远不会被调用,因此它赢了不会干扰分配。
对上述2个功能进行基准测试,如下所示:
func BenchmarkAvgHalfAllocs(b *testing.B) {
for i := 0; i < b.N; i++ {
AvgHalfAllocs()
}
}
func BenchmarkAvgOneAndHalfAllocs(b *testing.B) {
for i := 0; i < b.N; i++ {
AvgOneAndHalfAllocs()
}
}
结果是:
BenchmarkAvgHalfAllocs-4 50000000 29.2 ns/op 64 B/op 0 allocs/op
BenchmarkAvgOneAndHalfAllocs-4 20000000 92.0 ns/op 192 B/op 1 allocs/op
如您所见,在0
的情况下,每个呼叫的0.5个分配被截断为AvgHalfAllocs()
,在AvgOneAndHalfAllocs()
的情况下,每个呼叫的1.5个分配被截断为1。
在AvgHalfAllocs()
的情况下,平均分配的内存为0.5 * 128字节= 64字节。
在AvgOneAndHalfAllocs()
的情况下,平均分配的内存为1.5 * 128字节= 192字节。