b.setparallelism 仅设定 goroutine 并发数上限,须配合 b.runparallel 使用才生效;未调用则无效,且盲目增大参数值可能因调度开销或锁竞争反而降低性能。

为什么 b.SetParallelism 设了却没加速?
因为 b.SetParallelism 只控制基准测试中 *goroutine 并发数*,不自动并行化你的 Benchmark 函数逻辑。它只在你显式调用 b.RunParallel 时才生效——设了但没调用,等于白设。
- 常见错误:给
BenchmarkFoo调了b.SetParallelism(4),但函数体里全是串行代码,结果耗时不变甚至变慢(调度开销白加) - 正确场景:适合测试可并行吞吐的逻辑,比如缓存读写、JSON 解析流水线、HTTP client 批量请求
- 参数值不是越大越好:
b.SetParallelism(n)的n是 goroutine 数量上限,超过 GOMAXPROCS 或机器核心数后收益递减,还可能因锁竞争拖慢
b.RunParallel 怎么写才不踩坑?
b.RunParallel 接收一个 func(*testing.PB),里面的 *testing.PB 提供 Next() 控制任务分发——不是“开 n 个 goroutine 各跑一遍”,而是让每个 goroutine 持续取任务直到耗尽。
- 错误写法:
for i := 0; i 放在 <code>RunParallel里 → 实际变成 n×b.N 次执行,b.N失效 - 正确模式:用
pb.Next()做循环条件,每次处理一个逻辑单元(如一次 map 查找、一次 encode) - 注意共享状态:多个 goroutine 共用同一份数据(如全局 map)必须加锁或用
sync.Map,否则基准结果不可靠且可能 panic
func BenchmarkMapReadParallel(b *testing.B) {
m := make(map[int]int)
for i := 0; i < 1000; i++ {
m[i] = i * 2
}
b.SetParallelism(4)
b.RunParallel(func(pb *testing.PB) {
for pb.Next() {
_ = m[500] // 单次读操作
}
})
}和 go test -cpu 什么关系?
完全无关。-cpu 控制的是 testing 包启动时的 GOMAXPROCS 值,影响所有 goroutine 的 OS 线程绑定;而 b.SetParallelism 只影响当前 benchmark 的 RunParallel goroutine 数量。
- 如果你测的是 CPU 密集型逻辑(如加密计算),
-cpu=1和-cpu=8会显著影响单 goroutine 性能,此时b.SetParallelism的收益要看是否真能摊薄计算压力 - 网络/IO 类 benchmark(如 http.Client)通常受系统连接数、端口复用等限制,盲目提高
SetParallelism可能触发too many open files错误,得同步调高 ulimit - 默认
b.SetParallelism(1),即不启用并行;不调用RunParallel时,该设置被忽略
怎么判断要不要开并行?
看瓶颈在哪。如果单 goroutine 已把 CPU 打满(go tool pprof 显示 runtime.futex 占比低、CPU profile 火焰图平铺),说明是计算密集型,开并行才有意义;如果大量时间花在 syscall.Syscall 或 runtime.gopark,大概率是 IO 阻塞,得先优化等待逻辑,而不是堆 goroutine。
立即学习“go语言免费学习笔记(深入)”;
- 简单验证法:分别跑
go test -bench=. -benchmem -run=^$和go test -bench=. -benchmem -run=^$ -benchtime=1s,对比单次耗时与总迭代数比例 —— 如果b.N增大时单次耗时几乎不变,说明无强依赖,适合并行 - 别迷信数字:本地开发机开 8 并行,上线后服务部署在 2 核容器里,实际效果可能不如设成 2
- 真实服务压测时,并行度要匹配下游依赖的并发能力,比如后端 DB 连接池只有 10,你开 100 个 goroutine 只会让排队更长
并行基准测试真正难的不是设参数,而是确保你测的那段逻辑,在多 goroutine 下行为一致、无隐藏竞争、且符合你线上真实的并发模型。










