Go基准测试函数必须以Benchmark开头、签名严格为func BenchmarkXxx(b *testing.B)、置于同包的_test.go文件中;b.N由框架动态调整以确保总耗时≥1秒,不可手动固定循环次数。

怎么写一个能跑起来的 Benchmark 函数
Go 的基准测试函数不是随便起个名就能被 go test -bench 扫到的——必须严格符合命名和签名规范,否则直接被忽略。
- 函数名必须以
Benchmark开头,后面接大驼峰标识符(如BenchmarkMapInsert) - 签名必须是
func BenchmarkXxx(b *testing.B),参数类型不能错,也不能多参少参 - 必须放在
_test.go文件里,且文件中要包含package xxx(和被测代码同包) - 别在函数里漏掉
b.ResetTimer()或误用b.StopTimer()—— 它们只影响计时,不控制循环次数
常见错误现象:go test -bench=. 输出 no tests to run 或静默跳过,大概率是函数名/签名/文件位置三者之一没对上。
testing.B 的 N 是什么,为什么不能手动写 for 循环次数
b.N 不是固定值,而是 Go 测试框架根据函数执行耗时动态调整的迭代次数。它会先试跑少量次数估算单次耗时,再放大到能稳定测量的量级(通常目标总耗时 ≥ 1 秒)。
- 别写
for i := 0; i —— 这会让基准失真,<code>b.N失去意义,且go test -benchtime参数失效 - 正确写法永远是
for i := 0; i ,让框架掌控节奏 - 如果函数本身很慢(比如含 I/O 或 sleep),可能需要加
b.ReportAllocs()和-benchmem看内存分配,而不是硬调b.N
性能影响:手写固定次数容易导致结果波动极大,尤其在 CI 环境下 CPU 负载不稳时;用 b.N 才能获得可比、可复现的纳秒级单次耗时(ns/op)。
立即学习“go语言免费学习笔记(深入)”;
使用 uber-go/dig 在 Golang 中实现依赖注入——基于反射容器,提供 Provide/Invoke、dig.In/dig.Out 参数与结果对象,支持命名依赖等
怎么测带 setup/cleanup 的逻辑,比如打开文件或初始化 map
setup 和 cleanup 代码如果混在主循环里,会被计入耗时,严重污染结果。必须把它们从计时范围内剥离开。
- setup 放在
b.ResetTimer()之前(或函数开头),cleanup 放在循环之后、b.StopTimer()之后 - 典型结构:
func BenchmarkReadFile(b *testing.B) { data, err := os.ReadFile("test.txt") // setup,不计时 if err != nil { b.Fatal(err) } b.ResetTimer() // 从此开始计时 for i := 0; i < b.N; i++ { _ = bytes.Contains(data, []byte("foo")) // 实际被测逻辑 } // cleanup 一般不需要,但如果用了 sync.Pool 或临时目录,放这里并配 b.StopTimer() } - 注意:不要在循环里重复做 setup(比如反复
json.Marshal同一数据),那测的是序列化+主逻辑,不是主逻辑本身
容易踩的坑:用 b.Run 嵌套子 benchmark 时,父函数的 ResetTimer 对子函数无效,每个子 benchmark 要自己管理计时。
为什么 go test -bench=. 没输出,或者显示 0 B/op 和 0 allocs/op
这不是代码错了,而是 Go 编译器做了逃逸分析优化,把本该堆上分配的对象全塞进栈里了,或者编译期把整个循环常量化掉了。
- 确认是否真的有内存分配:加
b.ReportAllocs()并运行go test -bench=. -benchmem - 如果仍为 0,试试在循环里加个“干扰项”,比如
blackhole = append(blackhole, result)(var blackhole []int),防止内联/消除 - 更可靠的做法是测真实场景:比如传入真实 struct 而非字面量,或用
runtime.GC()强制触发(仅调试用,别提交) - 另一个可能是函数太轻量,单次执行远低于纳秒精度,框架自动跳过——这时看
ns/op是否为0,而不是信B/op
兼容性影响:不同 Go 版本的逃逸分析策略会变,同一段 benchmark 在 1.19 和 1.22 下可能显示完全不同分配数,所以别跨版本直接比数字,重点看趋势和相对变化。

















