Go 1.24 新方法:编写性能测试用例方法 testing.B.Loop 介绍

news2025/4/16 7:56:45

Go 开发者在使用 testing包编写基准测试用例时,如果不注意,可能会遇到各种陷阱。这些陷阱,导致基准测试结果不准确。Go1.24 版本引入了一种新的基准测试编写方式,它同样易用,并且可以帮助规避编写基准测试时的一些坑。

Go 1.24 版本推荐使用 testing.B.Loop代替 testing.B.N来编写基准测试用例。

Go1.24 版本前,我们使用 b.N 来编写基准测试用例,例如:

func Benchmark(b *testing.B) {
  for range b.N {
    ... 要测量的代码 ...
  }
}

改用b.Loop仅需要微不足道的改动:

func Benchmark(b *testing.B) {
  for b.Loop() {
    ... 要测量的代码 ...
  }
}

testing.B.Loop有很多优点:

  • 可以防止基准测试循环内的不当编译优化;
  • 可以自动将设置和清理部分代码耗时从基准测试时间统计中剔除;
  • 代码不应意外地依赖于总迭代次数或当前迭代。

上述这些优点都是在使用 b.N编写基准测试代码时易犯的错误,这些错误会导致基准测试不准确。除了上述优点之外,b.Loop风格的基准测试,还能再更短的时间执行完。

接下来,我们来看下testing.B.Loop的优势以及如何有效地使用它。

旧基准测试循环问题

在 Go 1.24 之前,大部分的基准测试用例结构简单。但是复杂的测试用例,却需要很小心的编写:

func Benchmark(b *testing.B) {
  ... setup ...
  b.ResetTimer() // 如果设置可能很昂贵
  for range b.N {
    ... 代码测量 ...
    ... 使用汇点或累积防止未用代码消除 ...
  }
  b.StopTimer() // 如果清理或报告可能很昂贵
  ... 清理 ...
  ... 报告 ...
}

如果设置 (setup)或清理 (cleanup)逻辑复杂,耗时较久,为了避免这些准备性的逻辑参与到核心代码的耗时统计中,需要使用ResetTimerStopTimer 方法,将这些时间剔除掉。但是真正开发的过程中, 可能有一些开发者会遗漏这些逻辑。即使开发者没有遗漏,也很难判断设置或清理过程是否“足够耗时”到需要使用它们。

还有一个更微妙的陷阱,需要更深入的理解(示例源代码):

func isCond(b byte) bool {
  if b%3 == 1 && b%7 == 2 && b%17 == 11 && b%31 == 9 {
    return true
  }
  return false
}

func BenchmarkIsCondWrong(b *testing.B) {
  for range b.N {
    isCond(201)
  }
}

在这个例子中,用户可能会观察到 isCond 在亚纳秒级别的时间内执行。CPU 的速度很快,但并没有快到这个程度!这个看似异常的结果源于 isCond 被内联处理,并且由于其结果从未被使用,编译器将其视为无效代码而进行消除。

因此,这个基准测试根本没有测量 isCond。它测量的是进行无操作所需的时间。在这种情况下,亚纳秒的结果是一个明显的警示,但在更复杂的基准测试中,部分无效代码消除可能导致看起来合理但实际上并未测量预期内容的结果。

testing.B.Loop可以带来哪些好处?

b.N 风格的基准测试不同,testing.B.Loop 能够跟踪其首次调用时间以及基准测试的最终迭代结束时刻。循环开始时的 b.ResetTimer 和结束时的 b.StopTimer 被整合进 testing.B.Loop,消除了手动管理基准测试计时器以进行初始化和清理代码的开发步骤。

另外,Go 编译器现在可以探测到只调用 testing.B.Loop 时的循环,并阻止 testing.B.Loop 内的代码死循环。

testing.B.Loop 的另一个优点是其一次性快速提升的方法。对于 b.N 风格的基准测试,测试包必须多次调用基准测试函数,并使用不同的 b.N 值逐步增加,直到测量时间达到一个阈值。相比之下,b.Loop 可以简单地运行基准测试循环,直到达到时间阈值,只需调用基准测试函数一次。

b.N 风格循环的某些限制仍适用于 b.Loop 风格的循环。用户仍需在必要时负责在基准测试循环中管理计时器。(示例源)

func BenchmarkSortInts(b *testing.B) {
  ints := make([]int, N)
  for b.Loop() {
    b.StopTimer()
    fillRandomInts(ints)
    b.StartTimer()
    slices.Sort(ints)
  }
}

在这个例子中,为了测试 slices.Sort 方法的就地排序性能,每次迭代都需要一个随机初始化的数组。开发菏泽仍需在这些情况下手动管理计时器。

此外,基准测试函数体中仍需要只有一个这样的循环(b.N风格循环不能与b.Loop风格循环共存),并且循环的每次迭代应该做相同的事情。

何时使用

testing.B.Loop 方法,是从 Go 1.24 版本起,编写基准测试的首选方式。使用示例如下:

func Benchmark(b *testing.B) {
  ... 设置 ...
  for b.Loop() {
    // 可选的循环内部设置/清理计时器控制
    ... 要测量的代码 ...
  }
  ... 清理 ...
}
  • 知识星球:令飞编程。10+ 高质量体系课( Go、云原生、AI Infra)、15+ 高质量实战项目,P8 技术专家助你提高技术天花板,冲击百万年薪!
  • 我公众号:令飞编程,分享 Go、云原生、AI Infra 相关技术。回复「资料」免费下载 Go、云原生、AI 等学习资料;
  • 哔哩哔哩:令飞编程 ,以视频、直播的形式,分享技术、职场、课程、面经等;

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2335800.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

烽火ai场控接入deepseek自动回复话术软件

要将烽火AI场控软件与DeepSeek自动回复话术软件进行对接,实现直播间自动互动功能,需通过API接口或脚本工具完成数据互通。以下是具体操作步骤及注意事项: 确认兼容性与准备工作 软件支持检查 确认烽火AI场控是否开放API接口(一般需…

【Linux系统】进程地址空间

命令行参数 int main (int argc, char* argv[]) 命令行参数列表 argc&#xff1a;参数的个数argv&#xff1a;参数的清单 int main (int argc, char* argv[]) {printf("argc: %d\n",argc);for(int i 0; i < argc; i){printf("argv[%d] : %s \n", i…

记录学习的第二十六天

还是每日一题。 今天这道题有点难度&#xff0c;我看着题解抄的。 之后做了两道双指针问题。 这道题本来是想用纯暴力做的&#xff0c;结果出错了。&#x1f613;

python成功解决AttributeError: can‘t set attribute ‘lines‘

文章目录 报错信息与原因分析解决方法示例代码代码解释总结 报错信息与原因分析 在使用 matplotlib绘图时&#xff0c;若尝试使用 ax.lines []来清除图表中的线条&#xff0c;会遇到AttributeError: can’t set attribute错误。这是因为 ax.lines是一个只读属性&#xff0c;不…

如何建立可复用的项目管理模板

建立可复用的项目管理模板能够显著提高项目执行效率、减少重复劳动、确保项目管理标准化。在企业中&#xff0c;项目管理往往涉及多个步骤和多个团队&#xff0c;然而每次开始一个新项目时&#xff0c;如果都从头开始设计流程和文档&#xff0c;势必浪费大量的时间和精力。通过…

如何使用通义灵码玩转Docker - AI助手提升开发效率

一、引言 Docker 作为一种流行的虚拟化技术&#xff0c;能够帮助开发者快速搭建所需的运行环境。然而&#xff0c;对于初学者来说&#xff0c;掌握 Docker 的基本概念和使用方法可能会遇到一些挑战。本文将介绍如何利用通义灵码这一智能编码助手&#xff0c;帮助你更高效地学习…

GGML源码逐行调试(下)

目录 前言1. 简述2. 预分配计算图内存2.1 创建图内存分配器2.2 构建最坏情况的计算图2.3 预留计算图内存 3. 分词4. 模型推理与生成4.1 模型推理4.2 采样 结语下载链接参考 前言 学习 UP 主 比飞鸟贵重的多_HKL 的 GGML源码逐行调试 视频&#xff0c;记录下个人学习笔记&#x…

「2025AIGC终极形态」AI系统源码:文本→图像→音乐→视频生成

—从技术痛点到企业级部署&#xff0c;手把手实现全流程AI内容工厂 行业核心痛点&#xff1a;为什么需要多模态AIGC系统&#xff1f; 1. 工具割裂&#xff0c;效率低下 传统流程&#xff1a; 文案&#xff08;ChatGPT&#xff09;→ 配图&#xff08;Midjourney&#xff09;→…

使用CS Roofline Toolkit测量带宽

使用CS Roofline Toolkit测量带宽 工程下载&#xff1a;使用CS Roofline Toolkit测量带宽-案例工程文件&#xff0c;也可以按照下面的说明使用git clone下载 目录 使用CS Roofline Toolkit测量带宽0、Roofline模型理解1、CS Roofline Toolkit下载1.1、设置代理1.2、git clone下…

L1-4 拯救外星人

题目 你的外星人朋友不认得地球上的加减乘除符号&#xff0c;但是会算阶乘 —— 正整数 N 的阶乘记为 “N!”&#xff0c;是从 1 到 N 的连乘积。所以当他不知道“57”等于多少时&#xff0c;如果你告诉他等于“12!”&#xff0c;他就写出了“479001600”这个答案。 本题就请你…

现代c++获取linux系统名称

现代c获取linux系统名称 前言一、使用命令获取操作系统名称二、使用c代码获取操作系统名称三、验证四、总结 前言 本文介绍一种使用c获取当前操作系统名称的方法 一、使用命令获取操作系统名称 在linux系统中可以使用uname或者uname -s命令来获取当前操作系统名称&#xff0c…

力扣刷题HOT100——53.最大子数组和

给你一个整数数组 nums &#xff0c;请你找出一个具有最大和的连续子数组&#xff08;子数组最少包含一个元素&#xff09;&#xff0c;返回其最大和。 子数组是数组中的一个连续部分。 示例 1&#xff1a; 输入&#xff1a;nums [-2,1,-3,4,-1,2,1,-5,4] 输出&#xff1a;6…

ES和MySQL概念对比

基本概念 ES和MySQL都属于数据库&#xff0c;不过各有各的特性&#xff0c;大致使用方法与MySQL类似并无区别。 MySQL&#xff1a;擅长事务持有ACID的特性&#xff0c;确保数据的一致性和安全。 ES&#xff1a;持有倒排索引&#xff0c;适合海量数据搜索和分析。 ES和MySQL如何…

Android开发过程中遇到的SELINUX权限问题

1、selinux权限一般问题 问题详情 log输出如下所示&#xff1a; 01-01 00:00:12.210 1 1 I auditd : type1107 audit(0.0:33): uid0 auid4294967295 ses4294967295 subju:r:init:s0 msg‘avc: denied{ set } for propertypersist.sys.locale pid476 uid1000 gid1000 scontext…

Windows系统docker desktop安装(学习记录)

目前在学习docker&#xff0c;在网上扒了很多老师的教程&#xff0c;终于装好了&#xff0c;于是决定再装一遍做个记录&#xff0c;省的以后再这么麻烦 一&#xff1a;什么是docker Docker 是一个开源的应用容器引擎&#xff0c;它可以让开发者打包他们的应用以及依赖包到一个…

MIP-Splatting:全流程配置与自制数据集测试【ubuntu20.04】【2025最新版】

一、引言 在计算机视觉和神经渲染领域&#xff0c;3D场景重建与渲染一直是热门研究方向。近期&#xff0c;3D高斯散射&#xff08;3D Gaussian Splatting&#xff09;因其高效的渲染速度和优秀的视觉质量而受到广泛关注。然而&#xff0c;当处理大型复杂场景时&#xff0c;这种…

怎样完成本地模型知识库检索问答RAG

怎样完成本地模型知识库检索问答RAG 目录 怎样完成本地模型知识库检索问答RAG使用密集检索器和系数检索器混合方式完成知识库相似检索1. 导入必要的库2. 加载文档3. 文本分割4. 初始化嵌入模型5. 创建向量数据库6. 初始化大语言模型7. 构建问答链8. 提出问题并检索相关文档9. 合…

XCTF-web(三)

xff_referer 拦截数据包添加&#xff1a;X-Forwarded-For: 123.123.123.123 添加&#xff1a;Referer: https://www.google.com baby_web 提示&#xff1a;想想初始页面是哪个 查看/index.php simple_js 尝试万能密码&#xff0c;没有成功&#xff0c;在源码中找到如下&#xf…

Verilog的整数除法

1、可变系数除法实现----利用除法的本质 timescale 1ns / 1ps // // Company: // Engineer: // // Create Date: 2025/04/15 13:45:39 // Design Name: // Module Name: divide_1 // Project Name: // Target Devices: // Tool Versions: // Description: // // Depe…

React 把一系列 state 更新加入队列

把一系列 state 更新加入队列 设置组件 state 会把一次重新渲染加入队列。但有时你可能会希望在下次渲染加入队列之前对 state 的值执行多次操作。为此&#xff0c;了解 React 如何批量更新 state 会很有帮助。 开发环境&#xff1a;Reacttsantd 学习内容 什么是“批处理”以…