当前位置:首页 > 文章列表 > Golang > Go教程 > Go benchmark 怎么统计内存分配次数

Go benchmark 怎么统计内存分配次数

来源:17golang原创 2026-09-05 23:49:39 0浏览 收藏

Go benchmark 想统计内存分配次数,最短路径是运行 go test -benchmem,结果里的 allocs/op 就是每次操作平均发生的分配次数;同时出现的 B/op 是每次操作分配的字节数。若只想让某一个基准函数开启统计,可以调用 b.ReportAllocs()。需要在 Go 代码里拿到数字,则用 testing.Benchmark 返回的 BenchmarkResult.AllocsPerOp(),或对独立函数使用 testing.AllocsPerRun

要点速览
  • -benchmem 适合从命令行快速比较多个 benchmark。
  • b.ReportAllocs() 只打开当前 benchmark 的分配统计,不会改变其他基准。
  • allocs/op 是平均值;要避免把预热、初始化和并行调度误算进目标操作。

先用 -benchmem 看见 allocs/op

准备一个以 _test.go 结尾的文件。下面的例子刻意把输入放在基准函数外面,循环里只保留需要比较的字符串转换;这样测量目标更清楚。

package demo

import (
	"strconv"
	"testing"
)

var benchmarkSink string

func BenchmarkFormatID(b *testing.B) {
	for b.Loop() {
		// 把结果写入包级变量,避免编译器把转换优化掉。
		benchmarkSink = strconv.Itoa(20260905)
	}
}

在包目录执行:

go test -run '^$' -bench '^BenchmarkFormatID$' -benchmem
# -run '^$' 跳过普通测试;-benchmem 打开分配统计。
# -bench 用正则只选择目标基准,减少无关输出。

输出通常会同时包含 ns/opB/opallocs/op。其中 allocs/op 不是“整个进程从启动到退出的分配总数”,而是基准框架根据测量期间的总分配次数除以操作次数得到的平均指标。参数关系可以这样记:

指标回答的问题怎么用
allocs/op一次操作平均分配几次看对象数量和逃逸带来的分配频率
B/op一次操作平均分配多少字节看分配体积,和次数一起比较
ns/op一次操作平均耗时判断分配是否已经影响时间
Go testing.B benchmark 的测量边界、循环操作与 allocs/op 和 B/op 指标关系结构图
图1:benchmark 测量边界把循环中的目标操作连接到 allocs/op 与 B/op 两个内存指标。

只统计当前基准时调用 b.ReportAllocs

如果不想要求每次命令都记得加 -benchmem,可以把统计开关写在基准函数中。官方文档说明,ReportAllocs 等价于设置 -test.benchmem,但只影响调用它的 benchmark。

func BenchmarkFormatIDWithReport(b *testing.B) {
	// 只让这个基准输出内存分配指标。
	b.ReportAllocs()
	for b.Loop() {
		// 循环内保持和其他方案相同的工作量,才能公平比较。
		benchmarkSink = strconv.Itoa(20260905)
	}
}

这时即使只运行 go test -run '^$' -bench BenchmarkFormatIDWithReport,结果也会带上分配指标。需要注意的是,放在循环前的初始化通常不应算作一次操作的成本;如果使用旧式的 for i := 0; i 写法,昂贵初始化可能在 benchmark 调整 b.N 时重复执行,应该用 b.ResetTimer() 明确清零计时和分配计数。新代码优先采用 b.Loop(),它由 benchmark 框架管理迭代边界。

需要在 Go 代码里读取次数怎么办

命令行适合人工查看,自动回归或阈值判断则可以调用 testing.Benchmark。它返回 BenchmarkResult,其中 AllocsPerOp() 的含义就是总内存分配次数除以操作次数。

package demo

import (
	"fmt"
	"strconv"
	"testing"
)

func allocationsPerOperation() int64 {
	result := testing.Benchmark(func(b *testing.B) {
		b.ReportAllocs()
		for b.Loop() {
			// 让被测结果存活,避免示例只测到无效计算。
			benchmarkSink = strconv.Itoa(20260905)
		}
	})
	return result.AllocsPerOp()
}

func Example_allocationsPerOperation() {
	// 返回值适合用于回归门槛或记录到自定义测试报告。
	fmt.Println(allocationsPerOperation() >= 0)
	// Output: true
}

AllocsPerOp 返回的是整数型每操作指标;若你还想看字节数,可以调用同一个结果的 AllocedBytesPerOp()。不要把 BenchmarkResult.String() 当成完整内存报告:官方说明它不包含 allocs/opB/op,这两项由 MemString() 提供。

Go testing 包中 ReportAllocs、BenchmarkResult.AllocsPerOp 与 AllocsPerRun 的指标获取关系图
图2:从 benchmark 开关到程序化读取,三条 API 路径分别服务命令行、基准结果和独立函数测量。

只测一个函数时用 AllocsPerRun

有些场景不需要完整 benchmark,只想回答“调用这个函数一次平均分配几次”。这时 testing.AllocsPerRun(runs, f) 更直接:

func TestFormatAllocations(t *testing.T) {
	avg := testing.AllocsPerRun(100, func() {
		// 每轮只放被测调用,不把准备工作混进指标。
		benchmarkSink = strconv.Itoa(20260905)
	})
	if avg 

这里有两个容易忽略的语义。第一,函数会先执行一次预热,然后才统计指定次数;第二,测量期间会把 GOMAXPROCS 临时设为 1,并在返回前恢复。因此它适合测量稳定的单函数分配,不适合直接代表多 goroutine 生产流量下的分配行为。

如果 benchmark 使用 RunParallel,不要在各 goroutine 中分别输出同一个自定义计数器而不做同步;并且不要在并行体内调用 ResetTimer,因为它对全局 benchmark 有影响。想比较不同实现时,先统一输入、初始化位置和运行参数,再看 allocs/opB/op 是否同时下降。

常见问题

allocs/op 为 0 就代表完全没有内存分配吗?

它表示测量口径下平均每次操作没有观察到可计入的分配,不等于整个进程、测试初始化或其他 goroutine 都没有分配。

为什么加了 b.ReportAllocs 还是看不到 allocs/op?

确认运行的是该 benchmark,并检查函数名是否匹配 BenchmarkXxx。如果运行的是普通测试,报告不会自动变成 benchmark 输出。

应该比较 allocs/op 还是 B/op?

对象数量敏感的问题先看 allocs/op,大对象或缓冲区问题要同时看 B/op;性能回归还要把 ns/op 一起记录。

版本声明
本文转载于:17golang原创 如有侵犯,请联系study_golang@163.com删除
包子漫画登录后能做什么?在线阅读、阅读记录与收藏管理说明包子漫画登录后能做什么?在线阅读、阅读记录与收藏管理说明
上一篇
包子漫画登录后能做什么?在线阅读、阅读记录与收藏管理说明
MySQL 按天统计怎么补齐没有数据的日期
下一篇
MySQL 按天统计怎么补齐没有数据的日期
查看更多
最新文章
查看更多
课程推荐
  • 前端进阶之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推荐
  • SuperCLUE中文大模型评测基准:功能、能力维度与应用指南
    SuperCLUE
    SuperCLUE是权威的中文大语言模型综合评测基准,涵盖语言理解、知识应用、AI Agent智能体及安全性等12项核心能力。通过多轮对话与客观测试,定期发布榜单与技术报告,为模型研发、优化及行业选型提供科学依据。
    155次使用
  • C-Eval中文评测基准:大语言模型多学科能力评估指南
    C-Eval
    深入了解C-Eval中文评估套件,涵盖52个学科与4级难度。本文详解其功能特点、Zero-shot/Few-shot使用方法及代码示例,助您全面评测LLM中文理解与泛化能力。
    85次使用
  • ClickPrompt:AI提示词生成与优化工具,支持Stable Diffusion、ChatGPT及代码辅助
    ClickPrompt
    ClickPrompt是一款专为AI提示词编写者设计的开源在线工具,支持Stable Diffusion绘图、ChatGPT对话及GitHub Copilot代码辅助。提供Prompt自动生成、一键运行、社区分享及可视化优化功能,帮助用户高效获取精准AI输出。
    45次使用
  • PromptHero官网:AI提示词搜索、优化与学习平台,支持Midjourney/Stable Diffusion
    PromptHero
    PromptHero是专业的AI提示词搜索引擎与优化平台,支持Stable Diffusion、Midjourney等主流模型。提供海量提示词库、分类搜索、在线课程及社区互动,助力用户高效生成高质量AI图像与文本。
    25次使用
  • OpenArt免费开源指南:Stable Diffusion Prompt Book提示词手册详解
    Stable Diffusion Prompt Book
    深入解析OpenArt推出的Stable Diffusion Prompt Book,这本免费的开源提示词指南涵盖从基础语法到高级技巧,提供风格化词库与参数建议,助您优化AI绘画生成效果。
    28次使用
微信登录更方便
  • 密码登录
  • 注册账号
登录即同意 用户协议隐私政策
返回登录
  • 重置密码