go benchmark 基准测试

这篇具有很好参考价值的文章主要介绍了go benchmark 基准测试。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

目录

一、benchmark的使用

1.一个简单的例子

2.运行用例

3.benchmark 是如何工作的

4.提升准确度

5.内存分配情况

6.测试不同的输入

二、benchmark的注意事项

1.ResetTimer

2.StopTimer & StartTimer


一、benchmark的使用

1.一个简单的例子

go mod init test 创建项目test,创建目录bench/fib

创建fib.go

package fib

func fib(n int) int {
	if n == 0 || n == 1 {
		return n
	}
	return fib(n-1) + fib(n-2)
}

创建fib_test.go

package fib

import (
	"testing"
)

func BenchmarkFib(b *testing.B) {
	for i := 0; i < b.N; i++ {
		fib(30)
	}
}

go 不管是单元测试还是基准测试,测试函数都应该写在以_test.go 为结尾的文件中。

go 单元测试函数以Test开头,函数参数为*testing.T

go 基准测试函数以Bench开头,函数参数为*testing.B

2.运行用例

如何运行测试用例呢?

运行单元测试:go test -run=xxx,其中xxx为正则表达式,用来匹配单元测试函数的函数名。

运行基准测试:go test -bench=xxx,其中xxx为正则表达式,用来匹配基准测试函数的函数名。

上述命令只能运行当前目录中的测试用例,如果想运行其他目录的测试用例呢?

go test -bench=. test/bench/fib  ,指定目标包在项目中的绝对路径。

go test -bench=. ./fib  , 运行当前目录下的子目录fib中的测试。

go test -bench=. ./... , 运行当前目录下的所有的package中的测试。

go benchmark 基准测试,golang,单元测试

对-bench=加上正则表达式:

go test -bench=^BenchmarkFib ./fib

go benchmark 基准测试,golang,单元测试

3.benchmark 是如何工作的

benchmark用例的参数为b testing.B, b.N 表示要测试的内容运行的次数,这个次数对于每个用例都不同。那么这个次数变化规律是什么呢?b.N从1开始,如果内容在1s内运行结束,那么b.N会增加,测试会再次执行。b.N 的值大概以 1, 2, 3, 5, 10, 20, 30, 50, 100 这样的序列递增,越到后面,增加得越快。

修改测试的内容,让运行时间>1s


func BenchmarkFib(b *testing.B) {
	for i := 0; i < b.N; i++ {
		time.Sleep(time.Second)
		fib(30)
	}
}

go benchmark 基准测试,golang,单元测试

 可以看到,只执行了1次。

benchmarkFib-12 的-12的意思是 GOMAXPROCS数,即cpu核数,可以使用-cpu来指定cpu核数,-cpu=2,3  表示分别使用GOMAXPROCS=2 和GOMAXPROCS=3 进行测试。

go benchmark 基准测试,golang,单元测试

可以看到测试用例运行了两轮,分别以单核和12核,但我们的测试结果没有发生变化,因为我们的测试内容本身是单核的,与多核无缘。

4.提升准确度

对于性能测试来说,提升测试精度的一个重要手段是提升测试时间和测试次数。我们可以是用-benchtime 和 -count 来达到目的。

benchmark 默认的benchtime是1s,我们指定2s,可以看到执行次数也提升了约1倍。

go benchmark 基准测试,golang,单元测试

我们还能直接指定b.N ,即 -benchtime=30x 表示30次

go benchmark 基准测试,golang,单元测试

那么count就是测试的轮数了,-count=2 测试两轮。

go benchmark 基准测试,golang,单元测试

5.内存分配情况

前面的测试结果中,只能看见执行的次数和一次执行的时间,没有任何与内存相关的信息。加入 -benchmem 就可以看到。

go benchmark 基准测试,golang,单元测试

 因为fib函数使用的空间全在栈上,不需要进行内存分配。

下面测试切片的内存分配。创建目录 bench/cap

cap.go


// 一次性分配空间
func generateWithCap(n int) []int {
	rand.Seed(time.Now().UnixNano())
	nums := make([]int, 0, n)
	for i := 0; i < n; i++ {
		nums = append(nums, rand.Int())
	}
	return nums
}

// 多次分配空间
func generate(n int) []int {
	rand.Seed(time.Now().UnixNano())
	nums := make([]int, 0)
	for i := 0; i < n; i++ {
		nums = append(nums, rand.Int())
	}
	return nums
}

cap_test.go

package cap

import "testing"

func BenchmarkGenerateWithCap(b *testing.B) {
	for n := 0; n < b.N; n++ {
		generateWithCap(1000000)
	}
}

func BenchmarkGenerate(b *testing.B) {
	for n := 0; n < b.N; n++ {
		generate(1000000)
	}
}

 测试结果:

go benchmark 基准测试,golang,单元测试

可以看到:

        一次性分配内存的切片赋值函数比多次分配内存的切片赋值函数消耗内存更少。

        一次性分配内存的切片赋值函数运行时间更少,因为内存分配需要耗时间。

6.测试不同的输入

  不同函数的复杂度不同,O(1)、O(n)、O(lgn)等,利用 benchmark 验证复杂度一个简单的方式,是构造不同的输入,对刚才的generate函数构建不同的输入可以达到这个目的。

  cap_test.go

func benchmarkGenerate(n int, b *testing.B) {
	for i := 0; i < b.N; i++ {
		generate(n)
	}
}

func BenchmarkGenerate1000(b *testing.B)    { benchmarkGenerate(1000, b) }
func BenchmarkGenerate10000(b *testing.B)   { benchmarkGenerate(10000, b) }
func BenchmarkGenerate100000(b *testing.B)  { benchmarkGenerate(100000, b) }
func BenchmarkGenerate1000000(b *testing.B) { benchmarkGenerate(1000000, b) }

随着输入按10倍的速度增长,运行时间也按10倍在增长,则函数的复杂度是线性的,即O(n).

go benchmark 基准测试,golang,单元测试

二、benchmark的注意事项

1.ResetTimer

如果在正式执行测试前需要进行准备工作,那么在准备工作完成后,可以使用b.ResetTimer() 函数来重置计数器。

使用sleep模拟耗时的准备工作。

fib_test.go 

func BenchmarkFib(b *testing.B) {
	time.Sleep(time.Second)
	for i := 0; i < b.N; i++ {
		fib(30)
	}
}

go benchmark 基准测试,golang,单元测试

每次执行fib(30)高达1s多,显然不对。

使用b.ResetTimer()

 fib_test.go


func BenchmarkFib(b *testing.B) {
	time.Sleep(time.Second)
	b.ResetTimer()
	for i := 0; i < b.N; i++ {
		fib(30)
	}
}

go benchmark 基准测试,golang,单元测试

我们将耗时的准备工作排除在测试之外,每次调用fib(30)花费 6ms = 0.006s 

2.StopTimer & StartTimer

如果在每一次函数前后都需要准备工作和清理工作,那么就需要StopTimer + StartTimer 函数了。

例:

     sort_test.go

// 一次性分配空间
func generateWithCap(n int) []int {
	rand.Seed(time.Now().UnixNano())
	nums := make([]int, 0, n)
	for i := 0; i < n; i++ {
		nums = append(nums, rand.Int())
	}
	return nums
}


//冒泡排序
func bubbleSort(nums []int) {
	for i := 0; i < len(nums); i++ {
		for j := 1; j < len(nums)-i; j++ {
			if nums[j] < nums[j-1] {
				nums[j], nums[j-1] = nums[j-1], nums[j]
			}
		}
	}
}

func BenchmarkBubbleSort(b *testing.B) {
	for n := 0; n < b.N; n++ {
        //暂停计时
		b.StopTimer()
		nums := generateWithCap(10000)
        //继续计时
		b.StartTimer()
		bubbleSort(nums)
	}
}

go benchmark 基准测试,golang,单元测试

显然我们只测试到排序的性能,没有将内存分配的时间花费算入结果。

每次排序需花费120ms的时间。文章来源地址https://www.toymoban.com/news/detail-786675.html

到了这里,关于go benchmark 基准测试的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 【go语言】3.3.1 单元测试和基准测试

    Go 语言的 testing 包为编写单元测试和基准测试提供了强大的支持。单元测试用于验证代码的正确性,基准测试用于测量代码的性能。 在Go语言中,单元测试和基准测试是两种常用的测试方法,用于测试和评估代码的质量和性能。 单元测试是一种针对代码中最小可测试单元(函

    2024年02月08日
    浏览(42)
  • 【测试开发】单元测试、基准测试和性能分析(以 Go testing 为例)

    你写不出 bug-free 的代码。 你认为自己写出了 bug-free 的代码,但它在你意想不到的地方出错了。 你觉得自己写出了永不出错的代码,但它的性能十分糟糕。 “测试左移”距离真正落地或许还有不短的距离,但在开发过程中注重自己的代码质量,至少养成 写单测 的习惯还是很

    2024年02月04日
    浏览(47)
  • Golang单元测试和压力测试

            go语言中的测试依赖go test命令。编写测试代码和编写普通的Go代码过程类似,并不需要学习新的语法,规则和工具。         go test命令是一个按照一定约定和组织的测试代码的驱动程序。在包目录内,所有以_test.go为后缀名的源代码文件都是go test测试的一部分,不会

    2024年04月27日
    浏览(31)
  • Golang单元测试详解(一):单元测试的基本使用方法

    Golang 中的单元测试是使用标准库 testing 来实现的,编写一个单元测试是很容易的: 创建测试文件:在 Go 项目的源代码目录下创建一个新的文件(和被测代码文件在同一个包),以 _test.go 为后缀名。例如,要测试net包中 dial.go 中的方法,在 net 包中创建一个名字为 dial_test.g

    2024年02月06日
    浏览(44)
  • Golang 单元测试

    前言 单元测试是通过编写测试函数来完成的,这些函数位于_test.go文件中 步骤 要创建一个单元测试,你需要遵循以下步骤: 在与要测试的代码相同的包中创建一个新的文件,文件名以_test.go结尾 导入 testing 包 编写测试函数,函数名以 Test 开头,接受一个 *testing.T 类型的参数

    2024年01月16日
    浏览(34)
  • Golang单元测试举例

    cal.go  cal_test.go 说明:再GoLand中,要运行测试哪个函数可以自行选择 测试文件名必须以_test.go结尾; 测试方法的开头必须是Testxxx()  monster.go  monster_test.go

    2024年02月10日
    浏览(43)
  • GoLang 单元测试打桩和 mock

    目录 什么是 mock 变量打桩 接口方法/Redis 函数/方法打桩 包函数 成员方法 MySQL sqlmock sqlite mock gorm http mock 源码地址 单测基础        单元测试,顾名思义对某个单元函数进行测试,被测函数本身中用到的变量、函数、资源不应被测试代码依赖,所谓 mock,就是想办法通过 “虚

    2024年02月02日
    浏览(39)
  • 通过Mock玩转Golang单元测试!

    如果项目中没有单元测试,对于刚刚开始或者说是规模还小的项目来说,效率可能还不错。但是一旦项目变得复杂起来,每次新增功能或对旧功能的改动都要重新手动测试一遍所有场景,费时费力,而且还有可能因为疏忽导致漏掉一些覆盖不到的点。在这个基础上,单元测试

    2024年02月05日
    浏览(39)
  • ChatGPT生成单元测试实践(Golang)

    目前gpt本质上是续写,所以在待测函数定义清晰的情况下,单元测试可以适当依赖它进行生成。 收益是什么: 辅助生成测试用例测试代码,降低单元测试编写的心智成本 辅助code review,帮助发现代码显式/潜在问题 本文测试环境: gpt: gpt-3.5-turbo go:go 1.17 本文实践场景:企业

    2023年04月20日
    浏览(45)
  • 【论文阅读】点云地图动态障碍物去除基准 A Dynamic Points Removal Benchmark in Point Cloud Maps

    终于一次轮到了讲自己的paper了 hahaha,写个中文的解读放在博客方便大家讨论 Title Picture Reference and prenotes paper: https://arxiv.org/abs/2307.07260 code: https://github.com/KTH-RPL/DynamicMap_Benchmark b站:地图动态障碍物去除总结 ITSC’23: A Dynamic Points Removal Benchmark in Point Cloud Maps 主要就是2019年末

    2024年02月06日
    浏览(39)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包