Dave Cheney在他的blog写了一篇关于Go的基准测试编写的基本介绍(链接)。我以此为内容,整理输出内容。

对自己编写package编写基准测试是必不可少的过程,尤其对于执行性能有重要影响的代码,更是需要。Go向来是以工具丰富而著称的,从代码格式调整,到单元测试,到竞争检查,以及我们接下来要介绍的基准测试,都提供方便的工具给开发者。

我们以Fibonacci函数来介绍。

1
2
3
4
5
6
7
//fib.go
func Fib(n int) int {
if n < 2 {
return n
}
return Fib(n-1) + Fib(n-2)
}

编写第一个benchmark testing:

1
2
3
4
5
6
7
//fib\_test.go
func BenchmarkFib10(b *testing.B) {
// 执行b.N 次Fib函数
for n := 0; n < b.N; n++ {
Fib(10)
}
}

以下几点需要注意:

  • 基准测试需要位于文件名为”{packagename}_test.go”的文件中,如这里的”fib_test.go”
  • 需要引入“testing”包
  • 基准测试函数需要以”Benchmark”为函数名的开头
  • 基准函数会执行许多遍。b.N的没次执行都会自增长,直到工具认为统计数据已经满足基准测试输出的要求。

完成以上代码,我们就可以调用工具执行测试了:

1
2
3
4
5
$ go test -bench=.
testing: warning: no tests to run
PASS
BenchmarkFib10 2000000 835 ns/op
ok test/fib 2.522s

注意:

  • 以上结果的第一行和第二行结果是go test执行的结果。
  • 与go test相似,-bench flag可接收一个有效的正则表达式来执行符合条件的测试函数。

同时,也轻易通过以下代码扩展,得到多个测试基准的结果:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
func benchmarkFib(i int, b *testing.B) {
for n := 0; n < b.N; n++ {
Fib(i)
}
}

func BenchmarkFib1(b *testing.B) { benchmarkFib(1, b) }
func BenchmarkFib2(b *testing.B) { benchmarkFib(2, b) }
func BenchmarkFib3(b *testing.B) { benchmarkFib(3, b) }
func BenchmarkFib10(b *testing.B) { benchmarkFib(10, b) }
func BenchmarkFib20(b *testing.B) { benchmarkFib(20, b) }
func BenchmarkFib40(b *testing.B) { benchmarkFib(40, b) }
```
运行结果:
``` sh
BenchmarkFib1 500000000 7.81 ns/op
BenchmarkFib2 100000000 18.4 ns/op
BenchmarkFib3 100000000 24.9 ns/op
BenchmarkFib10 1000000 1269 ns/op
BenchmarkFib20 20000 109198 ns/op
BenchmarkFib40 1 1780678179 ns/op
ok test/fib 14.852s

其他说明:

  • 每个测试的运行最小运行时间默认是1s,如果测试返回结果时,运行时间还没达到1s,b.N将以1, 2, 5, 10, 20, 50, … 的序列递增,然后重新运行测试代码。

  • 注意上面的结果中,BenchmarkFib40只运行了一次,这是由于它的执行太慢。为了得到更大的样例数据,你可以通过设置最小运行时间来达到。设置-benchtime flag的即可。

    1
    2
    3
    4
    go test  -bench=Fib40 -benchtime=10s
    PASS
    BenchmarkFib40 10 1711313264 ns/op
    ok test/fib 18.989s

特别注意:

  • 由于b.N是字增的,所以要谨慎用它来做函数参数。不要患下面这样的错误,否则,测试运行将没法终止。
    1
    2
    3
    4
    5
    6
    7
    8
    9
    func BenchmarkFibWrong(b *testing.B) {
    for n := 0; n < b.N; n++ {
    Fib(n)
    }
    }

    func BenchmarkFibWrong2(b *testing.B) {
    Fib(b.N)
    }