这是我参与「第三届青训营 -后端场」笔记创作活动的的第一篇笔记。```
1.稳定的测试环境
Go语言标准库内置的testing测试框架提供了基准测试(benchmark)的能力,能让我们很容易地对某一段代码进行性能测试。 性能测试受环境的影响很大,为了保证测试的可重复性,在进行性能测试时,尽可能地保持测试环境的稳定。
- 机器处于闲置状态,测试时不要执行其他任务,也不要和其他人共享硬件资源。
- 机器是否关闭了节能模式,一般笔记本会默认打开这个模式,测试时关闭。
- 避免使用虚拟机和云主机进行测试,一般情况下,为了尽可能地提高资源的利用率,虚拟机和云主机 CPU 和内存一般会超分配,超分机器的性能表现会非常地不稳定。
2.benchmark的使用
2.1 一个简单的例子
Go语言标准库内置了支持benchmark的testing库,接下来看一个简单的例子:***
使用go mod init example初始化一个模块,新增fib.go文件,实现函数fib,用于计算第N个斐波那契数。
// fib.go
package main
func fib(n int) int {
if n == 0 || n == 1 {
return n
}
return fib(n-2) + fib(n-1)
}
接下来,我们在 fib_test.go 中实现一个 benchmark 用例:
// fib_test.go
package main
import "testing"
func BenchmarkFib(b *testing.B) {
for n := 0; n < b.N; n++ {
fib(30) // run fib(30) b.N times
}
}
- benchmark 和普通的单元测试用例一样,都位于
_test.go文件中。 - 函数名以
Benchmark开头,参数是b *testing.B。和普通的单元测试用例很像,单元测试函数名以test开头,参数是t *testing.T。
2.2运行示例
go test <module name>/<package name>用来运行某个package内的所有测试用例
- 运行当前package内用例:
go test example或go test . - 运行子package内的用例:
go test example/<package name>或go test ./<package name> - 如果想递归测试当前目录下的所有的package:
go test ./...或go test example/...。
go test命令默认不运行benchmark用例的,如果我们想运行benchmark用例,作者需要加上-bench参数。例如:
$ go test -bench .
goos: darwin
goarch: amd64
pkg: example
BenchmarkFib-8 200 5865240 ns/op
PASS
ok example 1.782s
-bench 参数支持传入一个正则表达式,匹配到的用例才会得到执行,例如,只运行以 Fib 结尾的 benchmark 用例:
$ go test -bench='Fib$' .
goos: darwin
goarch: amd64
pkg: example
BenchmarkFib-8 202 5980669 ns/op
PASS
ok example 1.813s
2.3 benchmark 是如何工作的
benchmark 用例的参数 b *testing.B,有个属性 b.N 表示这个用例需要运行的次数。b.N 对于每个用例都是不一样的。
那这个值是如何决定的呢?b.N 从 1 开始,如果该用例能够在 1s 内完成,b.N 的值便会增加,再次执行。b.N 的值大概以 1, 2, 3, 5, 10, 20, 30, 50, 100 这样的序列递增,越到后面,增加得越快。我们仔细观察上述例子的输出:
BenchmarkFib-8 202 5980669 ns/op
BenchmarkFib-8 中的 -8 即 GOMAXPROCS,默认等于 CPU 核数。可以通过 -cpu 参数改变 GOMAXPROCS,-cpu 支持传入一个列表作为参数,例如:
$ go test -bench='Fib$' -cpu=2,4 .
goos: darwin
goarch: amd64
pkg: example
BenchmarkFib-2 206 5774888 ns/op
BenchmarkFib-4 205 5799426 ns/op
PASS
ok example 3.563s
在这个例子中,改变 CPU 的核数对结果几乎没有影响,因为这个 Fib 的调用是串行的。
202 和 5980669 ns/op 表示用例执行了 202 次,每次花费约 0.006s。总耗时比 1s 略多