go基准测试需用testing包,函数名以benchmark开头、参数为*testing.b、必须用b.n控制循环。错误示例是自行写固定次数for循环。

Go 自带的 testing 包就能做靠谱的基准测试,不需要额外框架——只要用对 go test -bench 和写好 BenchmarkXxx 函数。
写一个合法的 Benchmark 函数
Go 的基准测试函数必须满足三个硬性条件,缺一不可:
- 函数名以
Benchmark开头,后接大驼峰名称(如BenchmarkMapInsert) - 参数类型必须是
*testing.B - 必须在函数体中调用
b.N控制循环次数,不能自己写固定次数的 for 循环
错误示例:for i := 0; i —— 这会让 Go 无法自动调整迭代轮数,测出的结果不可比、不收敛。
正确写法:
立即学习“go语言免费学习笔记(深入)”;
func BenchmarkCopySlice(b *testing.B) {
src := make([]int, 1000)
dst := make([]int, 1000)
for i := 0; i < b.N; i++ {
copy(dst, src)
}
}
避免隐式内存分配干扰结果
基准测试中每轮新建切片、map 或结构体,会把 GC 压力和内存分配时间混进耗时里,导致结果虚高且波动大。
- 把初始化逻辑移到
b.ResetTimer()之前(或用b.StopTimer()/b.StartTimer()精确包住待测逻辑) - 复用对象:在循环外预分配,循环内只重置内容(如用
slice = slice[:0]清空而非make新建) - 避免在
b.N循环内触发 GC,比如不要在循环里反复json.Marshal大结构体
典型陷阱:for i := 0; i —— 每次都分配新 slice,测的不是序列化性能,而是分配+序列化+GC 的混合结果。
用 -benchmem 和 -count 看清真实开销
默认 go test -bench=. 只输出 ns/op,但内存行为往往更关键:
- 加
-benchmem显示每次操作的平均分配字节数(B/op)和内存分配次数(allocs/op),这两个值比时间更稳定、更具诊断价值 - 加
-count=5运行多次取中位数,避免单次抖动误导判断(尤其在笔记本或虚拟机上) - 加
-benchtime=5s延长总运行时间,让b.N更大,减少计时器误差占比
推荐组合命令:go test -bench=BenchmarkFoo -benchmem -count=5 -benchtime=3s。
对比不同实现时注意 CPU 绑定与缓存效应
当比较两个算法(比如 map 查找 vs switch 分支)时,微小差异可能被硬件特性掩盖:
- 确保被测代码段足够“热”:避免因分支预测失败或 cache miss 导致首几次迭代明显偏慢;Go 的
b.N自动跳过预热轮次,但预分配数据仍建议在b.ResetTimer()前完成 - 禁用 CPU 频率缩放(Linux 下
sudo cpupower frequency-set -g performance),否则基准结果随温度/负载漂移 - 避免并行干扰:测试期间关掉无关进程,尤其是浏览器、IDE、同步工具;macOS 上注意
mdworker可能突然抢 CPU
最常被忽略的一点:同一台机器上两次 go test -bench 结果相差 5% 是正常的,但若 B/op 差异超过 10%,大概率是某次忘了复用内存或触发了 GC 尖峰。











