b.ResetTimer() 必须在预热及初始化完成后调用,否则会将 setup 时间计入基准测试结果,导致 ns/op 失真;它应置于 setup 之后、主循环之前,且不可在循环内重复调用。

b.ResetTimer() 必须在预热完成后调用,否则结果失真
基准测试报告的 ns/op 偏高,往往不是逻辑慢,而是把初始化时间算进去了。比如 make([]byte, 1e7) 花了 3ms,而真正要测的加密函数只耗 200ns,不重置就会显示 “3,000,200 ns/op”——这完全掩盖了真实性能。
- 初始化操作(构建大 slice、解析 JSON、编译正则、建 HTTP client)必须放在
b.ResetTimer()之前 -
b.ResetTimer()不能写在函数开头,否则会把包级init()、全局变量初始化也排除掉,这部分本属于真实调用链 - 常见错误现象:多个 benchmark 数值不可比、
ns/op随输入规模非线性暴涨、甚至出现负值(Go 1.21+ 会 panic)
别在循环里调用 b.ResetTimer(),它不是 for 每次迭代的开关
b.ResetTimer() 是“清零并重启”,不是“暂停/恢复”。它把已累计耗时和 alloc 计数全部归零,然后从这一刻起重新计时。在 for 循环里反复调用,等于每次只测最后一次迭代,其余全被截断。
- ✅ 正确位置:所有 setup 完成后、
for i := 0; i 开始前 - ❌ 错误写法:
for i := 0; i —— 这会导致计时器不断重置,最终结果趋近于 0 或直接 panic - defer
b.ResetTimer()无效:它会在函数返回时才执行,此时循环早已结束,计时早已完成
需要跳过中间杂活?用 b.StopTimer() + b.StartTimer(),不是 b.ResetTimer()
当你要排除的不是“一次性初始化”,而是“每次迭代都得做、但不该计入性能”的操作(比如重建 map、序列化输入、关闭响应体),b.ResetTimer() 就无能为力了——它只能清零一次,不能穿插使用。
-
b.StopTimer()暂停计时(不归零,只挂起),b.StartTimer()恢复计时;二者必须成对出现,漏一个就整段不计时 - 典型场景:HTTP client 测试中,只测
client.Do(),不测日志打印、resp.Body.Close() - 并发测试(
b.RunParallel)里不能用StopTimer/StartTimer,只能靠b.ResetTimer()控制全局起点
reset 和防优化是两回事,别指望 b.ResetTimer() 拦住编译器
b.ResetTimer() 只管计时边界,不管代码是否被执行。如果被测函数返回值没被使用,Go 编译器可能直接优化掉整个调用——哪怕你重置得再准,测的也是“空操作”。
立即学习“go语言免费学习笔记(深入)”;
- 务必保留副作用:用
_ = result不够可靠;推荐写blackhole(result),其中var blackhole = func(x interface{}) {} - 内存统计(
allocs/op)同样受b.ResetTimer()影响:它也会清零已分配字节数和对象数 - GC 抖动、调度延迟不会被自动消除;若需更稳定数据,可手动触发
runtime.GC()并等待收敛,再调用b.ResetTimer()


















