基准测试结果收集在类型值中testing.BenchmarkResult https://golang.org/pkg/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() https://golang.org/pkg/testing/#BenchmarkResult.AllocedBytesPerOp and BenchmarkResult.AllocsPerOp() http://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()
}
}
Here AvgHalfAllocs()
平均每个调用执行一半分配,它是通过从一半调用返回而不分配任何内容,并在另一半调用中执行 1 次分配来实现的。
AvgOneAndHalfAllocs()
平均每次调用进行 1.5 次分配,因为它调用AvgHalfAllocs()
3次。
The purpose of the falseCond
variable and the fmt.Println()
call is just so the compiler does not optimize away our allocation, but fmt.Println()
will never be called, so it won't interfere with the allocations.
对上述 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.5 的分配被截断为0
的情况下AvgHalfAllocs()
,并且在以下情况下 1.5 被截断为 1AvgOneAndHalfAllocs()
.
情况下的平均分配内存AvgHalfAllocs()
是 0.5 * 128 字节 = 64 字节。
情况下的平均分配内存AvgOneAndHalfAllocs()
是 1.5 * 128 字节 = 192 字节。