Go基准测试需显式启用,正确命令为go test -bench=.并配合-benchmem、b.ResetTimer()及多轮采样,否则结果失真不可信。

不加 -bench 就等于没跑基准测试
Go 的基准测试不会随 go test 默认执行——这是和单元测试最根本的区别。只写 go test,哪怕文件里有 BenchmarkXXX 函数,也完全被忽略,只跑 TestXXX(如果有的话)。
- 正确启动命令是:
go test -bench=.(注意等号后是英文点.,不是星号或通配符) -
.是正则表达式,表示“匹配任意字符串”,即所有以Benchmark开头的函数 - 常见错误:
go test -bench(漏掉=.)→ 报错flag provided but not defined: -bench - 另一个坑:
go test -bench="BenchmarkAdd"在某些 shell 下引号会截断或转义失败,推荐写成go test -bench=BenchmarkAdd或用单引号
-benchmem 不是可选,是性能真相的开关
只看 ns/op 是危险的。很多性能问题其实卡在内存分配上,而默认输出根本不显示这些数据。
- 必须加
-benchmem,否则你永远看不到X B/op和Y allocs/op这两列关键指标 -
112 B/op 3 allocs/op意味着每次调用平均分配 112 字节、触发 3 次堆分配——这直接关联 GC 压力 - 如果
allocs/op > 0但B/op == 0,很可能是小对象逃逸,可用go build -gcflags="-m"看逃逸分析 - 高频分配?检查是否在循环里反复
make([]T, n)或新建map,考虑预分配或sync.Pool
b.ResetTimer() 不是建议,是必写项
初始化代码(比如切片预分配、map 构建、读配置、设随机种子)如果写在循环外又不重置计时器,就会把 setup 时间算进结果里,导致数据严重失真。
- 典型结构:
src := make([]int, 1000)→b.ResetTimer()→for i := 0; i - 漏掉
b.ResetTimer()后,一个初始化耗时 500ms 的 benchmark 可能报出50 ns/op—— 完全不可信 - 别在循环里写
if i == 0 { setup() },这种“伪初始化”仍会被计时,且破坏b.N的统计意义 - 副作用操作(如改全局变量、写文件)要确保每次迭代独立,否则
b.N增大后行为可能突变
想比性能?别只信一次 go test -bench 输出
单次运行受系统负载、GC、CPU 频率波动影响太大,ns/op 数值抖动常达 ±15%。真实对比必须靠多轮采样 + 统计工具。
- 日常调优推荐组合:
go test -bench=. -benchmem -benchtime=3s -count=3 -
-benchtime=3s让每轮至少跑够 3 秒,降低单次误差;-count=3表示重复整个 benchmark 3 次 - 对比优化前后结果,要用官方工具
benchstat(需先go install golang.org/x/perf/cmd/benchstat@latest) - 并发场景别手写 goroutine:用
b.RunParallel,否则 goroutine 数量失控、调度混乱,结果毫无参考价值
真正难的不是写对 BenchmarkXXX 函数,而是让初始化、计时、采样、对比全都落在同一逻辑层上。一环松动,整组数据就失去可比性。










