基准测试函数必须满足三个条件:文件名以_test.go结尾、函数名以Benchmark开头且首字母大写、参数类型为*testing.B;否则go test -bench会静默忽略。

基准测试函数必须长什么样?
Go 的 Benchmark 函数不是随便起个名、写个循环就行——它必须严格满足三个条件,否则 go test -bench 会直接忽略,还不报错,静默跳过。
- 文件名必须是
*_test.go(比如utils_test.go) - 函数名必须以
Benchmark开头,且首字母大写(BenchmarkMapAccess✅,benchmarkMapAccess❌) - 参数类型必须是
*testing.B(不是*testing.T,也不是无参)
漏掉任意一条,运行 go test -bench=. 就像没写一样。最常见的是把函数写在普通 .go 文件里,或者手误写成 func BenchmarkXxx(t *testing.T) —— 这种错误不会报错,只会让你对着空白输出发呆。
怎么写才测得准?别让初始化拖慢结果
基准测试真正要测的是“目标操作”,不是“建数据+跑操作”。但很多人把 make(map[int]int) 或 strings.Builder{} 放在 b.N 循环里,结果测出来的是分配开销,不是算法本身。
- 所有准备动作(造 map、填 slice、初始化 struct)必须放在
b.ResetTimer()之前 - 如果准备阶段耗时明显,用
b.StopTimer()和b.StartTimer()手动包住非目标代码 - 想看内存分配?必须显式调用
b.ReportAllocs(),光加-benchmem参数不够
正确示例:
立即学习“go语言免费学习笔记(深入)”;
func BenchmarkMapRange(b *testing.B) {
m := make(map[int]int, 1000)
for i := 0; i < 1000; i++ {
m[i] = i * 2
}
b.ResetTimer() // 计时从此开始
b.ReportAllocs()
for i := 0; i < b.N; i++ {
sum := 0
for _, v := range m {
sum += v
}
_ = sum
}
}运行命令和关键参数怎么选?
go test -bench=. 是起点,但默认行为容易误导:只跑约 1 秒、不显示内存、单次运行、受 CPU 负载干扰大。
- 加
-benchmem看分配:否则你永远不知道那个快的函数是不是靠多分配换来的 - 加
-benchtime=3s或5s,避免因太快导致采样不准 - 加
-count=3多跑几次取平均,减少抖动影响 - 精确匹配用正则锚点:
-bench=^BenchmarkStringJoin$,避免匹配到BenchmarkStringJoinWithPrefix
推荐日常调试组合:go test -bench=^BenchmarkMapRange$ -benchmem -benchtime=3s -count=3
对比两个实现,为什么不能只跑一次?
单次 go test -bench 输出只是原始数字,看不出“快了 37%”还是“慢了 2 倍”。真实优化必须靠统计对比。
- 安装官方工具:
go install golang.org/x/perf/cmd/benchstat@latest - 分别保存两组结果:
go test -bench=Concat -count=5 > old.txt、go test -bench=Builder -count=5 > new.txt - 用
benchstat old.txt new.txt输出带误差范围的 delta(比如-85.20%)
没用 benchstat 的对比,等于拿秒表掐两次时间然后心算——系统调度、GC、CPU 频率波动都会吃掉你的结论可信度。尤其当差异小于 10% 时,不跑多次 + 统计,基本就是猜。











