Go基准测试必须用go test -bench框架驱动,函数名需为BenchmarkXxx、参数为*testing.B、文件名为xxx_test.go且与被测代码同包,否则静默跳过或编译失败。

Go基准测试不能手写循环+time.Now()计时,必须用go test -bench框架驱动,否则ns/op数字毫无可比性、不可复现。
函数名、签名、文件位置三者必须同时合规
漏掉任意一项,go test -bench=.会静默跳过——不报错、不提示、输出里压根没你的函数。
- 函数名必须是
BenchmarkXxx(首字母大写驼峰),Benchmarkfib或benchmarkFib都无效 - 参数必须是
*testing.B(带星号指针),写成testing.B值类型或加额外参数直接编译失败 - 文件必须叫
xxx_test.go且和被测代码在同一包;放在main.go或跨包调用会报错或无法加载
for i := 0; i 是强制规范,不是建议
b.N由框架动态决定,目标是让整个循环耗时接近-benchtime(默认1秒)。它可能从1跳到1000万甚至上亿——这是正常行为,说明框架在努力压出稳定样本。
- 手动写死
for i := 0; i 等于绕过整个机制,ns/op失去意义 - 别在循环里写
if b.N == 1做初始化——每次运行b.N都不同,逻辑必然错乱 - 被测逻辑结果必须被消费(如赋给
_或全局变量),否则编译器可能直接优化掉整段计算
初始化开销必须用b.ResetTimer()隔离
setup阶段(如make([]byte, 1e6)、预热json.Marshal、打开文件)如果不剥离,就会被摊进ns/op,导致结果是“初始化 + 执行”的混合值。
使用 uber-go/dig 在 Golang 中实现依赖注入——基于反射容器,提供 Provide/Invoke、dig.In/dig.Out 参数与结果对象,支持命名依赖等
- 标准结构:先做所有 setup → 调用
b.ResetTimer()→ 再for i := 0; i 执行主逻辑 -
b.ReportAllocs()要放在b.ResetTimer()之后,否则内存统计含 setup 阶段,allocs/op和B/op失真 - 如果主逻辑有副作用(比如修改全局
map),每次迭代前需重置状态,否则数据污染影响结果
真实压测要控制采样质量,不能只跑go test -bench=.
默认只跑1秒,短函数可能只执行几轮就结束,统计波动大;长函数可能根本跑不完就被截断。
- 加
-benchmem同时观察内存分配(allocs/op和B/op),避免只盯CPU忽略GC压力 - 用
-benchtime=5s延长总运行时间,让b.N更大,降低单次误差权重 - 用
-count=3重复运行三次取中位数,对抗系统噪声(如后台进程干扰) - 对比两个实现差异时,必须用
benchstat(go install golang.org/x/perf/cmd/benchstat@latest),它用Welch’s t-test判断差异是否统计显著,不是简单比平均值
最易被忽略的是b.N的动态性——它不是固定次数,而是框架根据采样实时调整的整数;你写的循环体里任何依赖b.N为常量的逻辑,都会在不同机器、不同Go版本、甚至同一次连续运行中失效。
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!










