可比的 Benchmark 函数需满足:同一逻辑、同一输入规模、数据预生成在 b.ResetTimer() 前、结果必须被使用(如 _ = result)、避免非确定性操作;运行时须加 -benchmem -count=5 -benchtime=5s;对比用 benchstat 分析显著性差异。

直接用 go test -bench 跑多个 Benchmark 函数,看 ns/op 和 allocs/op 就行 —— 但前提是初始化一致、结果不被编译器优化掉、多轮采样才敢下结论。
怎么写可比的 Benchmark 函数
两个函数测的必须是同一逻辑、同一输入规模,否则对比毫无意义。比如测排序,不能一个用 []int{1,2,3},另一个用 []int{1,2,...,10000};也不能一个在循环里 make 切片,另一个复用全局变量。
- 数据预生成放在
b.ResetTimer()之前,确保只测核心逻辑耗时 - 结果必须“被使用”,否则 Go 编译器(尤其 1.21+)可能直接删掉整条调用链:
_ = result或赋值给局部变量再丢弃 - 避免在循环内做 I/O、
rand.Intn()、time.Now()等非确定性操作,会引入噪声或被优化 - 如果函数返回指针或结构体,别漏了
_ =,否则整个调用可能消失
怎么跑出稳定、可对比的结果
单次 go test -bench=. 输出只是快照,系统抖动、CPU 频率变化都可能让结果偏差 10% 以上。真实对比必须靠统计。
- 加
-count=5至少跑 5 轮,go test 自动取中位数,过滤异常值 - 加
-benchtime=5s让每轮至少运行 5 秒,避免默认 1 秒太短导致b.N波动大 - 加
-benchmem必须带,光看ns/op容易踩坑:一个函数快 20%,但allocs/op高 5 倍,高频调用下 GC 会拖垮整体吞吐 - 命令示例:
go test -bench=^BenchmarkSort -benchmem -count=5 -benchtime=5s
怎么科学对比新旧版本差异
别靠肉眼扫两行数字判断“变快了”。要用 benchstat 算显著性差异和提升比例,它才是 CI 里能自动告警的依据。
立即学习“go语言免费学习笔记(深入)”;
- 先装工具:
go install golang.org/x/perf/cmd/benchstat@latest - 老代码跑一次存文件:
go test -bench=. -benchmem -count=10 > old.txt - 改完再跑:
go test -bench=. -benchmem -count=10 > new.txt - 对比:
benchstat old.txt new.txt,输出类似StringConcat-8 1250ns ± 3% → 185ns ± 2% -85.20% - 注意负号表示变快,正号(如
+12.3%)才是性能退化,CI 应该拦截
最容易被忽略的三个细节
很多人写了 Benchmark 却得出错误结论,问题往往不在算法本身,而在测试写法。
-
b.ResetTimer()没放对位置:比如 map 初始化写在循环里,测出来的是make + range,不是纯遍历 - 忘了
-benchmem:一个函数ns/op更低,但allocs/op是另一版的 10 倍,线上跑几小时就 GC 飙升 - 不同 Benchmark 函数用了不同数据生成逻辑:比如一个用
make([]int, 1000),另一个用rand.Perm(1000),分布不一致,map 查找性能就失真
真正可靠的对比,90% 功夫花在让测试本身“干净”上 —— 数据一致、计时不掺水、结果不被优化、多轮有统计。剩下的,ns/op 和 benchstat 自会说话。











