Go 的 go test -bench 可客观复现对比算法性能,需规范编写以 Benchmark 开头的函数,接收 *testing.B 参数并在 b.N 次循环中执行待测逻辑,Go 自动调整 b.N 使总耗时约 1 秒。

用 Go 的 go test -bench 可以客观、可复现地对比不同算法实现的性能,关键在于写对基准测试函数、控制变量、理解结果含义。
写出规范的 Benchmark 函数
基准测试函数必须以 Benchmark 开头,接收 *testing.B 参数,并在 b.N 次循环中执行待测逻辑。Go 会自动调整 b.N 使总耗时接近 1 秒,确保统计有效。
示例:对比两种字符串反转实现
func BenchmarkReverseBuiltIn(b *testing.B) {
s := "hello world"
for i := 0; i < b.N; i++ {
_ = reverseBuiltIn(s)
}
}
func BenchmarkReverseManual(b *testing.B) {
s := "hello world"
for i := 0; i < b.N; i++ {
_ = reverseManual(s)
}
}
- 每次迭代只测核心逻辑,避免初始化开销干扰(如字符串构造放在循环外)
- 用
_ =抑制返回值,防止编译器优化掉整个调用 - 若算法依赖输入规模,可用
b.Run分组测试不同长度(见下文)
用 b.Run 进行多维度对比
当需要测试不同输入规模或多种参数组合时,用 b.Run 创建子基准,便于横向比较。
立即学习“go语言免费学习笔记(深入)”;
例如对比切片去重在不同长度下的表现:
func BenchmarkDedup(b *testing.B) {
for _, size := range []int{100, 1000, 10000} {
b.Run(fmt.Sprintf("Size%d", size), func(b *testing.B) {
data := make([]int, size)
for i := range data {
data[i] = i % (size / 10) // 制造重复
}
for i := 0; i < b.N; i++ {
_ = dedupMap(data)
}
})
}
}
- 每个子 benchmark 独立运行,输出带前缀(如
BenchmarkDedup/Size100-8) - 确保每次子测试的数据生成逻辑一致且不被缓存
- 用
-benchmem同时查看内存分配,避免只看时间忽略 GC 压力
运行与解读 benchmark 结果
执行命令:
go test -bench=^BenchmarkReverse -benchmem -count=3
-
-bench=^BenchmarkReverse精确匹配函数名(^表示开头) -
-benchmem显示每次操作的平均内存分配次数和字节数 -
-count=3运行 3 轮取平均值,减少噪声影响
典型输出:
BenchmarkReverseBuiltIn-8 10000000 124 ns/op 16 B/op 1 allocs/op BenchmarkReverseManual-8 20000000 92.1 ns/op 16 B/op 1 allocs/op
重点关注三列:ns/op(纳秒/次,越小越好)、B/op(字节/次)、allocs/op(内存分配次数/次)。若两个实现 ns/op 相差不足 5%,需结合 allocs/op 和实际场景判断是否值得优化。
排除干扰,保证结果可信
真实性能受环境波动影响,需主动控制变量:
- 关闭 CPU 频率调节:
sudo cpupower frequency-set -g performance(Linux) - 避免同时运行其他高负载程序(浏览器、IDE、docker)
- 使用
runtime.GC()在每次子 benchmark 前手动触发 GC,减少 GC 时间抖动 - 对有副作用或状态依赖的函数,确保每次迭代从干净状态开始(如重置全局变量、重建结构体)
不复杂但容易忽略。










