当前位置:首页 > 文章列表 > Golang > Go教程 > testing.B.Loop 中的初始化代码如何排除计时

testing.B.Loop 中的初始化代码如何排除计时

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

如果基准测试需要先构造一份较大的输入数据,使用传统的 for range b.N 写法时,初始化可能随着基准函数反复执行而被带入测量过程。Go 1.24 引入的 testing.B.Loop 把计时边界交给框架管理:第一次调用 b.Loop() 时重置计时器,循环结束时停止计时,所以放在循环前的初始化不会计入结果。

官方资料:https://pkg.go.dev/testing

本文只讨论一个问题:怎样安排初始化、被测代码和清理,让基准结果更接近真正想比较的那段工作。

先看传统 b.N 写法的计时陷阱

旧式基准通常由 testing 包多次调用,逐步调整 b.N,直到一次测量达到足够时长。若初始化直接写在基准函数里,它可能伴随这些测量尝试重复执行。

func BenchmarkDecodeLegacy(b *testing.B) {
	// 这份输入只应准备一次,但 b.N 风格可能让整个函数重复执行。
	payload := makePayload(1 

b.ResetTimer() 能把计时器重新归零,但它是显式维护的边界。后续有人在它前面追加初始化、忘记重置,或者把清理逻辑放回循环外,基准语义就容易发生漂移。

用 b.Loop 把初始化放在计时边界外

testing.B.Loop 将初始化、循环体计时和清理分开的静态结构说明图
图1:testing.B.Loop 的计时边界说明图,初始化与清理位于循环体之外。

迁移后的结构是 初始化 -> for b.Loop() -> 清理。第一次进入循环时,testing 包重置计时器;当循环条件返回 false 时,计时停止。因此只要初始化位于第一次 b.Loop() 之前,清理位于循环结束之后,它们就不会计入基准耗时。

func BenchmarkDecode(b *testing.B) {
	// 在计时开始前准备固定输入,避免测量数据构造成本。
	payload := makePayload(1 

关键不是把 b.ResetTimer() 换成另一句 API,而是让“被测代码”成为 for b.Loop() { ... } 的唯一主体。初始化不需要猜测何时开始计时,清理也不需要再手动暂停计时。

Loop 还解决了哪些旧式基准问题

Loop 的价值不止是省掉一行 ResetTimer。官方文档还说明,循环体中的函数调用参数、返回值和赋值变量会被保持存活,避免编译器把没有可见副作用的被测调用整体优化掉。

func BenchmarkSum(b *testing.B) {
	// 输入放在计时边界外,并保持每次迭代使用同一份数据。
	values := []int{3, 5, 8, 13}

	for b.Loop() {
		// 不需要额外的 sink 变量来强行保活这次调用。
		sum(values)
	}
}

这里仍然要注意语义:Loop 的条件应当准确写成 b.Loop(),不要把它包进一个会改变语法形态的辅助函数或复杂条件。需要总迭代次数时,循环结束后可以读取 b.N,但不要再同时写一个从零到 b.N 的循环。

旧写法和新写法应该怎样对照

传统 b.N 与现代 b.Loop 基准计时边界对比说明图
图2:传统 b.N 与 b.Loop 的计时边界对比,展示初始化重复风险与新式边界。

可以把迁移前后的判断压缩成下面这张表:

关注点b.N 写法b.Loop 写法
计时开始通常需要手动调用 ResetTimer第一次 b.Loop() 自动重置
初始化可能随基准函数重复执行放在 Loop 前,不计入循环体计时
清理需要自己确认计时状态Loop 返回 false 后已停止计时
被测调用保活可能需要结果接收或 sinkLoop 循环体有专门的保活语义

这不是说所有旧基准都必须立刻改写。若项目仍需兼容 Go 1.23 或更早版本,b.Loop 不存在,应继续使用 b.N 和 b.ResetTimer,或者通过构建约束分别提供实现。

迁移时检查四个边界

第一,确认项目的最低 Go 版本至少为 1.24;否则新写法会在编译阶段失败。第二,把固定输入、解析模板和连接准备等只需做一次的工作放到第一次 b.Loop() 之前。第三,把每次迭代都必须发生的重置放回循环体,例如复用缓冲区前清空它。第四,检查是否仍然依赖当前迭代序号或总次数,因为 Loop 的目标是让基准函数不再围绕 b.N 编排业务逻辑。

func BenchmarkEncode(b *testing.B) {
	// 只准备一次的输入放在 Loop 之前,兼容边界由 go.mod 决定。
	input := newInput()
	buffer := make([]byte, 0, 1024)

	for b.Loop() {
		// 每轮必须清空的状态仍然属于被测循环的准备工作。
		buffer = buffer[:0]
		encodeInto(buffer, input)
	}
}

最后一个例子提醒我们:不计时不等于不执行。初始化和清理仍然会运行,只是不会进入 ns/op 的计时区域;如果初始化本身就是产品要比较的成本,就应该把它明确放进 b.Loop() 内,而不是为了让数字更小而移到外面。

常见问题

只要把初始化放到 Loop 前面,就一定只执行一次吗?

在一次基准测量中,Loop 让基准函数围绕循环运行一次,初始化位于第一次调用前,因此不会随着旧式 b.N 的测量尝试重复进入计时区域。但初始化是否被其他测试代码调用,仍取决于你的测试组织方式。

还需要调用 b.ResetTimer 吗?

使用标准的 for b.Loop() 结构时,通常不需要为排除循环前初始化而手动重置。只有在基准中存在额外的计时分段需求时,才应根据目标重新设计边界,而不是机械保留旧代码。

能否写成 for b.Loop() && condition?

不要这样写。为了保留 Loop 的计时和编译器保活语义,条件应保持为精确的 b.Loop();额外条件可以在循环体内用分支表达。

归纳起来,testing.B.Loop 的迁移重点是边界而不是语法替换:固定输入放在循环前,待测工作放进 Loop,清理放在循环后,并确认项目最低 Go 版本满足要求。这样基准结果的含义更清楚,也少了一处容易被后来修改破坏的手动计时控制。

版本声明
本文转载于:17golang原创 如有侵犯,请联系study_golang@163.com删除
Redis 窗口计数限流器的边界与过期策略Redis 窗口计数限流器的边界与过期策略
上一篇
Redis 窗口计数限流器的边界与过期策略
GitHub Actions reusable workflow 传递矩阵参数
下一篇
GitHub Actions reusable workflow 传递矩阵参数
查看更多
最新文章
查看更多
课程推荐
  • 前端进阶之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模型性能。
    406次使用
  • H2O EvalGPT:开源LLM大模型评估与排行榜工具
    H2O EvalGPT
    H2O EvalGPT是H2O.ai推出的开源LLM评估平台,提供详细的大模型性能排行榜、行业特定基准测试及A/B测试功能,助您快速选择最适合项目的高性能大语言模型。
    483次使用
  • LMArena是什么?伯克利AI模型评估平台使用指南与功能解析
    LMArena
    LMArena是加州大学伯克利分校推出的AI模型匿名评测平台。通过盲测投票机制,用户可对比不同大模型回答并生成实时排行榜,助力开发者优化模型及用户选择最佳AI工具。
    493次使用
  • 斯坦福HELM:大语言模型Holistic Evaluation整体评估框架详解
    HELM
    深入了解斯坦福推出的HELM(Holistic Evaluation of Language Models)大模型评测体系。本文解析其核心功能、安装配置步骤及应用场景,涵盖准确性、公平性、鲁棒性等多维度指标,助力开发者全面优化语言模型性能。
    437次使用
  • MMBench详解:多模态大模型基准测试、功能特点与使用指南
    MMBench
    MMBench是由上海人工智能实验室等机构联合推出的多模态基准测试平台,提供细粒度能力评估、大规模数据集及VLMEvalKit工具。本文详细介绍其核心功能、安装使用方法及应用场景,助力开发者全面评估多模态模型性能。
    262次使用
微信登录更方便
  • 密码登录
  • 注册账号
登录即同意 用户协议 和 隐私政策
返回登录
  • 重置密码