要获得可比的 go test -bench 结果,必须控制变量:固定 Go 版本、编译参数、测试数据;禁用后台干扰;用 -count=10 和 -benchmem;输出存为 old.txt/new.txt 后用 benchstat 基于 p 值判断显著性。

怎么跑出可比的 go test -bench 结果
基准测试结果不可比,八成是因为没控制变量。Go 的 go test -bench 默认会动态调整运行次数(-benchmem 也默认开),但不同机器、不同 Go 版本、甚至同一台机器上 CPU 频率波动都会让单次结果抖动很大。
- 必须加
-count=5(或更高,推荐 10)——单次 bench 只是采样,多次才能算统计分布 - 务必加
-benchmem,否则内存分配指标不输出,后续benchstat没法对比分配差异 - 避免后台程序干扰:关掉 IDE、浏览器、同步工具;Mac 用户注意
coreservicesd可能偷 CPU,用sudo powermetrics --samplers smc观察 - 别用
-bench=.扫全包:函数名含下划线(如BenchmarkFoo_v2)会被跳过,显式写-bench=BenchmarkFoo
benchstat 怎么读出「变快了还是变慢了」
benchstat 不是看平均值差多少,而是做 Welch’s t-test 判断差异是否显著。它输出的 p=0.001 或 p=0.42 才是关键,不是那行「1.23x faster」。
- 安装:
go install golang.org/x/perf/cmd/benchstat@latest(注意路径里有perf,不是tools) - 输入必须是原始 bench 输出(带
BenchmarkXXX行和ns/op的文本),不是 JSON 或摘要;保存为old.txt和new.txt再执行benchstat old.txt new.txt - 如果某列显示
geomean而不是具体函数名,说明两组数据函数名不完全匹配(比如一个叫BenchmarkParseJSON,另一个叫BenchmarkJSONParse) - 看到
p=0.049别急着下结论——p 仅表示「不太可能纯靠运气出现这差异」,不代表性能提升 5%;真要量化提升,看 <code>delta列的置信区间(如-12.3% ± 3.1%)
为什么 benchstat 报错「no benchmarks found」
这不是 benchstat 的 bug,是输入格式没对齐。它只认标准 go test -bench 输出的原始文本,且要求每行以 Benchmark 开头、包含 ns/op 和 B/op。
- 别复制终端里带颜色的输出(ANSI 转义符会破坏解析),用重定向:
go test -bench=. -count=5 -benchmem > bench-old.txt - 别手动删行或改字段顺序(比如把
1234 ns/op改成1.234 µs/op),benchstat不做单位换算 - 如果用了
-cpuprofile或-memprofile,这些输出会混在 bench 日志里,导致解析失败;profile 要单独重定向到其他文件 - Windows 用户注意换行符:用
unix2dos或 VS Code 保存为 LF,否则benchstat在 Linux/macOS 下可能读不到行尾
对比两个分支时容易漏掉的硬约束
想比 main 分支和 feat/xxx 分支的性能差异?光跑 bench 不够,环境一致性才是瓶颈。
立即学习“go语言免费学习笔记(深入)”;
- Go 版本必须一致:
go version输出要完全相同(包括 patch 号),1.21.0和1.21.1的 runtime 调度器行为可能不同 - 编译参数要一致:如果用了
-gcflags或-ldflags,两边都得带上;特别是-gcflags="-l"(禁用内联)会极大影响结果,但常被遗忘 - 测试数据不能变:如果 benchmark 里用了
rand.Read生成输入,两次跑出来的数据分布不同,结果就不可比;应固定 seed 或预生成 byte slice - 别在 CI 上直接跑对比:CI 机器通常共享 CPU,
benchstat的 p 值会失真;本地实机 +taskset -c 0-3绑核更可靠
真正卡住人的从来不是命令怎么敲,而是「以为控制住了变量,其实没控住」——比如 GOPROXY、GOCACHE、甚至 /tmp 目录的磁盘速度,都可能悄悄拖慢某次采样。跑完先看 ns/op 的标准差,超过均值 10%,这组数据就该丢掉重跑。


















