benchmark函数必须以benchmark开头、参数为*testing.b、置于同包的_test.go文件中,否则go test -bench=.会静默跳过;b.n是动态循环次数,不可用log或fmt输出。

怎么写一个能跑通的 benchmark 函数
Go 的 testing.B 不是随便起个函数名就能被 go test -bench 识别的,必须严格满足命名和签名规则。否则 go test -bench=. 会直接跳过,连报错都没有,容易误以为“没跑”。
- 函数名必须以
Benchmark开头,后面接驼峰名(如BenchmarkMapVsSlice) - 参数必须是
*testing.B,不能少星号、不能换类型、不能多参数 - 必须放在
_test.go文件里,且包名和待测代码一致 - 别在函数里用
log或fmt.Println——B.N是循环次数,不是固定值,输出会刷屏还干扰计时
示例:
func BenchmarkStringConcat(b *testing.B) {
for i := 0; i
<h3>对比两种实现时,怎么避免干扰项</h3>
<p>benchmark 的核心是「只让被测逻辑差异生效」,但实际常因初始化、缓存、GC 干扰结果。比如测试 <code>map[string]int</code> 和 <code>sync.Map</code>,如果每次循环都新建 map,开销就全算到被测逻辑头上,失真。</p><div class="aritcle_card flexRow artxards">
<div class="artcardd flexRow">
<a class="aritcle_card_img" rel="nofollow" href="/xiazai/skill8096" title="Alibabacloud Sdk Client Initialization For Golang"><img
src="https://img.php.cn/upload/skill/000/000/081/179186233233253.jpg" alt="Alibabacloud Sdk Client Initialization For Golang" onerror="this.onerror='';this.src='/static/lhimages/moren/morentu.png'" ></a>
<div class="aritcle_card_info flexColumn">
<a rel="nofollow" href="/xiazai/skill8096" title="Alibabacloud Sdk Client Initialization For Golang" class="overflowclass">Alibabacloud Sdk Client Initialization For Golang</a>
<p class="overflowclass">初始化并管理阿里云SDK Go 客户端,涵盖 sync.Once 单例、goroutine 安全、endpoint 与 region 配置、VPC 端点及调试。</p>
</div>
<a rel="nofollow" href="/xiazai/skill8096" title="Alibabacloud Sdk Client Initialization For Golang" class="aritcle_card_btn flexRow flexcenter"><b></b><span>下载</span>
</a>
</div>
</div>
- 把初始化(如构造数据、预热结构)放到
b.ResetTimer()之前,确保不计入耗时 - 用
b.ReportAllocs()看内存分配,避免一种方案因频繁 alloc 拖慢,却误判为“逻辑慢” - 加
b.RunParallel测试并发场景时,注意变量捕获:闭包里别直接用循环变量,要用局部拷贝 - 运行时加
-benchmem -count=5多次采样,看方差——如果每次结果波动超 10%,说明有外部干扰(如 GC 正好卡在某次循环)
为什么 go test -bench=. 跑出来的时间不准
默认输出的 ns/op 是单次操作平均纳秒数,但它隐含两个前提:被测逻辑可稳定重复、无副作用。而真实代码常有状态残留或依赖外部时序(比如首次调用触发 lazy init),导致前几次极慢、后面变快。
- Go benchmark 默认会自动调整
B.N让总耗时接近 1 秒,所以小函数可能跑几百万次,大函数只跑几次——这本身没问题,但你要确认B.N是否足够覆盖 warmup 阶段 - 遇到首轮明显偏慢,手动在循环前加一次「预热调用」,再调
b.ResetTimer() - 不要只看
ns/op,重点关注BenchmarkXXX-8 1000000 1234 ns/op中的-8—— 它代表 GOMAXPROCS 值,跨机器对比前得统一设置GOMAXPROCS=1,否则并发调度差异会掩盖真实性能
怎么判断两种方案真的有性能差别
benchmark 数值差 5% 未必有意义。Go 自身调度、CPU 频率波动、TLB miss 都可能导致 ±3% 波动。盲目优化微小差异,反而增加维护成本。
- 用
benchstat(go install golang.org/x/perf/cmd/benchstat@latest)比对两组结果,它会做统计检验,输出类似geomean: -12.34% (p=0.002)—— p 值小于 0.05 才算显著 - 关注
allocs/op和B/op:内存分配少往往意味着 GC 压力小,长期运行比单纯ns/op更关键 - 如果两种方案在不同数据规模下优劣反转(比如小 slice 用
append快,大 slice 用预分配快),那就得结合真实业务数据量来测,而不是只测一个点
真正难的从来不是跑出数字,而是确认那个「被测逻辑」是否干净、那个「业务场景」是否被覆盖到了。漏掉一次预热、混进一行日志、或者忘了锁住 GOMAXPROCS,结果就可能完全误导你。
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!










