上QQ阅读APP看书,第一时间看更新
46.1 性能基准测试在Go语言中是“一等公民”
在前文中,我们已经接触过许多性能基准测试。和上一条所讲的模糊测试的境遇不同,性能基准测试在Go语言中是和普通的单元测试一样被原生支持的,得到的是“一等公民”的待遇。我们可以像对普通单元测试那样在*_test.go文件中创建被测对象的性能基准测试,每个以Benchmark前缀开头的函数都会被当作一个独立的性能基准测试:
func BenchmarkXxx(b *testing.B) { //... }
下面是一个对多种字符串连接方法的性能基准测试(改编自第15条):
// chapter8/sources/benchmark_intro_test.go var sl = []string{ "Rob Pike ", "Robert Griesemer ", "Ken Thompson ", } func concatStringByOperator(sl []string) string { var s string for _, v := range sl { s += v } return s } func concatStringBySprintf(sl []string) string { var s string for _, v := range sl { s = fmt.Sprintf("%s%s", s, v) } return s } func concatStringByJoin(sl []string) string { return strings.Join(sl, "") } func BenchmarkConcatStringByOperator(b *testing.B) { for n := 0; n < b.N; n++ { concatStringByOperator(sl) } } func BenchmarkConcatStringBySprintf(b *testing.B) { for n := 0; n < b.N; n++ { concatStringBySprintf(sl) } } func BenchmarkConcatStringByJoin(b *testing.B) { for n := 0; n < b.N; n++ { concatStringByJoin(sl) } }
上面的源文件中定义了三个性能基准测试:BenchmarkConcatStringByOperator、Benchmark-ConcatStringBySprintf和BenchmarkConcatStringByJoin。我们可以一起运行这三个基准测试:
$go test -bench . benchmark_intro_test.go goos: darwin goarch: amd64 BenchmarkConcatStringByOperator-8 12810092 88.5 ns/op BenchmarkConcatStringBySprintf-8 2777902 432 ns/op BenchmarkConcatStringByJoin-8 23994218 49.7 ns/op PASS ok command-line-arguments 4.117s
也可以通过正则匹配选择其中一个或几个运行:
$go test -bench=ByJoin ./benchmark_intro_test.go goos: darwin goarch: amd64 BenchmarkConcatStringByJoin-8 23429586 49.1 ns/op PASS ok command-line-arguments 1.209s
我们关注的是go test输出结果中第三列的那个值。以BenchmarkConcatStringByJoin为例,其第三列的值为49.1 ns/op,该值表示BenchmarkConcatStringByJoin这个基准测试中for循环的每次循环平均执行时间为49.1 ns(op代表每次循环操作)。这里for循环调用的是concatStringByJoin,即执行一次concatStringByJoin的平均时长为49.1 ns。
性能基准测试还可以通过传入-benchmem命令行参数输出内存分配信息(与基准测试代码中显式调用b.ReportAllocs的效果是等价的):
$go test -bench=Join ./benchmark_intro_test.go -benchmem goos: darwin goarch: amd64 BenchmarkConcatStringByJoin-8 23004709 48.8 ns/op 48 B/op 1 allocs/op PASS ok command-line-arguments 1.183s
这里输出的内存分配信息告诉我们,每执行一次concatStringByJoin平均进行一次内存分配,每次平均分配48字节的数据。