

新闻资讯
技术学院Benchmark 函数必须接收 *testing.B 参数,因为 go test -bench 通过 B.N 控制执行次数并自动调优,忽略 B.N 会导致基准测试失效。
Benchmark 函数必须接收 *testing.B 参数Go 的基准测试不是靠你手动写 for 循环计时,而是由 go test -bench 驱动调用 B.N 次目标代码。框架会自动调整 B.N 值(从 1 开始指数增长),直到单次运行时间稳定在约 1 秒左右,再统计总耗时。如果你忽略 B.N、硬写 for i := 0; i ,结果就完全不可比,go test 甚至可能报 benchmarked function does not call b.N 错误。
B.N 是动态值,每次运行可能不同,不能假设为固定数字for i := 0; i 内部,否则不被计入测量范围
b.ResetTimer() 之前,避免污染测量testing.B 中 ResetTimer、StopTimer、StartTimer 的真实用途它们不是“暂停/恢复计时器”这种字面意思,而是控制「哪些代码段参与最终的纳秒级耗时统计」。默认整个函数体都计时;但你常需要排除 setup 或 cleanup 代码。
b.StopTimer():停止统计,后续代码不计入耗时(比如预热缓存、构造大数据)b.StartTimer():恢复统计(通常紧跟在准备动作之后)b.ResetTimer():清空已累计的耗时 + 重置迭代计数,**常用于跳过预热阶段**(例如前 100 次不计入,之后才开始正式计时)func BenchmarkMapAccess(b *testing.B) {
m := make(map[int]int)
for i := 0; i < 10000; i++ {
m[i] = i * 2
}
b.ResetTimer() // 丢弃上面建 map 的时间,从这里重新开始计时
for i := 0; i < b.N; i++ {
_ = m[i%10000] // 实际被测操作
}
}Go 编译器可能把无副作用的计算整个优化掉,导致测出 “0 ns/op”。同时,频繁分配会触发 GC,干扰真实性能。关键是要让结果“强制存活”且“不可省略”。
blackhole 变量承接返回值:result := expensiveFunc(); blackhole = result,其中 var blackhole interface{}
//go:noinline 注释(仅调试用,勿提交)go build -gcf
lags="-m" your_bench.go,避免意外堆分配fmt.Println、log.Print —— I/O 会严重拖慢并掩盖真实瓶颈go test -bench 输出的关键字段输出形如 BenchmarkSort-8 1000000 1245 ns/op 32 B/op 1 allocs/op,每个字段都有明确含义:
BenchmarkSort-8:函数名 + GOMAXPROCS 值(-8 表示用了 8 个 OS 线程)1000000:实际执行了 b.N 次(不是你写的固定数)1245 ns/op:每次操作平均耗时(核心指标)32 B/op:每次操作分配多少字节内存1 allocs/op:每次操作发生几次堆分配(越少越好)对比多个 benchmark 时,务必用 go test -bench=. -benchmem -count=5 多次运行取中位数,单次结果波动大。别只看 ns/op,B/op 和 allocs/op 在高并发或长周期服务里影响更大。