前往小程序,Get更优阅读体验!
立即前往
发布
社区首页 >专栏 >Go高性能编程 EP5: 更精准的benchmark

Go高性能编程 EP5: 更精准的benchmark

作者头像
用户11547645
发布2025-03-07 16:10:14
发布2025-03-07 16:10:14
6200
代码可运行
举报
文章被收录于专栏:萝卜要加油萝卜要加油
运行总次数:0
代码可运行

当我们尝试去优化代码的性能时,首先得知道当前的性能怎么样,得到一个基准性能。Go语言标准库内置的 testing 测试框架提供了benchmark的能力。本文主要介绍 如何使用benchmark 进行基准测试,以及如何提高benchmark 的精准度,最后介绍了两个工具,帮助我们更加方便的进行benchmark。

稳定的测试环境

性能测试受环境的影响很大,为了保证测试的可重复性,在进行性能测试时,尽可能地保持测试环境的稳定。

  • 机器处于闲置状态,测试时不要执行其他任务,也不要和其他人共享硬件资源。
  • 机器是否关闭了节能模式,一般笔记本会默认打开这个模式,测试时关闭。
  • 避免使用虚拟机和云主机进行测试,一般情况下,为了尽可能地提高资源的利用率,虚拟机和云主机 CPU 和内存一般会超售,超售机器的性能表现会非常地不稳定。
  • 一次测试是没有意义的,统计意义下可对比的结果是关键,对于一般的benchmark,我一般会重复执行20次。

benchmark 是如何工作的

benchmark 其实就是重复调用某个函数,然后记录函数的执行时间等指标,来度量它的性能。一个典型的benchamrk 函数如下所示:

代码语言:javascript
代码运行次数:0
复制
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 开始 我们可以这样运行它,都是等效的。

代码语言:javascript
代码运行次数:0
复制
go test -bench .
go test -bench="BenchmarkFib20"
b.N

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 的其他参数,比如cpuprofilememprofiletrace

提升准确度

降低系统噪音:perflock

perflock 作用是限制 CPU 时钟频率,从而一定程度上消除系统对性能测试程序的影响,减少结果的噪声,进而性能测量的结果方差更小也更加可靠。

ResetTimer

如果在 benchmark 开始前,需要一些准备工作,如果准备工作比较耗时,则需要将这部分代码的耗时忽略掉。

StopTimer & StartTimer

StopTimer & StartTimer 也是相同原理,每次函数调用前后需要一些准备工作和清理工作,我们可以使用 StopTimer 暂停计时以及使用 StartTimer 开始计时。

度量 benchmark

benchstat 是官方提供的一个对比工具,用于比较两次benchmark之间的性能差别

Benchstat computes statistical summaries and A/B comparisons of Go benchmarks.

我们用benchstat对比一下 bubbleSort 跟quickSort 的性能

代码语言:javascript
代码运行次数:0
复制
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()  
    }  
}
代码语言:javascript
代码运行次数:0
复制
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[1]

bench 是一个为 Go 程序提供集成性能测量、自动性能锁定、统计分析和颜色指示的基准测试工具。

funcbench[2]

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/

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2025-01-21,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 萝卜要加油 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 稳定的测试环境
  • benchmark 是如何工作的
    • b.N
    • 进阶参数
  • 提升准确度
    • 降低系统噪音:perflock
    • ResetTimer
    • StopTimer & StartTimer
  • 度量 benchmark
  • 一些第三方的工具
    • bench[1]
    • funcbench[2]
  • 引用链接
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档