在Go语言中,基准测试是评估代码性能的重要手段。借助标准库testing包,我们可以方便地编写并运行基准函数,但如果不了解其机制和陷阱,测得的数据往往不能反映真实情况。

基准测试的基本写法
基准函数的命名必须以Benchmark开头,接收*testing.B参数。框架会自动调整循环次数b.N,使测试运行足够长时间。
package main
import "testing"
func BenchmarkAdd(b *testing.B) {
for i := 0; i < b.N; i++ {
_ = 1 + 2
}
}
常见陷阱与应对方法
编译器优化导致代码被消除
如果基准中的计算结果没有被使用,编译器可能直接删掉相关逻辑。应使用testing.B提供的变量接收结果,或在循环外声明接收者。
func BenchmarkRealWork(b *testing.B) {
var sum int
for i := 0; i < b.N; i++ {
sum = fib(10)
}
_ = sum
}
func fib(n int) int {
if n < 2 {
return n
}
return fib(n-1) + fib(n-2)
}
忽略内存分配影响
使用b.ReportAllocs()可以输出每次操作的内存分配次数和字节数,帮助发现隐藏的分配开销。
func BenchmarkSlice(b *testing.B) {
b.ReportAllocs()
for i := 0; i < b.N; i++ {
s := make([]int, 0, 8)
s = append(s, i)
}
}
计时受初始化干扰
当循环前有较重准备逻辑时,应调用b.ResetTimer()避免其计入耗时。
func BenchmarkWithSetup(b *testing.B) {
data := make([]int, 10000)
for i := range data {
data[i] = i
}
b.ResetTimer()
for i := 0; i < b.N; i++ {
_ = data[i%len(data)]
}
}
并行基准测试注意点
使用b.RunParallel可测试并发性能,但要保证共享状态安全,且不要和b.N手动循环混用。
func BenchmarkParallel(b *testing.B) {
b.RunParallel(func(pb *testing.PB) {
for pb.Next() {
_ = 1 + 1
}
})
}
小结
精确衡量Go代码性能,需要避开编译器消除、错误计时、忽略分配等陷阱。合理运用ResetTimer、ReportAllocs和RunParallel,才能拿到可信的基准数据。