当我们尝试去优化代码的性能时,首先得知道当前的性能怎么样,得到一个基准性能。Go语言标准库内置的 testing 测试框架提供了benchmark的能力。本文主要介绍 如何使用benchmark 进行基准测试,以及如何提高benchmark 的精准度,最后介绍了两个工具,帮助我们更加方便的进行benchmark。
性能测试受环境的影响很大,为了保证测试的可重复性,在进行性能测试时,尽可能地保持测试环境的稳定。
benchmark 其实就是重复调用某个函数,然后记录函数的执行时间等指标,来度量它的性能。一个典型的benchamrk 函数如下所示:
func fib(n int) int {
if n < 2 {
return n
}
return fib(n-1) + fib(n-2)
}
func BenchmarkFib20(b *testing.B) {
for i := 0; i < b.N; i++ {
// Call the function we're benchmarking
fib(20)
}
}
benchmark 函数必须以 Benchmark 开始 我们可以这样运行它,都是等效的。
go test -bench .
go test -bench="BenchmarkFib20"
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 这样的序列递增,越到后面,增加得越快
count
控制运行次数,而不受由b.N 控制benchtime
控制运行时间,而受1s控制benchmem
度量内存分配的次数cpu
指定使用几个CPU核心,默认使用的是go
同时,benchmark 测试支持go test
的其他参数,比如cpuprofile
、memprofile
、trace
等perflock
作用是限制 CPU 时钟频率,从而一定程度上消除系统对性能测试程序的影响,减少结果的噪声,进而性能测量的结果方差更小也更加可靠。
如果在 benchmark 开始前,需要一些准备工作,如果准备工作比较耗时,则需要将这部分代码的耗时忽略掉。
StopTimer & StartTimer
也是相同原理,每次函数调用前后需要一些准备工作和清理工作,我们可以使用 StopTimer
暂停计时以及使用 StartTimer
开始计时。
benchstat 是官方提供的一个对比工具,用于比较两次benchmark之间的性能差别
Benchstat computes statistical summaries and A/B comparisons of Go benchmarks.
我们用benchstat对比一下 bubbleSort 跟quickSort 的性能
func softNums() {
//bubbleSort(nums)
//quickSort(nums)
}
funcinitNums(count int) {
rand.Seed(time.Now().UnixNano())
nums = make([]int, count)
for i := 0; i < count; i++ {
nums[i] = rand.Intn(count)
}
}
funcBenchmarkSoftNums(b *testing.B) {
initNums(10000)
b.ResetTimer()
for i := 0; i < b.N; i++ {
softNums()
}
}
go test -bench="BenchmarkSoftNums" -count=10 |tee quicksoft.txt
go test -bench="BenchmarkSoftNums" -count=10 |tee bubblesoft.txt
➜ benchmark git:(main) ✗ benchstat bubblesoft.txt quicksoft.txt
goos: darwin
goarch: arm64
pkg: blog-example/go/benchmark
│ bubblesoft.txt │ quicksoft.txt │
│ sec/op │ sec/op vs base │
SoftNums-10 31942.4µ ± 1% 775.8µ ± 2% -97.57% (p=0.000 n=10)
这样的结果是不是很清晰?我们可以把他放在报告或者github issue 中,很专业。
bench
是一个为 Go 程序提供集成性能测量、自动性能锁定、统计分析和颜色指示的基准测试工具。
funcbench
是 Prometheus 项目中用于自动化 Go 代码基准测试和性能比较的工具。以下是其主要特点和功能:
支持比较本地分支与GitHub 分支、性能锁定、性能比较 等特点,有利于提高开发效率。如果项目比较大,可以参考这种方式。
[1]bench: https://github.com/golang-design/bench
[2]funcbench: https://github.com/prometheus/test-infra/tree/master/funcbench
[3]https://dave.cheney.net/high-performance-go-workshop/gophercon-2019.html#welcome
[4]https://golang.design/under-the-hood/zh-cn/part3tools/ch09analysis/perf/