Go 的 Benchmark 函数必须命名为 BenchmarkXXX(*testing.B),调用 b.N 循环执行被测逻辑,初始化代码置于 b.ResetTimer() 前,调用 b.ReportAllocs() 获取内存分配统计。
Go 自带的 testing 包支持基准测试(Benchmark),不需要额外安装工具,但必须理解它不是模拟并发用户请求的“压测”,而是测量单个函数在受控条件下的执行性能。
Go 的 go test 只识别形如 BenchmarkXXX(*testing.B) 的函数。它和普通测试函数不同:不能用 t.Fatal,必须调用 b.N 控制循环次数,且被测逻辑需放在 b.ResetTimer() 和 b.ReportAllocs() 附近以排除初始化干扰。
b.N 是框架自动调整的迭代次数,不是你手动指定的“跑 1000 次”——它会根据首次运行耗时动态扩增,确保总耗时稳定在 1 秒左右b.ResetTimer() 之前,否则会污染结果b.ReportAllocs() 才能在结果中看到内存分配统计(B/op 和 allocs/op)func BenchmarkAdd(b *testing.B) {
b.ReportAllocs()
b.ResetTimer()
for i := 0; i < b.N; i++ {
_ = add(1, 2)
}
}
用 go test -bench=. 运行,默认只显示耗时(ns/op)。加上 -benchmem 才显示内存数据;加上 -count=3 可跑多次取平均值,避免单次抖动影响判断。
1000000000 表示跑了 10 亿次,1.23 ns/op 是每次平均耗时——数值越小越好2 B/op 表示每次调用分配了 2 字节内存,0 allocs/op 表示没触发堆分配——这对高频函数很关键500000000 3.45 ns/op,说明函数较慢,Go 自动减少了 b.N 次数来保证单轮不超时testing.B 是单 goroutine 循环调用,不涉及网络、I/O 等阻塞操作,也不模拟多客户端并发竞争资源。它测的是“理想路径下函数的纯 CPU 性能”。比如:
立即学习“go语言免费学习笔记(深入)”;
json.Marshal 可以,但测 HTTP handler 不行——handler 里有 net/http 的调度、TLS 握手、连接复用等不可控变量sync.Map.Load 有意义,但测整个 API 接口的 QPS 就会严重高估——实际瓶颈往往在数据库连接池或锁争用上ab、hey 或 vegeta 这类外部工具打真实 endpoint最容易踩的坑是让编译器优化掉被测逻辑,或者把副作用留在循环外。
result := add(1,2); for i:=0; i → 整个循环被优化为空,结果是
0 ns/op,毫无意义
_ = 或用 result,导致 Go 认为返回值未使用而内联/消除调用fmt.Println)或 sleep → 结果反映的是系统调用耗时,不是函数本身性能time.Now() 手动计时 → 绕过 testing.B 的自适应机制,且纳秒级时间获取本身有开销真正要定位线上性能瓶颈,得先用 pprof 抓 CPU profile,再针对 hot path 写精准 benchmark;盲目对整个 handler 跑 benchmark,往往只验证了“这段代码确实挺快”,却掩盖了真正的慢点。