简单整两句
Benchmark测试通常用于对代码片段的性能测试和对第三方库的性能进行评估测试。
Benchmark性能测试用例也是测试代码,所以它应该写在以_test结尾(xxx_test.go)的测试用例代码文件中。
形式如下:
跟普通的测试用例不一样的地方是,Benchmark性能测试用例函数的开头是以Benchmark开头的,而不Test开头的。
参数类型是testing.B,而不是testing.T。
在调用被测函数的前后,还需要添加几行辅助测试的代码,如ResetTimer、for循环、StopTimer。
上代码
package benchmark_test
import (
"bytes"
"testing"
)
func BenchmarkConcatStringByAdd(b *testing.B) {
elems := []string{"1", "2", "3", "4", "5"}
b.ResetTimer()
for i := 0; i < b.N; i++ {
ret := ""
for _, elem := range elems {
ret += elem
}
}
b.StopTimer()
}
func BenchmarkConcatStringByBytesBuffer(b *testing.B) {
elems := []string{"1", "2", "3", "4", "5"}
b.ResetTimer()
for i := 0; i < b.N; i++ {
var buf bytes.Buffer
for _, elem := range elems {
buf.WriteString(elem)
}
}
b.StopTimer()
}
代码中两个Benchmark测试函数分别用于测试+=和bytes.Buffer两种方法做字符串连接的性能。
运行Benchmark性能测试用例
$ go test -bench=.
goos: windows
goarch: amd64
pkg: bench
BenchmarkConcatStringByAdd-4 5479839 215 ns/op
BenchmarkConcatStringByBytesBuffer-4 12910694 89.6 ns/op
PASS
ok bench 2.921s
可以看到,两种字符串连接操作的性能差距是巨大的。
测试结果输出中的5479839和12910694是待测函数的运行次数,也就是for循环中的b.N,后面的215 ns/op和89.6 ns/op是每运行一次的耗时,单位的纳秒。
内存分析
如果想进一步了解造成性能差异的原因,可以在运行benchmark测试用例时再加删上-benchmem参数:
$ go test -bench=. -benchmem
goos: windows
goarch: amd64
pkg: bench
BenchmarkConcatStringByAdd-4 5384275 199 ns/op 16 B/op 4 allocs/op
BenchmarkConcatStringByBytesBuffer-4 13493425 89.4 ns/op 64 B/op 1 allocs/op
PASS
ok bench 2.893s
其中的“4 allocs/op”代表测试一次+=字符串连接会存在4次内存分配。
每次测试buf.WriteString连接字符串会存在1次内存分配。
所以,内存分配次数较少的方法性能会更好。