【Go高性能】测试(单元测试、基准测试)

Go测试

  • 一、分类
    • 1. 单元测试
    • 2. 基准测试
  • 二、基准测试
    • 1. 介绍
    • 2. 基准测试基本原则
    • 3. 使用testing包构建基准测试
      • 3.1 执行基准测试
      • 3.2 基准测试工作原理
      • 3.3 改进基准测试的准确性
        • 3.3.1 -benchtime
        • 3.3.2 -count
        • 3.3.3 -cpu
    • 4. 使用benchstat工具比较基准测试(可跳过)
      • 4.1 对比标准 benchmarks 和 benchstat
    • 5. 避免基准测试的启动耗时
    • 6. 基准测试的内存分配
  • 三、单元测试
    • 1. Goland自动生成
    • 2. 常用断言方法


介绍如何使用 Go 语言的标准库 testing 进行测试。

参考:①https://cloud.tencent.com/developer/article/2211864
②https://geektutu.com/post/quick-go-test.html#7-Benchmark-%E5%9F%BA%E5%87%86%E6%B5%8B%E8%AF%95

一、分类

在Go语言的世界里,testing包是进行单元测试和基准测试的核心组件。它不仅简化了测试流程,还通过简洁明了的API鼓励开发者编写高质量的测试代码。

Go 语言推荐测试文件和源代码文件放在一块,测试文件以 _test.go 结尾。

1. 单元测试

  • 功能: 测试单个函数/方法,报告测试失败、记录日志、设置测试状态等。
  • 示例: 在 func TestXxx(t *testing.T) 中使用,适用于验证代码的正确性。

2. 基准测试

  • 功能:用于基准测试(性能测试),记录执行时间和内存分配等性能指标。
  • 示例: 在 func BenchmarkXxx(b *testing.B) 中使用,适用于测量代码的性能。

二、基准测试

1. 介绍

要想改进程序的性能,首先要知道程序的当前性能。
benchmark 是 go 语言中用于测试基准性能的工具。该工具用于测试被测试函数的平均运行耗时、内存分配次数。主要适用于在已知性能瓶颈在哪里时的场景。通过对相同功能函数的不同实现的性能指标(平均运行耗时、平均内存分配次数)进行比较,以判断性能的优劣。

2. 基准测试基本原则

为了保证基准测试结果的相对稳定性,需要保持硬件环境的稳定。即:

  • 机器处于空闲状态
  • 机器关闭了节能模式
  • 避免使用虚拟机和云主机

不过如果单纯测试某个函数的性能,精准性要求也不高的话,没必要这么细致啦~

3. 使用testing包构建基准测试

Go的tesing包中内置了基准测试功能。在编写基准测试时基本和编写单元测试的原则相似:

  • 文件名必须以 _test.go 为后缀
  • 函数名必须以 BenchmarkXxxx开头
  • 基准测试函数的参数类型是 *Testing.B,而非 *Testing.T

需要测试函数:

func singleFunc() {time.Sleep(100 * time.Millisecond)
}

基准测试代码:

func Benchmark_singleFunc(b *testing.B) {for n := 0; n < b.N; n++ {singleFunc()}
}

3.1 执行基准测试

  1. 方法一:直接点击方法左侧运行
    在这里插入图片描述
  2. 方法二:使用 go test -bench=要测的方法名 ./方法所在包的相对路径
    例如:go test -bench=singleFunc(已在函数所在包的路径下)
  • -bench 标记使用正则表达式来匹配要运行的基准测试函数名称。所以,最常用的方式是通过-bench . 标记来执行该包下的所有的基准函数。
  • 默认情况下,执行go test命令时 只会执行单元测试,而基准测试会被排除在外。所以,需要在 go test 命令中添加 -bench 标记,以执行基准测试。

基准测试报告每一列值对应的含义如下:

type BenchmarkResult struct {N         int           // 迭代次数T         time.Duration // 基准测试花费的时间Bytes     int64         // 一次迭代处理的字节数MemAllocs uint64        // 总的分配内存的次数MemBytes  uint64        // 总的分配内存的字节数
}

测试结果:

1. goos: windows
2. goarch: amd64
3. pkg: method/f
4. Benchmark_singleFunc
5. Benchmark_singleFunc-6                10         101990650 ns/op
5. PASS

关注指标:

  1. 第5行Benchmark_singleFunc-6 10 101990650 ns/op
  • Benchmark_singleFunc: 这是基准测试的名称,表示测试的函数名。
  • -6: 这是运行基准测试的 goroutine 数量(并发数)。和运行该测试用例时的GOMAXPROCS值有关系,默认为启动时 Go 进程可见的 CPU 数。在这个例子中,6表示使用了 6 个 goroutine 来并行执行基准测试。
  • 10:是基准测试在测试期间执行的总次数(b.N 的值)。
  • 101990650 ns/op:是每次操作的平均耗时,单位是纳秒(ns)。

3.2 基准测试工作原理

每个基准函数被执行时都有一个b.N值,该值是由go运行时自动生成的, 代表基准函数应该执行的次数。

b.N 从 1 开始,基准函数默认要运行 1 秒,如果该函数的执行时间在 1 秒内就运行完了,那么就递增 b.N 的值,再重新再执行一次。

3.3 改进基准测试的准确性

3.3.1 -benchtime

基准测试运行的时间越长,迭代次数越多,最终的平均值结果越准确。

如果你的基准测试只执行了 100 次或 10 次迭代,那么最终得出的平均值可能会偏高。如果你的基准测试执行了上百万或十亿次迭代,那么得出的平均耗时将会非常准确。

可以使用 -benchtime 标识指定基准测试执行的时间以调整迭代次数(即b.N的值),以便得到更准确的结果。例如:

PS E:\Program Data\GoProject\basic\method\f> go test -bench=singleFunc -benchtime=10s
goos: windows
goarch: amd64
pkg: method/f
Benchmark_singleFunc-6               100         103430246 ns/op
PASS
ok      method/f        13.644s

执行以上命令,直到其达到 b.N 的值需要花费超过 10 秒的时间才能返回。由于我们的运行时间增加了 10 倍,因此迭代的总次数也增加了 10 倍。结果(每次操作耗时 103430246ns/op) 没有太大的变化,说明我们的数据相对比较稳定,是我们所期望的。
如果你有一个基准测试运行了数百万次或数十亿次迭代,你可能会发现基准值不稳定,因为你的机器硬件的散热性能、内存局部性、后台进程、gc 等因素都会影响函数执行的时间。

3.3.2 -count

通过 -count 标志,可以指定基准测试跑多次,以消除上述的不稳定因素:

 go test -bench . -benchtime 2s -count 5 
goos: windows
goarch: amd64
pkg: method/f
Benchmark_singleFunc-6                22         103056855 ns/op
Benchmark_singleFunc-6                21         102299486 ns/op
Benchmark_singleFunc-6                22         104436250 ns/op
Benchmark_singleFunc-6                21         102005167 ns/op
Benchmark_singleFunc-6                21         102501433 ns/op
PASS
ok      method/f        14.764s
3.3.3 -cpu
go test -bench . -cpu 1,2,4            
goos: windows
goarch: amd64
pkg: method/f
Benchmark_singleFunc                  10         102037000 ns/op
Benchmark_singleFunc-2                10         101757150 ns/op
Benchmark_singleFunc-4                10         102318930 ns/op
PASS
ok      method/f        6.607s

该示例展示了分别用 CPU 为 1 核、2 核、4 核时运行基准测试的结果。

4. 使用benchstat工具比较基准测试(可跳过)

由于基准测试受电源管理、后台进程、散热的影响,所以对于任何一个基准测试来说,运行多次来求平均值是一个非常好的建议。

下面介绍一个由 Russ Cox 编写的工具:benchstat

% go get golang.org/x/perf/cmd/benchstat

benchstat 可以对一组基准测试的结果求平均值,并显示出对应的稳定性。这是函数在使用电池的电脑上执行的基准示例:

% go test -bench . -benchtime 2s -count 5 | tee old.txt
goos: windows
goarch: amd64
pkg: method/f
Benchmark_singleFunc-6                22         103056855 ns/op
Benchmark_singleFunc-6                21         102299486 ns/op
Benchmark_singleFunc-6                22         104436250 ns/op
Benchmark_singleFunc-6                21         102005167 ns/op
Benchmark_singleFunc-6                21         102501433 ns/op
PASS
ok      method/f        14.764s% benchstat old.txt
name     time/op
singleFunc-6  102.5ms ± 2%

平均操作耗时是102.5毫秒,并且误差在 +/-2%。

4.1 对比标准 benchmarks 和 benchstat

确定两组基准测试结果之间的差异可能是单调乏味且容易出错的。 Benchstat 可以帮助我们解决这个问题。

提示 : 保存基准运行的输出很有用,但你也可以保存生成它的二进制文件。 为此,请使用-c标志来保存测试二进制文件;我经常将这个二进制文件从.test重命名为.golden。

% go test -c
% mv fib.test fib.golden 

为了比较新版本,我们编译了一个新的测试二进制文件并对它们都进行了基准测试,并使用benchstat对输出进行比较。

% go test -c
% ./fib.golden -test.bench=. -test.count=10 > old.txt
% ./fib.test -test.bench=. -test.count=10 > new.txt
% benchstat old.txt new.txt
name     old time/op  new time/op  delta
Fib20-8  44.3µs ± 6%  25.6µs ± 2%  -42.31%  (p=0.000 n=10+10)

比较基准测试时需要检查三件事

  • 新老两次的方差。1-2% 是不错的, 3-5% 也还行,但是大于5%的话,可能不太可靠。 在比较一方具有高差异的基准时要小心,您可能看不到改进。
  • p值。p值低于0.05是比较好的情况,大于0.05则意味着基准测试结果可能没有统计学意义。
  • 样本不足。benchstat将报告它认为有效的新旧样本的数量,有时你可能只发现9个报告,即使你设置了-count=10。拒绝率小于10%一般是没问题的,而高于10%可能表明你的设置是不稳定的,也可能是比较的样本太少了。

5. 避免基准测试的启动耗时

有时候基准测试每次执行的时候会有一次启动配置耗时。b.ResetTimer() 函数可以用于忽略启动的累积耗时。如下

func BenchmarkExpensive(b *testing.B) {boringAndExpensiveSetup() //启动配置。默认这里的执行时间是被计算在内的b.ResetTimer()for n := 0; n < b.N; n++ {//function under test}
}

在上例代码中,使用 b.ResetTimer() 函数重置了基准测试的计时器

如果在每次循环迭代中,你有一些费时的配置逻辑,要使用 b.StopTimer()b.StartTimer() 函数来暂定基准测试计时器。

func BenchmarkComplicated(b *testing.B) {for n := 0; n < b.N;n++ {b.StopTimer()complicatedSetup()b.StartTimer()//function under test}
}

6. 基准测试的内存分配

内存分配的次数和分配的大小跟基准测试的执行时间相关。在基准测试中有两种方式可以记录并输出内存分配:

  • 在代码中增加 b.ReportAllocs() 函数来告诉 testing 框架记录内存分配的数据。
  • 在go test命令中添加 -benchmem 标识来强制 testing 框架打印出所有基准测试的内存分配次数

方式一:代码中添加 b.ReportAllocs()

func BenchmarkRead(b *testing.B) {b.ReportAllocs()for n := 0; n < b.N; n++ {//function under test}
}

方式二:go test命令中添加 -benchmem标识

%  go test -run=^$ -bench=. -benchmem bufio
goos: darwin
goarch: amd64
pkg: bufio
BenchmarkReaderCopyOptimal-8            13860543                82.8 ns/op            16 B/op          1 allocs/op
BenchmarkReaderCopyUnoptimal-8           8511162               137 ns/op              32 B/op          2 allocs/op
BenchmarkReaderCopyNoWriteTo-8            379041              2850 ns/op           32800 B/op          3 allocs/op
BenchmarkReaderWriteToOptimal-8          4013404               280 ns/op              16 B/op          1 allocs/op
BenchmarkWriterCopyOptimal-8            14132904                82.7 ns/op            16 B/op          1 allocs/op
BenchmarkWriterCopyUnoptimal-8          10487898               113 ns/op              32 B/op          2 allocs/op
BenchmarkWriterCopyNoReadFrom-8           362676              2816 ns/op           32800 B/op          3 allocs/op
BenchmarkReaderEmpty-8                   1857391               639 ns/op            4224 B/op          3 allocs/op
BenchmarkWriterEmpty-8                   2041264               577 ns/op            4096 B/op          1 allocs/op
BenchmarkWriterFlush-8                  87643513                12.5 ns/op             0 B/op          0 allocs/op
PASS
ok      bufio   13.430s

第四列是每次操作的平均内存分配大小,单位是字节(B)。
第五列是每次操作的平均内存分配次数(allocations)。

三、单元测试

单元测试通常放置在与被测试文件同目录下的_test.go文件中。测试函数必须以Test开头,后接被测试函数名,接受一个t *testing.T参数。

1. Goland自动生成

  1. 把鼠标定在要测试的方法上面,右击选Generate,
    在这里插入图片描述
    在这里插入图片描述
  2. 生成测试文件
    在这里插入图片描述
  3. 增加测试数据
    在这里插入图片描述
  4. 运行测试
    可以直接运行全部的测试方案,也可以自己选择想要运行的测试方案。

2. 常用断言方法

  • t.Errort.Fatal:报告错误,后者还会终止测试。
  • t.Logf:记录日志信息。
  • t.Errorf:当条件不满足时,记录错误并继续执行后续测试。

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

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

SpringBoot日常:Spring之@PostConstruct解析

简介 spring的Bean在创建的时候会进行初始化&#xff0c;而初始化过程会解析出PostConstruct注解的方法&#xff0c;并反射调用该方法。 PostConstruct 的使用和特点 只有一个非静态方法能使用此注解&#xff1b;被注解的方法不得有任何参数&#xff1b;被注解的方法返回值必…

数据主权与隐私保护的深入探讨

随着数字化进程的加速&#xff0c;数据已成为当今世界的重要资源。数据主权和隐私保护这两个概念也越来越受到关注。数据主权涉及到国家对数据的控制权和管理权&#xff0c;而隐私保护则关乎个人数据的安全性和隐私权利。两者相互交织&#xff0c;共同塑造了数字时代的法律、经…

[RIS]GRES: Generalized Referring Expression Segmentation

1. BaseInfo TitleGRES: Generalized Referring Expression SegmentationAdresshttps://arxiv.org/pdf/2306.00968Journal/TimeCVPR2023Author南洋理工Codehttps://github.com/henghuiding/ReLARead20240829TableVisonLanguage 2. Creative Q&A 考虑结果多目标和无目标&…

2024软考:一场与“难”共舞的奇妙冒险,你值得拥有!

在这个时代&#xff0c;如果说有什么考试能让IT界的勇士们闻风丧胆&#xff0c;又爱又恨&#xff0c;那软考绝对能C位出道&#xff0c;成为众多技术大佬心中的“白月光”与“朱砂痣”。随着岁月悠悠&#xff0c;2024年的软考似乎又悄悄地在难度上动了点小心思&#xff0c;让人不…

【C++ Primer Plus习题】6.5

问题: 解答: #include <iostream> using namespace std;int main() {float salary 0;float tax 0;while (salary>0){cout << "请输入您的工资:";cin >> salary;if (cin.fail())break;if (salary < 5000){tax 0;}else if (salary < 15…

风控领域特征工程

在金融行业&#xff0c;风险控制&#xff08;风控&#xff09;是核心环节&#xff0c;它关乎资产安全、合规性以及机构的长期稳健发展。随着大数据时代的到来&#xff0c;金融机构面临着前所未有的数据量和复杂性。在这样的背景下&#xff0c;风控领域特征工程应运而生&#xf…

20240829版图的层次

1 最常用 Esc&#xff1a;取消操作 i&#xff1a;插入元件版图 c&#xff1a;复制 m&#xff1a;移动 u&#xff1a;撤销上一步操作 q&#xff1a;查看属性 f&#xff1a;全局视图 e&#xff1a;显示设置&#xff08;图层、栅格、走线模式等&#xff09; r&#xff1a;矩形填充…

<Rust>egui学习之小部件(六):如何在窗口中添加菜单栏部件?

前言 本专栏是关于Rust的GUI库egui的部件讲解及应用实例分析&#xff0c;主要讲解egui的源代码、部件属性、如何应用。 环境配置 系统&#xff1a;windows 平台&#xff1a;visual studio code 语言&#xff1a;rust 库&#xff1a;egui、eframe 概述 本文是本专栏的第六篇博…

【爬虫软件】采集抖音博主的主页发布作品

这是我用python开发的抖音爬虫采集软件&#xff0c;可自动按博主抓取已发布视频。 软件界面截图&#xff1a; 爬取结果截图&#xff1a; 几点重要说明&#xff1a; 软件使用演示视频&#xff1a; https://www.bilibili.com/video/BV1Kb42187qf 完整讲解文章&#xff1a; ht…

释放金融交易的未来:掌握量化机器人技术

在金融交易的竞技场上&#xff0c;量化机器人技术正成为投资者手中的一张王牌&#xff0c;引领着交易策略的未来。本文将向您展示量化机器人如何成为金融交易的变革者&#xff0c;并向您展示如何利用这一技术来优化您的投资策略。 量化机器人的力量 量化机器人技术是金融交易领…

Anaconda3简介与安装步骤

目录 Anaconda3简介与功能 1.Anaconda3简介 2.主要功能和特点 3.使用场景 4.总结 Anaconda3安装 1.Anaconda3下载 1.1我的百度网盘 1.2官网下载 1.2.1访问官网 1.2.2输入邮箱 1.2.3登录你的邮箱下载&#xff08;你的噶&#xff09; 2.安装 2.1双击安装 2.2选择安…

Linux——nginx 负载均衡

常规的web服务器一般提供对于静态资源的访问&#xff0c;比如说&#xff1a;图片、web样式 网站提供的大部分交互功能都需要web编程语言的支持&#xff0c;而web服务对于程序的调用&#xff0c;不管编译型语言还是解释型语言&#xff0c;web服务同将对于应用程序的调用递交给通…

路别走窄了,华为认证的这些方向,比数通值钱

华为认证作为全球领先的ICT技术认证之一&#xff0c;以其高标准和权威性在全球范围内受到认可。它不仅代表了专业技能的国际水平&#xff0c;更是IT专业人士职业生涯中的重要里程碑。但谈论起华为认证&#xff0c;似乎大家都默认首选数通。 不止在华为认证&#xff0c;而是在整…

Vue使用v-model收集各种表单数据、过滤器

目录 1. 使用v-model收集各种表单数据2. 日期格式化3. 过滤器 1. 使用v-model收集各种表单数据 若<input type“text”/>&#xff0c;则v-model收集的是value值&#xff0c;用户输入的就是value值若<input type“radio”/>&#xff0c;则v-model收集的是value值&a…

H5开发有哪些技巧?

随着现代社会的飞速发展&#xff0c;网页开发已经从传统的HTML、CSS、JavaScript往H5发展。H5也称为HTML5&#xff0c;可以理解为是HTML的升级版&#xff0c;具有更加优秀的性能、更加完善的功能和更加多样的体验。因其灵活性和跨平台特性&#xff0c;成为了各类移动应用和网页…

R语言报错 | object ‘integral‘ not found whilst loading name

1、报错背景 Registered S3 method overwritten by htmlwidgets:method from print.htmlwidget tools:rstudio Error: package or namespace load failed for ‘Seurat’:object integral not found whilst loading namespace spatstat.core 当我想library&…

sheng的学习笔记-AI-半监督学习

AI目录&#xff1a;sheng的学习笔记-AI目录-CSDN博客 基础知识 什么是半监督学习 我们在丰收季节来到瓜田&#xff0c;满地都是西瓜&#xff0c;瓜农抱来三四个瓜说这都是好瓜&#xff0c;然后再指着地里的五六个瓜说这些还不好&#xff0c;还需再生长若干天。基于这些信息&a…

【Qt CMake】Qt5Widgets.dll:-1: error: LNK1107: 文件无效或损坏: 无法在 0x308 处读取

项目场景&#xff1a; CMake 编译QT 工程 编译报错 E:\tool\Qt5.15.2\5.15.2\msvc2019_64\bin\Qt5Widgets.dll&#x1f44e; error: LNK1107: 文件无效或损坏: 无法在 0x308 处读取 问题描述 Cmkae 报错&#xff1a; E:\tool\Qt5.15.2\5.15.2\msvc2019_64\bin\Qt5Widgets.dll…

Linux 数据结构 链表

1.段错误调试方法&#xff1a; 1.按照网上的方法配置Ubuntu&#xff0c;允许生成core文件 2.重新编译代码并加入-g选项(允许进行GDB调试) 3.ulimit -c unlimited 不限制core文件的生成的大小 4.执行代码,复现段错误,产生包含出错信息的core文件(检查cor…

Android UI绘制原理:UI的绘制流程是怎么样呢?为什么子线程不能刷新UI呢?讲解大体的流程是怎么样的

目录&#xff1a; 一、 为什么要学习android UI绘制原理呢&#xff1f;对我们有什么帮助&#xff1f; 1.解决复杂布局问题&#xff1a;了解UI绘制原理可以帮助我们更好地理解和解决布局问题&#xff0c;比如使用自定义View、优化布局层级等。 2.知道何时触发布局&#xff08;…