结论:基准测试写在 _test.go 里以 BenchmarkXxx(b *testing.B) 命名,go test -bench=. -benchmem 运行;框架自动调整 b.N 使测试时长足够,循环体 for i := 0; i < b.N; i++。靠谱的关键是:重置计时排除准备成本、防止编译器优化掉被测代码、报告分配情况。
展开:范式与错误对照:
func BenchmarkConcat(b *testing.B) {
b.ReportAllocs() // 报告 B/op 和 allocs/op
parts := genParts(1000) // 准备数据:计时外
b.ResetTimer() // 重置计时,排除准备开销
var sink string
for i := 0; i < b.N; i++ {
sink = concat(parts) // 结果赋给包级 sink 防优化
}
_ = sink
}
常见错误清单:1)忘记 ResetTimer——初始化时间被计入;2)结果未使用——编译器内联后发现无副作用,整个调用被消除,测出"纳秒级"假象(sink 模式或包级变量解决);3)在循环内做数据准备/随机数生成,测的是准备而非目标;4)忽略 allocs/op——很多优化(sync.Pool、预分配)的收益体现在分配次数而非纯时间;5)单次运行下结论——用 -count=10 + benchstat 做统计对比,避免环境噪声;6)b.RunParallel 测并发扩展性时忘了每个 P 有自己的 pb.Next 循环。易错点:timer 分辨率与短函数——被测函数快到几十纳秒时,循环开销占比升高,结果解释要谨慎。
追问方向:benchstat 的显著性检验、pprof 对 benchmark 产物做 CPU/内存剖析(-cpuprofile/-memprofile)。