Benchmem的输出

时间:2019-05-22 10:53:52

标签: go performance-testing

使用内存分析器运行基准测试时,我看到以下输出

SomeFunc             100      17768876 ns/op         111 B/op          0 allocs/op

我不明白输出-0 allocs / op,但是分配了111 B?知道这意味着什么吗?我的函数是否在堆上分配内存?

1 个答案:

答案 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字节。