当前位置:首页 > 文章列表 > Golang > Go问答 > Go testing.B.Loop 怎么写基准测试:循环语义与旧式 b.N 对比

Go testing.B.Loop 怎么写基准测试:循环语义与旧式 b.N 对比

来源:17golang原创 2026-08-27 10:39:11 0浏览 收藏

Go 1.24 以后,基准测试可以把传统的 for range b.N 改成 for b.Loop()。这个改动不只是少写一个计数变量:循环第一次进入时会自动开始计时,退出后会停止计时,循环体里的调用结果也会得到更稳妥的保活处理。下面用一个小型字符串解析基准,把新旧写法放在同一个项目里核对。

新基准优先写成 for b.Loop() { ... };旧代码不要把 b.Loop() 和 b.N 再套在一起,迁移后重点检查 Go 版本、计时边界和循环体是否仍然只包含目标操作。

实践要点:
  • testing.B.Loop 自 Go 1.24 提供,适合新的基准函数。
  • 循环前的准备工作不计入基准,循环结束后的清理工作也不计入。
  • 兼容旧工具链时保留 b.N 版本,不能在同一个函数中混用两种循环。

先做一个能运行的基准小项目

示例任务很简单:把一行带逗号的商品编号拆成字符串切片。目录只需要一个模块和一个测试文件,方便把结果交给 go test -bench 复查。

mkdir benchloop
cd benchloop
go mod init example.com/benchloop

新建 split_test.go,先准备固定输入,再分别写两种基准。

package benchloop

import (
    "strings"
    "testing"
)

var result []string

func splitIDs() []string {
    return strings.Split("sku-101,sku-204,sku-305,sku-406", ",")
}

func BenchmarkSplitLoop(b *testing.B) {
    for b.Loop() {
        result = splitIDs()
    }
}

func BenchmarkSplitN(b *testing.B) {
    for range b.N {
        result = splitIDs()
    }
}

这里用包级变量接收结果,是为了让示例的目标调用不会因为结果未使用而被整体优化掉。真实项目中也可以用局部变量配合断言或黑盒写入,但不要把校验逻辑塞进被计时的主体。

看懂 B.Loop 的计时边界

b.Loop 首次返回 true 时才开始测量循环体。准备输入、创建测试对象等工作放在循环前,退出后的释放和汇总放在循环后,这些内容不会混入每次操作的耗时。

testing.B.Loop 基准测试的准备区、计时循环和清理区边界

可以把下面的检查当成迁移后的第一条验收标准:如果把 strings.Split 之前的昂贵初始化放进循环,得到的数字就不再只代表拆分操作;如果在循环里调用 b.ResetTimer,通常说明代码还停留在旧式计时思路。

func BenchmarkSplitLoop(b *testing.B) {
    input := "sku-101,sku-204,sku-305,sku-406"
    for b.Loop() {
        result = strings.Split(input, ",")
    }
}

旧式 b.N 和新循环不要混着写

传统基准由测试框架多次调用函数,并逐步调整 b.N。B.Loop 则让一次基准函数调用覆盖完整的测量循环,并负责计时启动和停止。两套机制的生命周期不同,所以不能写成下面这种双重循环:

// 错误示例:一次基准里重复控制迭代次数
for b.Loop() {
    for range b.N {
        result = splitIDs()
    }
}

实际迁移时只保留其中一种。若项目还需要 Go 1.23 或更早版本编译,继续使用 b.N;若模块最低版本已提升到 Go 1.24,则新基准可以直接采用 b.Loop。不要只因为本机装了新版本,就忽略 CI 的工具链。

B.Loop 与 b.N 基准测试的计时和调用模型对比

运行结果时核对这四件事

在 Go 1.24 或更高版本下执行:

go test -run '^$' -bench 'BenchmarkSplit(Loop|N)$' -benchmem -count=5

可见结果通常包含 ns/op,可能还会有 B/op 和 allocs/op。两种写法的绝对数字不必逐次相同,先确认下面几项:

  1. 两个基准确实都被发现,名称没有写错。
  2. 循环体只包含目标操作,输入准备没有被意外计时。
  3. 输出中的分配次数和字节数符合代码预期,没有因为结果变量位置变化而突然增加。
  4. 在同一台机器、同一工具链下重复多次,再用稳定的样本比较变化。

如果要比较迁移前后性能,建议把旧写法和新写法放在同一次命令里跑,不要拿不同机器或不同编译器的单次结果下结论。-count=5 只是增加样本,不会替代对环境噪声的判断。

常见误区与兼容处理

把 b.Loop 写成复杂条件可以吗?

不要改写成带额外条件的复合表达式。官方文档强调,循环条件应保持准确的 b.Loop() 形式,这样编译器和测试框架才能按约定处理循环体。

循环体里的返回值一定不会被优化吗?

B.Loop 会对循环体中的调用和赋值提供更可靠的保活语义,但仍然要让基准表达真实任务。把结果写入包级变量只是示例手段,复杂场景应检查最终值或使用专门的黑盒方法。

Go 1.23 项目能直接调用 b.Loop 吗?

不能。该方法从 Go 1.24 提供。兼容旧工具链时保留 b.N 写法,或者通过文件级构建约束维护两份基准代码,并在 CI 中明确验证最低版本。

相关问题:基准迁移还要检查什么

为什么同一段代码的 ns/op 会波动?

CPU 频率、后台任务、缓存状态和样本数量都会带来波动。固定输入并使用相同命令多跑几次,再比较整体趋势。

并行基准也能用 B.Loop 吗?

并行场景应按官方接口使用 RunParallel 和 PB.Next,不要把串行的 B.Loop 再嵌进并行控制器里。

把迁移结论留在代码旁边

这个改动最适合新写的基准和正在清理的旧测试:先确认模块和 CI 的最低 Go 版本,再把一个基准函数完整改为 for b.Loop(),最后用相同输入、相同命令重复测量。只要不混用循环控制,准备与清理位置清楚,迁移后的结果就更容易解释。

版本声明
本文转载于:17golang原创 如有侵犯,请联系study_golang@163.com删除
Go regexp.FindStringSubmatchIndex 怎么定位捕获组:字节下标与中文字符串边界Go regexp.FindStringSubmatchIndex 怎么定位捕获组:字节下标与中文字符串边界
上一篇
Go regexp.FindStringSubmatchIndex 怎么定位捕获组:字节下标与中文字符串边界
Postman 怎么把单个请求导出为 cURL:Code 生成器与命令核对
下一篇
Postman 怎么把单个请求导出为 cURL:Code 生成器与命令核对
查看更多
最新文章
查看更多
课程推荐
  • 前端进阶之JavaScript设计模式
    前端进阶之JavaScript设计模式
    设计模式是开发人员在软件开发过程中面临一般问题时的解决方案,代表了最佳的实践。本课程的主打内容包括JS常见设计模式以及具体应用场景,打造一站式知识长龙服务,适合有JS基础的同学学习。
    543次学习
  • GO语言核心编程课程
    GO语言核心编程课程
    本课程采用真实案例,全面具体可落地,从理论到实践,一步一步将GO核心编程技术、编程思想、底层实现融会贯通,使学习者贴近时代脉搏,做IT互联网时代的弄潮儿。
    516次学习
  • 简单聊聊mysql8与网络通信
    简单聊聊mysql8与网络通信
    如有问题加微信:Le-studyg;在课程中,我们将首先介绍MySQL8的新特性,包括性能优化、安全增强、新数据类型等,帮助学生快速熟悉MySQL8的最新功能。接着,我们将深入解析MySQL的网络通信机制,包括协议、连接管理、数据传输等,让
    500次学习
  • JavaScript正则表达式基础与实战
    JavaScript正则表达式基础与实战
    在任何一门编程语言中,正则表达式,都是一项重要的知识,它提供了高效的字符串匹配与捕获机制,可以极大的简化程序设计。
    487次学习
  • 从零制作响应式网站—Grid布局
    从零制作响应式网站—Grid布局
    本系列教程将展示从零制作一个假想的网络科技公司官网,分为导航,轮播,关于我们,成功案例,服务流程,团队介绍,数据部分,公司动态,底部信息等内容区块。网站整体采用CSSGrid布局,支持响应式,有流畅过渡和展现动画。
    485次学习
查看更多
AI推荐
  • PubMedQA数据集详解:生物医学问答基准、功能与应用指南
    PubMedQA
    深入了解PubMedQA生物医学问答数据集,涵盖其核心功能、使用方法及在临床决策、药物研发等场景的应用,助力提升NLP模型性能。
    425次使用
  • H2O EvalGPT:开源LLM大模型评估与排行榜工具
    H2O EvalGPT
    H2O EvalGPT是H2O.ai推出的开源LLM评估平台,提供详细的大模型性能排行榜、行业特定基准测试及A/B测试功能,助您快速选择最适合项目的高性能大语言模型。
    505次使用
  • LMArena是什么?伯克利AI模型评估平台使用指南与功能解析
    LMArena
    LMArena是加州大学伯克利分校推出的AI模型匿名评测平台。通过盲测投票机制,用户可对比不同大模型回答并生成实时排行榜,助力开发者优化模型及用户选择最佳AI工具。
    516次使用
  • 斯坦福HELM:大语言模型Holistic Evaluation整体评估框架详解
    HELM
    深入了解斯坦福推出的HELM(Holistic Evaluation of Language Models)大模型评测体系。本文解析其核心功能、安装配置步骤及应用场景,涵盖准确性、公平性、鲁棒性等多维度指标,助力开发者全面优化语言模型性能。
    460次使用
  • MMBench详解:多模态大模型基准测试、功能特点与使用指南
    MMBench
    MMBench是由上海人工智能实验室等机构联合推出的多模态基准测试平台,提供细粒度能力评估、大规模数据集及VLMEvalKit工具。本文详细介绍其核心功能、安装使用方法及应用场景,助力开发者全面评估多模态模型性能。
    289次使用
微信登录更方便
  • 密码登录
  • 注册账号
登录即同意 用户协议 和 隐私政策
返回登录
  • 重置密码