
本文旨在解析Go语言基准测试中常见的误用模式,特别是当go test -bench命令产生非预期结果(如极低执行时间或零内存分配)时。我们将深入探讨testing.B的关键用法,包括b.N循环、b.ResetTimer()和数据准备策略,通过实际案例演示如何正确编写基准测试,以获取准确可靠的性能指标,避免因测试方法不当导致的误判。
Go语言提供了一套内置的基准测试(benchmarking)框架,通过go test -bench命令执行。基准测试函数通常以Benchmark开头,接收一个*testing.B类型的参数。testing.B结构体是进行性能测试的核心,它包含了一些关键字段和方法,其中最重要的是b.N。
b.N表示基准测试函数应该执行的迭代次数。Go测试框架会根据被测代码的执行时间自动调整b.N的值,以确保测试在合理的时间内完成,并获得统计学上可靠的结果。因此,被测代码的核心逻辑必须放置在一个for i := 0; i 。
另一个重要的概念是计时器的控制。b.ResetTimer()用于重置计时器,它会清除在调用之前所花费的时间。通常,数据准备等一次性设置操作应该在b.ResetTimer()之前完成,以确保计时器只测量实际的性能瓶颈。
在原始问题中,用户实现的冒泡排序、选择排序和插入排序的基准测试结果显示,选择排序的执行时间极短(0.60 ns/op),且内存分配为零。这显然与实际的排序算法性能不符。
让我们回顾一下原始的基准测试代码片段:
func BenchmarkBubble(b *testing.B) {
xs := generate(10000, -100, 100)
/* b.ResetTimer() */ // 注释掉了
SortBubble(xs) // 核心排序逻辑只执行了一次
}
func BenchmarkSelection(b *testing.B) {
xs := generate(10000, -100, 100)
/* b.ResetTimer() */ // 注释掉了
SortSelection(xs) // 核心排序逻辑只执行了一次
}
func BenchmarkInsertion(b *testing.B) {
xs := generate(10000, -100, 100)
/* b.ResetTimer() */ // 注释掉了
SortInsertion(xs) // 核心排序逻辑只执行了一次
}问题出在SortBubble(xs)等排序函数只被调用了一次。go test命令在执行基准测试时,会多次调用BenchmarkXxx函数本身,并根据每次调用的总时间来调整b.N。然而,基准测试框架计时的是整个BenchmarkXxx函数的执行时间,而不是其中某一行代码的执行时间。
当SortSelection(xs)只执行一次时,go test会发现这个函数体(包括数据生成和一次排序)执行得很快。为了达到统计的准确性,它可能会将b.N调整到一个非常大的值(例如10亿),然后用整个BenchmarkSelection函数的总执行时间除以这个巨大的b.N,从而得到一个接近零的ns/op结果。同时,由于排序操作本身只在函数体内部执行了一次,且没有在b.N循环中重复进行内存分配,因此0 B/op的结果也就不难理解了。
此外,原始的generate函数使用了rand.Seed(time.Now().UTC().UnixNano())。在基准测试中,为了保证测试数据的可重复性,通常建议使用一个固定的随机数种子来生成数据。
Anakin
一站式 AI 应用聚合平台,无代码的AI应用程序构建器
290
查看详情
要正确地对排序算法进行基准测试,我们需要遵循以下原则:
以下是修正后的generate函数和基准测试函数示例:
package child_sort
import (
"math/rand"
"testing"
)
// generateBenchData 使用固定种子生成可重复的随机整数切片
func generateBenchData(size int, min, max int) []int {
// 使用一个固定的源来创建rand.Rand实例,确保每次基准测试运行的数据序列相同
// 避免在每次调用generate时都使用time.Now(),这会使测试数据不可预测
src := rand.NewSource(42) // 固定种子,例如42
r := rand.New(src)
xs := make([]int, size, size)
for i := range xs {
xs[i] = min + r.Intn(max-min)
}
return xs
}
// SortBubble, SortSelection, SortInsertion (代码与原始问题相同,此处省略)
// ...
func BenchmarkBubbleCorrected(b *testing.B) {
// 1. 在计时器重置前准备原始数据
// 注意:这里的原始数据只生成一次
originalData := generateBenchData(10000, -100, 100)
b.ResetTimer() // 2. 重置计时器,排除数据生成的时间
// 3. 将排序操作放入b.N循环中
for i := 0; i < b.N; i++ {
// 4. 为每次排序操作创建一个数据的副本
// 这是必要的,因为排序会修改原始切片。
// 每次都从一个未排序的副本开始,才能准确衡量排序算法的性能。
dataCopy := make([]int, len(originalData))
copy(dataCopy, originalData)
SortBubble(dataCopy)
}
}
func BenchmarkSelectionCorrected(b *testing.B) {
originalData := generateBenchData(10000, -100, 100)
b.ResetTimer()
for i := 0; i < b.N; i++ {
dataCopy := make([]int, len(originalData))
copy(dataCopy, originalData)
SortSelection(dataCopy)
}
}
func BenchmarkInsertionCorrected(b *testing.B) {
originalData := generateBenchData(10000, -100, 100)
b.ResetTimer()
for i := 0; i < b.N; i++ {
dataCopy := make([]int, len(originalData))
copy(dataCopy, originalData)
SortInsertion(dataCopy)
}
}执行修正后的基准测试:
go test --bench . --benchmem
你将看到更合理、更符合预期的结果,例如:
BenchmarkBubbleCorrected-8 100 11234567 ns/op 40000 B/op 1 allocs/op BenchmarkSelectionCorrected-8 2000 567890 ns/op 40000 B/op 1 allocs/op BenchmarkInsertionCorrected-8 1000 890123 ns/op 40000 B/op 1 allocs/op
(注:实际结果会根据机器性能和Go版本有所不同,此处为示例数据)
现在,ns/op值将反映每次排序操作的真实平均时间,而B/op和allocs/op则反映了每次迭代中(由于copy操作)的内存分配情况。
Go语言的基准测试是一个强大的工具,但正确使用它至关重要。理解testing.B的工作原理,特别是b.N循环和b.ResetTimer()的正确应用,是获得准确性能指标的关键。对于修改输入数据的算法(如排序),务必在每次迭代中提供一个“新鲜”的数据副本。遵循这些最佳实践,可以有效避免常见的陷阱,确保你的性能分析结果可靠且具有指导意义。
以上就是Go语言基准测试:解析与优化非预期结果的详细内容,更多请关注其它相关文章!
# 器中
# 兴宁网站建设制作定做
# 民宿推广软文营销方案
# SEO网站推广与优化方案口红
# 济南问答营销推广公司
# 枣庄手机网站建设报价
# 哪个网站建设论文好点
# 做推广网站询问l火17星热情
# 东莞网站群发优化
# 长乐抖音关键词排名推广
# 重庆忠县网站建设书籍
# 的是
# 以确保
# 中都
# go
# 掉了
# 随机数
# 迭代
# 执行时间
# 计时器
# 冒泡排序
# 性能瓶颈
# 性能测试
# 排序算法
# unix
# 工具
# go语言
相关栏目:
【
Google疑问12 】
【
Facebook疑问10 】
【
优化推广96088 】
【
技术知识133117 】
【
IDC资讯59369 】
【
网络运营7196 】
【
IT资讯61894 】
相关推荐:
Mac怎么关闭按键声音_Mac键盘打字音效设置
苹果自助维修计划支持哪些设备机型
c++如何掌握指针的核心用法_c++指针入门到精通指南
快手网页版官方访问 快手网页版页面在线打开
深入理解Python对象引用与链表属性赋值
c++中的const关键字用法大全_c++ const正确使用指南
《鹿路通》退余额方法
Firefox OS应用开发:解决XMLHttpRequest跨域请求阻塞问题
红手指专业版app注册教程
Python项目中的条件导入:解决跨模块依赖问题
mysql归档数据怎么导出为csv_mysql归档数据导出为csv文件的方法
sublime怎么快速在浏览器中预览HTML_sublime配置View in Browser教程
快递查询,一键速查
J*aScript 数值去小数位处理:多种方法与实践
Python自动化抓取GBGB赛狗比赛结果:日期范围与赛道筛选教程
J*a实现任务清单管理_集合框架综合入门练手
《东方财富》条件单关闭方法
Apple Music无故扣费引质疑
J*aScript桌面应用_Electron多进程架构实战
QQ网页版官方账号登录入口 QQ网页版网页版入口快速导航
太平年在哪个平台播出
《sketchbook》选中部分图案移动方法
React应用中Commerce.js数据加载与状态管理最佳实践
《随手记》启用语音备注方法
Flexbox布局:实现粘性导航与底部页脚的完美结合
知乎APP怎么查看自己被邀请的问题_知乎APP邀请回答记录查看与参与方法
《图怪兽》退出登录方法
Win10截图远程协助 Win10远程桌面截屏法【场景应用】
拷贝漫画2025网页版入口 拷贝漫画官网免费看全集
圆通快递包裹轨迹查询 圆通速递快件实时位置跟踪
谷歌浏览器如何查找和删除恶意软件 谷歌浏览器内置安全清理工具使用教程
谷歌浏览器怎么把网页翻译成中文_Chrome网页翻译功能使用方法
在J*a中如何实现类的继承与方法重用_OOP继承方法重用技巧分享
铁路12306座位怎么选_12306官方选座操作方法
C++如何使用CMake构建项目_C++ CMakeLists.txt编写入门教程
谷歌邮箱官方入口链接 谷歌邮箱网页版电脑端快速登录
海棠阅读网页版_进入海棠网页版在线阅读中心
鲁班大师乓乓皮肤获取方法
手机雨课堂网页版入口免登录 雨课堂网页版可点击直接进入
CodeIgniter 3 中基于 MySQL 数据高效生成动态图表教程
火柴人战争网页版在线玩
J*a中为什么强调组合优于继承_组合模式带来的灵活性与可维护性解析
鲨鱼剧场app金币获取方法
word表格如何按某一列内容进行排序_Word表格按列排序方法
Scipy Sparse CSR 矩阵非零元素行级遍历的最佳实践
漫蛙官网(首页入口)_漫蛙漫画稳定访问教程分享
Win10如何查看已安装的更新补丁 Win10卸载指定更新教程【教程】
抖音网页版官方链接 抖音网页版官网链接入口
纯CSS实现自适应宽度与响应式布局的水平按钮组
告别阻塞等待:如何使用GuzzlePromises优雅处理PHP异步操作,提升应用响应速度
2025-11-30
运城市盐湖区信雨科技有限公司是一家深耕海外推广领域十年的专业服务商,作为谷歌推广与Facebook广告全球合作伙伴,聚焦外贸企业出海痛点,以数字化营销为核心,提供一站式海外营销解决方案。公司凭借十年行业沉淀与平台官方资源加持,打破传统外贸获客壁垒,助力企业高效开拓全球市场,成为中小企业出海的可靠合作伙伴。