benchstat 是专用于对比 go test -bench 多轮输出的分析工具,要求环境、参数、格式严格一致;Go 1.21+ 自带但需手动将 GOBIN 加入 PATH;仅解析标准纯文本基准输出,保存须用重定向,避免 stderr 混入和换行符错误;结果应重点关注每行 Δ 和 p 值而非 Geomean。

benchstat 不是运行工具,而是对比分析器;它只吃多轮 go test -bench 输出的原始文本文件,且必须保证两次测试环境、参数、格式完全一致,否则结果不可信。
benchstat 命令找不到?先看 Go 版本和 GOBIN
Go 1.21+ 默认自带 benchstat,但不会自动加进 $PATH —— 它放在 go env GOBIN 指向的目录里。旧版本(如 Go 1.20)压根没这个命令,go install golang.org/x/perf/cmd/benchstat@latest 装了也可能不兼容。
- 先执行
go version,确认是不是 ≥1.21 - 再跑
go env GOBIN,把输出路径加进 shell 的$PATH(比如export PATH="$(go env GOBIN):$PATH") - Mac M 系列用 Homebrew 装的 Go?
benchstat可能落在/opt/homebrew/bin/,但优先以GOBIN为准,别混着用
benchstat 解析失败:90% 是输出格式或保存方式错了
benchstat 只认标准 go test -bench 打印的纯文本行,例如 BenchmarkParseJSON-8 1000000 1234 ns/op。任何额外内容、截断、换行符错乱都会让它静默退出或报 no benchmarks to compare。
- 保存结果必须用重定向:
go test -bench=. -benchmem -count=5 > old.txt,别用tee或管道,缓冲可能导致末尾丢行 - 别混 stderr:
2>&1会把 panic 或构建错误塞进文件,benchstat直接跳过整行 - Windows 用户注意:确保
old.txt和new.txt都是 LF 换行(不是 CRLF),可用dos2unix或编辑器转 - 如果用了
-json,必须显式加benchstat -json old.json new.json,否则默认按文本解析失败
benchstat 输出怎么看?别被 Geomean 和百分比带偏
benchstat 第一行的 Geomean 是所有 benchmark 的几何平均归一化值,掩盖个体涨跌。真正该盯的是每行末尾的 Δ 列和 p= 值 —— 它们决定变化是否统计显著。
立即学习“go语言免费学习笔记(深入)”;
- 看到
-8.33% (p=0.12)?说明下降不显著,可能是噪声,别急着回滚 - 看到
+2.1% (p=0.003)?哪怕只涨一点点,只要 p - 想强制判断“有没有差异”,加
-delta-test=equal,它会用等价性检验替代 t-test,更适合微小改动场景 - 出现
NaN或inf?通常是某轮耗时为 0(比如函数太快被优化掉)或溢出,删掉对应行再跑,别留着污染统计
为什么 benchstat 说“没变化”,但服务明显变慢了?
因为 benchstat 只管你喂给它的那几个 BenchmarkXxx 函数,而真实性能退化常藏在别处:GC 压力上升、锁竞争加剧、网络栈开销、甚至编译器行为变化(比如 Go 1.22 对 map 的 inline 策略调整)。
- 单看
ns/op不够,一定要加-benchmem,关注B/op和allocs/op—— 内存暴涨往往比耗时增长更早暴露问题 - 基准测试里别让编译器优化掉关键逻辑:用全局变量接收返回值,或调用
b.ReportAllocs()间接阻止死码消除 - 跨版本对比时,必须固定
GOMAXPROCS、GOOS、GOARCH、构建标志(如-gcflags),连 CPU 频率都得尽量一致 - CI 中跑回归,别用笔记本或负载不稳的机器;本地调试仅作初筛,最终结论以 CI 固定规格节点为准



















