直接用 time.now() 测不准,因其绕过 testing.b 的自动预热、b.n 动态调优、gc 上下文共享等机制,且受调度抢占、cpu 频率波动、后台干扰等影响,误差常超 ±30%;必须用 *testing.b 并严格遵循 b.n 循环、b.resettimer() 等规范。

为什么直接写 time.Now() 测不准
手动用 time.Now() 套个循环测耗时,结果基本不可信。Go 编译器可能把整个计算优化掉(尤其结果没被使用时),ns/op 显示 0.32 纳秒这种反常识数字就是典型信号;更关键的是,它完全不控制 GC 触发时机、CPU 频率波动、后台进程抢占——同一台机器上两次运行,误差 ±30% 都算客气。
- 必须用
testing.B,框架会自动预热、动态调优b.N、共享 GC 上下文 - 初始化(如
make(map[int]int)、构造测试数据)必须放在b.ResetTimer()之前 - 被测逻辑里禁止
fmt.Println、rand.Intn()、文件 I/O —— 它们引入噪声,还可能让编译器保留无意义的副作用
go test -bench 跑不起来的常见卡点
输出 no benchmarks to run 或函数名压根不出现,90% 是签名或文件名不对。
Go语言(Golang)1.26.0版本提供 Go 官方 Windows amd64 MSI 安装包下载入口,版本号 1.26.0,可用于旧项目维护、兼容性测试和指定版本开发环境配置。
- 函数名必须是
BenchmarkXxx(首字母大写,不能小写或下划线) - 参数类型必须是
*testing.B,写成*testing.T或testing.B都无效 - 文件名必须是
xxx_test.go,放在普通.go文件里不会被扫描 - 循环体必须严格写成
for i := 0; i ,漏掉 <code>b.N或手写固定次数(如i )会导致结果失真
对比两个函数时,b.Run() 为什么不能省
单独跑 BenchmarkAddV1 和 BenchmarkAddV2,数值不具备可比性。它们各自触发 GC 的时机不同、CPU 调度上下文不同、甚至可能被系统进程打断一次——快 5% 可能只是某次 GC 没来得及暂停。
- 必须用
b.Run("v1", func(b *testing.B) { ... })把两个实现塞进同一个 benchmark 生命周期 - 这样它们共享相同的预热状态、GC 周期、CPU 频率稳定窗口
- 如果函数有副作用(比如修改全局 map),记得在
b.Run内部也加b.ResetTimer()控制计时区间
ns/op 和 B/op 哪个更值得盯
ns/op 是 CPU 时间,B/op 是每次操作分配的字节数。高频调用下,B/op 往往比 ns/op 更致命——一个函数快 10%,但 B/op 高出 8 倍,服务跑几小时就触发密集 GC,吞吐反而暴跌。
- 加
-benchmem才显示B/op和allocs/op,不加等于盲跑 -
B/op = 0不代表没分配,可能是逃逸分析判定变量未逃逸,或编译器优化掉了(验证方式:临时注释掉结果赋值,看ns/op是否归零) - 真正要判断“是否变好”,得用
benchstat old.txt new.txt做统计显著性检验,不是人眼比两行数字
ns/op 就能飘 ±15%。所以别信单次输出,-count=5 -benchtime=5s -benchmem 是底线配置,再省就不是测性能,是碰运气。golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!










