一、基准测试 Benchmark
在 Go 编程 | 连载 33 - UnitTest 单元测试 中实现了 Go 的单元测试用例,单元测试的一般形式为:
TestXxx(t *testing.T) 复制代码
除此之外 Go 的 testing
标准库还包含一个强大的基准测试,基准测试可以反复的运行函数,从而建立基准,并且无须执行运行次数,因为框架会通过调整次数来获得可靠的数据集,基准测试结束后将获得一个报告,该保函中包含了运行次数以及运行一次消耗的时间,单位为 ns。
基准测试函数的命名方式为 BenchmarkXxx
并且要求传入一个 *testing.B
类型。
BenchmarkXxx(b *testing.B) 复制代码
新建一个 fib.go 文件,增加一个函数 Fib
func Fib(n int) int { if n < 2 { return n } return Fib(n-1) + Fib(n-2) } 复制代码
新增一个测试文件 benchmark_fib_test.go,增加测试函数 BenchmarkFib
func BenchmarkFib(b *testing.B) { for n := 0; n < b.N; n++ { Fib(20) } } 复制代码
执行该测试文件,输出内容如下:
goos: darwin goarch: amd64 pkg: go-advanced/10-test cpu: Intel(R) Core(TM) i7-8557U CPU @ 1.70GHz BenchmarkFib BenchmarkFib-8 26546 45176 ns/op PASS 复制代码
使用 IDEA 运行基准测试时,要注意基准测试用例的名字一定要是 BenchmarkXxx
形式,否则会报错,将函数名改为 FibTest
,再次执行测试。
基准测试函数的名字为 BenchmarkXxx
时会自动使用 go bench
执行测试。
基准测试可以通过程序来确定完成特定任务时性能最佳的方式是哪一种。
新建一个 tango.go 文件,定义三个函数实现拼接字符串。
func StringFromAssignment(x int) string { var s string for i := 0; i < x; i++ { s += "tango" } return s } func StringFromAppendJoin(x int) string { s := []string{} for i := 0; i < x; i++ { s = append(s, "tango") } return strings.Join(s, "") } func StringFromBuffer(x int) string { var buffer bytes.Buffer for i := 0; i < x; i++ { buffer.WriteString("tango") } return buffer.String() } 复制代码
同级目录下新建一个 tango_benchmark_test.go 文件,定义三个函数的基准测试函数。
func BenchmarkStringFromAssignment(b *testing.B) { for i := 0; i < b.N; i++ { StringFromAssignment(100) } } func BenchmarkStringFromAppendJoin(b *testing.B) { for i := 0; i < b.N; i++ { StringFromAppendJoin(100) } } func BenchmarkStringFromBuffer(b *testing.B) { for i := 0; i < b.N; i++ { StringFromBuffer(100) } } 复制代码
执行基准测试,输出结果如下
goos: darwin goarch: amd64 pkg: go-advanced/10-test cpu: Intel(R) Core(TM) i7-8557U CPU @ 1.70GHz BenchmarkStringFromAssignment BenchmarkStringFromAssignment-8 148418 7382 ns/op BenchmarkStringFromAppendJoin BenchmarkStringFromAppendJoin-8 555007 2001 ns/op BenchmarkStringFromBuffer BenchmarkStringFromBuffer-8 1422670 818.5 ns/op PASS 复制代码
可以看出使用 StringFromBuffer 函数拼接字符串耗时最短,效率最高。
二、testing.B
类型
*testing.B
类型是基准测试函数的入参,类似单元测试中的 *testing.T
,也适用于管理测试的行为。
*testing.B
和 *testing.T
类型一样都组合了 common
结构体,所有也都拥有 FailNow
、SkipNow
、Skipf
等方法来结束测试并输出格式化错误信息。
*testing.B
类型可以用于管理基准测试的计时行为,有三个方法用于计时:
- StartTimer:开始对测试进行计时。该方法会在基准测试开始时自动被调用,也可以在调用 StopTimer 之后恢复计时;
- StopTimer:停止对测试进行计时。当需要执行一些复杂的初始化操作,并且不想对这些操作进行测量时,就可以使用这个方法来暂时地停止计时;
- ResetTimer:对已经逝去的基准测试时间以及内存分配计数器进行清零。对于正在运行中的计时器,这个方法不会产生任何效果。
基准测试还可以统计内存消耗以及指定运行时间等,只需要在命令行运行时添加相应的参数即可。