基准测试函数必须以benchmark开头且接收*testing.b参数;需用b.n控制循环次数,初始化操作应置于b.resettimer()前后,b.reportallocs()和b.setbytes()须在循环前调用。

基准测试函数必须以 Benchmark 开头且接收 *testing.B
Go 的 go test 只识别形如 BenchmarkXXX(*testing.B) 的函数作为基准测试,名字不以 Benchmark 开头、参数类型不对或数量不对都会被忽略。常见错误是写成 TestXXX 或漏掉星号:testing.B 是指针类型,传值会编译失败。
实操建议:
- 函数名必须大驼峰,例如
BenchmarkMapLookup,不能是benchmark_map_lookup - 必须在函数体内调用
b.N控制循环次数,不能硬编码循环 100 次——Go 会自动调整b.N使总耗时接近 1 秒 - 避免在循环外做初始化(如构造 map、分配 slice),除非用
b.ResetTimer()显式排除预热开销
b.ReportAllocs() 和 b.SetBytes() 要在循环前调用
这两个方法影响最终输出的内存分配统计和吞吐量单位(如 B/op、MB/s),但只在 b.Run 或主循环开始前生效。如果写在 for i := 0; i 里面,会被忽略或报 panic。
典型用法:
func BenchmarkCopySlice(b *testing.B) {
src := make([]byte, 1024)
dst := make([]byte, 1024)
b.SetBytes(int64(len(src)))
b.ReportAllocs()
b.ResetTimer() // 排除上面 make 的开销
for i := 0; i
<p>注意:<code>b.SetBytes</code> 传的是单次操作处理的字节数,不是总字节数;它只影响输出中的 <code>MB/s</code> 计算,不影响运行逻辑。</p><div class="aritcle_card flexRow artxards">
<div class="artcardd flexRow">
<a class="aritcle_card_img" rel="nofollow" href="/xiazai/skill4918" title="Golang Spf13 Viper"><img
src="https://img.php.cn/upload/skill/000/000/081/179025319165074.jpg" alt="Golang Spf13 Viper" onerror="this.onerror='';this.src='/static/lhimages/moren/morentu.png'" ></a>
<div class="aritcle_card_info flexColumn">
<a rel="nofollow" href="/xiazai/skill4918" title="Golang Spf13 Viper" class="overflowclass">Golang Spf13 Viper</a>
<p class="overflowclass">Go 配置库,使用 spf13/viper — 分层优先级(flag > env >file > KV > default),提供 BindPFlag/BindPFlags、SetEnvPrefix + SetEnvKeyReplace 等功能。</p>
</div>
<a rel="nofollow" href="/xiazai/skill4918" title="Golang Spf13 Viper" class="aritcle_card_btn flexRow flexcenter"><b></b><span>下载</span>
</a>
</div>
</div>
<h3>避免在基准测试中触发 GC 或隐式分配</h3>
<p>基准测试结果容易被垃圾回收干扰,尤其当被测代码频繁分配小对象(如字符串拼接、<code>fmt.Sprintf</code>、闭包捕获变量)时,<code>allocs/op</code> 高且波动大,真实 CPU 耗时反而难判断。</p>
<p>排查和优化建议:</p>
- 用
go test -bench=. -benchmem -gcflags="-m"查看逃逸分析,确认是否意外堆分配 - 禁用 GC 运行单次对比:
GODEBUG=gctrace=1 go test -run=^$ -bench=BenchmarkXXX,观察 GC 次数是否随b.N增长线性上升 - 字符串操作优先用
bytes.Buffer或预分配[]byte,避免+=触发多次 realloc
子基准测试(b.Run)适合横向比较不同实现
当你要对比 map 查找、slice 线性查找、二分查找等不同策略时,用 b.Run 分组更清晰,还能单独运行某一分支:go test -bench="BenchmarkSearch/Map"。
关键点:
- 每个子测试仍是独立计时,
b.N由父基准测试统一控制,但各子项可设不同b.Run名称 - 不要在子测试里调用
b.ReportAllocs()—— 它继承父级设置,重复调用无效 - 若子测试间有共享初始化(如构造同一份数据),应放在
b.Run外部,再用b.ResetTimer()重置计时起点
比如:
func BenchmarkSearch(b *testing.B) {
data := generateLargeSortedSlice(1e6)
b.ResetTimer()
b.Run("Linear", func(b *testing.B) {
for i := 0; i
真正麻烦的不是写对函数签名,而是让每次运行都测到“纯逻辑耗时”——GC、调度抖动、CPU 频率降频、甚至隔壁进程占用 cache 都会影响结果。跑三次取中位数、关掉笔记本电源管理、用 <code>taskset -c 0</code> 绑核,这些在关键路径优化时都绕不开。golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!










