go基准测试必须用go test -bench启动,否则b.n为0、计时失效;函数名需以benchmark开头、文件名以_test.go结尾、参数为*testing.b;需用b.resettimer()隔离初始化开销,防编译器优化需赋值给全局变量或用无害副作用。

Go 的 Benchmark 函数不能手动调用,必须由 go test -bench 启动;否则 b.N 为 0、计时不生效、内存统计全归零,测出来的数字毫无参考价值。
go test -bench 命令怎么写才有效
命令必须显式带 -bench,否则基准测试函数完全不执行。常见错误是只写 go test 或误用 -run(它只管 Test*,对 Benchmark* 无效)。
-
go test -bench=.:运行当前包下所有匹配的基准函数 -
go test -bench=BenchmarkMapInsert:子串匹配,可能误中BenchmarkMapInsertParallel -
go test -bench=BenchmarkMapInsert\b:用\b确保精确匹配函数名末尾,推荐用于单测 - 不要加引号:
go test -bench="BenchmarkFoo"会被 shell 当作字面量传入,Go 测试框架收不到正则语义 - 如果包不在当前目录,需指定路径:
go test ./pkg/cache -bench=BenchmarkLRUGet\b
Benchmark 函数为什么总不运行或显示 0.00 ns/op
最常见原因是硬性条件没满足——Go 不报错也不提示,直接静默跳过。
- 文件名不是
*_test.go→go test -bench=.扫不到 - 函数名没以大写
Benchmark开头(如写成benchmarkFoo或TestBenchmarkFoo)→ 被忽略 - 参数不是
*testing.B(比如用了testing.B值类型、或*testing.T)→ 不识别 - 循环体里没用
b.N,而是写死for i := 0; i → <code>b.N失效,框架无法校准,常导致0.00 ns/op
如何避免编译器优化掉待测逻辑
编译器发现结果没被使用,会直接删掉整个循环。典型表现是 b.N 高达千万级,但 ns/op 低到反常识(如 0.32 ns/op)。
- 把返回值赋给全局变量:
var blackhole int; blackhole = compute(x) - 或做无害副作用:
_ = result & 1(Go 1.21+ 也可用b.ReportMetric(0, "op")) - 绝对不要在循环里用
fmt.Println、log.Print或time.Now()—— 它们虽能阻止优化,但引入 I/O 噪声,污染结果 - 验证是否真被优化:临时注释掉赋值语句,如果
ns/op骤降到 0,说明之前确实被删了
b.ResetTimer() 和 b.StopTimer() 必须什么时候用
初始化开销(如建大 map、预热数据、打开文件)如果不剥离,就会被摊进每次操作的耗时里,ns/op 变成“准备 + 执行”的混合值。
- setup 代码(如
m := make(map[int]int, 1e6))必须放在b.ResetTimer()之前 - 若 setup 很重且需多次复用,可用
b.StopTimer()+b.StartTimer()控制区间 - 状态重置(如
clear(m))也要放在计时区外,否则第二次循环测的是扩容后插入,不是首次行为 -
b.ReportAllocs()必须在b.ResetTimer()之后调用,否则内存统计包含 setup 阶段
真正容易被忽略的是:同一台机器上两次单独运行 go test -bench=. 的数值差异可能达 10%,这不是误差,而是 GC 周期、调度抖动、CPU 频率调节共同作用的结果;要对比两个实现,必须在同一次 go test 中用 -bench=BenchA|BenchB,或用 benchstat 做统计显著性判断。
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!











