go test 默认不运行基准测试,需用 -bench 参数;可同时执行单元测试和基准测试,如 go test -run=TestAdd -bench=BenchmarkAdd -benchmem。
go test 同时跑单
元测试和基准测试默认情况下 go test 不会执行 Benchmark* 函数,必须显式加 -bench 参数。想“一次命令兼顾两者”,得组合使用 -run 和 -bench,但要注意它们的匹配逻辑互不干扰:
-run 只控制 Test* 的执行(支持正则,如 -run=^TestAdd$)-bench 只控制 Benchmark* 的执行(也支持正则,如 -bench=^BenchmarkAdd$)go test -run=TestAdd -bench=BenchmarkAdd -benchmem
-bench=.,它会运行所有基准测试,不管 -run 是否匹配到测试函数Benchmark 中调用 testing.B 的常见误用基准测试不是把逻辑塞进 b.N 循环就完事——循环体里不能含初始化、I/O、随机数等干扰项,否则结果失真。典型错误包括:
for i := 0; i 内部调用 rand.Intn() 或 time.Now()
b.ReportAllocs() 就断言内存表现b.StopTimer() / b.StartTimer() 位置不对,漏掉关键路径计时正确做法是把预热、准备、清理拆开:
func BenchmarkParseJSON(b *testing.B) {
data := []byte(`{"name":"foo","age":42}`)
var v map[string]interface{}
b.ResetTimer() // 确保只测核心解析
for i := 0; i < b.N; i++ {
json.Unmarshal(data, &v)
}
}
避免重复实现,建议把被测逻辑封装成导出函数或闭包,单元测试和基准测试都调用它。不要在 Test* 里复制 Benchmark* 的循环逻辑。
CalculateSum(nums []int) int
TestCalculateSum(t *testing.T) 调用它并比对结果BenchmarkCalculateSum(b *testing.B) 在循环中调用它b.ReportMetric() 显式标注单位,例如 b.ReportMetric(float64(costMs), "ms/op")
go test -bench=. -benchmem 结果里 B/op 有时为 0B/op 表示每次操作平均分配的字节数,为 0 通常意味着:编译器做了逃逸分析优化,把本该堆分配的对象转为栈分配;或者你压根没触发内存分配(比如纯计算、复用已有变量)。
make([]int, 0, N) 预分配容量,避免扩容导致额外分配[]byte 或反之(string(b) / []byte(s) 都分配)go build -gcflags="-m" your_file.go 查看逃逸分析输出-benchmem 必须和 -bench 一起用才生效,单独用无效真正难的是让性能测试反映真实负载——比如加锁逻辑在单 goroutine 下快如闪电,一上多协程就暴露竞争,这种场景得靠 runtime.GOMAXPROCS 和手动启多个 goroutine 模拟,而不是只依赖默认的单线程 b.N 循环。