Go语言测试第二弹——基准测试
在前一篇文章中,我们讲解了Go语言中最基础的单元测试,还没有看过的可以自行去查看,这篇文章我们详细了解Go语言里面的基准测试。
基准测试
基准测试,也就是BenchmarkTest,基准测试是用来测试代码性能的的一种方法,使用基准测试时,测试函数必须以Benchmark开头,后面跟具体需要测试的函数的名称,基本格式如下:
// Add函数对应的基准测试函数 func BenchmarkAdd(b *testing.B){ }
基准测试函数中的参数是b *testing.B,基准测试时函数必须要执行b.N次,只有这样测试才能具有参考性和一定的准确性。b.N这个值并不是固定的,而是根据情况变化,从1开始,如果当前测试函数能够在1秒内执行完毕,则会将b.N的值增加到2,如果测试函数同样在1秒内执行完毕,则会继续增加b.N的值,b.N的递增序列为1,2,3,5,10,20,30,50,100。
基本使用
现在我们先写一个计算斐波拉契数列的函数:
// Fib 递归函数计算斐波拉契数列的第 x 个数 func Fib(x int) int { if x return x } return Fib(x-1) + Fib(x-2) } for i := 0; i go test -bench=Fib goos: windows goarch: amd64 pkg: test/calc cpu: Intel(R) Core(TM) i7-9700 CPU @ 3.00GHz BenchmarkFib-8 4861580 235.1 ns/op PASS ok test/calc 1.567s
上面的输出中,上面都是一些系统相关信息,下面BenchmarkFib-8后面的数字表示的是GOMAXPROCS,默认等于CPU的核数,后面的4861580和235.1 ns/op表示当前测试用例执行的4861580次,平均花费时间为235.1 ns,总耗时为1.567s。
在使用的时候还可以通过其他参数来获取更多的测试数据,用来提升测试的准确率和可参考性。
使用-benchmem参数来获取内存分配的数据。
PS C:\Users\lee\GolandProjects\test\calc> go test -bench=Fib -benchmem goos: windows goarch: amd64 pkg: test/calc cpu: Intel(R) Core(TM) i7-9700 CPU @ 3.00GHz BenchmarkFib-8 4749666 251.0 ns/op 0 B/op 0 allocs/op PASS ok test/calc 1.609s
在耗时的后面新增了0 B/op和0 allocs/op,其中0 B/op表示每次运行测试分配了0B的内存,0 allocs/op表示每次运行测试进行了0次的内存分配,因为我们的功能函数没用到额外的内存,所以这两个值都是0,大家可以自己尝试其他方法来看看这个值的变化。
使用-benchtime参数指定测试的基准时间或次数,在前面我们说过测试用例会执行b.N次,这个数量是动态变化的,只要运行测试用例的时间没有超过1秒就会递增这个值。现在可以使用-benchtime参数指定这个基准时间,如果修改为5,可以使用-benchtime=5s,则表示执行时间不超过5秒,就会递增b.N。
PS C:\Users\lee\GolandProjects\test\calc> go test -bench=Fib -benchtime=5s goos: windows goarch: amd64 pkg: test/calc cpu: Intel(R) Core(TM) i7-9700 CPU @ 3.00GHz BenchmarkFib-8 23184202 235.6 ns/op PASS ok test/calc 5.859s
通过上面的输出可以看到,一共运行了23184202次,平均每次耗时235.6 ns,总耗时5.859s。因为通过-benchtime指定了基准时间为5秒,所以总运行次数大概是之前的5倍。
-benchtime除了指定时间之外,还可以用来指定具体的次数,假设指定执行100次,可以使用-benchtime=100x
PS C:\Users\lee\GolandProjects\test\calc> go test -bench=Fib -benchtime=100x goos: windows goarch: amd64 pkg: test/calc cpu: Intel(R) Core(TM) i7-9700 CPU @ 3.00GHz BenchmarkFib-8 100 487.0 ns/op PASS ok test/calc 0.167s
上面可以看到通过-benchtime指定次数后,一共调用了Fib函数100次,总耗时0.167s。
-count参数可以用来指定测试的轮数,比如指定执行3轮。
PS C:\Users\lee\GolandProjects\test\calc> go test -bench=Fib -count=3 goos: windows goarch: amd64 pkg: test/calc cpu: Intel(R) Core(TM) i7-9700 CPU @ 3.00GHz BenchmarkFib-8 4641591 261.0 ns/op BenchmarkFib-8 5031808 238.0 ns/op BenchmarkFib-8 5103565 245.1 ns/op
性能比较
在基准测试中,某些功能函数在不同的输入时,相对应的性能也会有所差别,在前面的所有测试中,我们传递的参数都是10,也就是每次都计算的是斐波拉契数列的第10个,如果我们需要测试一个函数在不同的输入下的性能差异,或者是测试两个函数在相同输入下的性能差异,就会使用到性能比较的测试方法,在比较性能的时候就需要使用到一个带参数的测试函数,再使用其他的Benchmark函数传入不同的值来调用,用以测试不同输入的性能差别。
我们将上面的测试代码修改如下:
func benchmarkFib(b *testing.B, n int) { for i := 0; i在代码中写了一个带有参数的辅助函数benchmarkFib,可以传入参数,并且构造了4个测试用例,分别传入不同的参数,执行上面的测试用例。
PS C:\Users\lee\GolandProjects\test\calc> go test -bench=Fib goos: windows goarch: amd64 pkg: test/calc cpu: Intel(R) Core(TM) i7-9700 CPU @ 3.00GHz BenchmarkFib2-8 304879520 3.793 ns/op BenchmarkFib10-8 5249786 232.8 ns/op BenchmarkFib20-8 41760 28409 ns/op BenchmarkFib30-8 345 3571588 ns/op PASS ok test/calc 6.245s可以看到,在不同的输入下,函数的执行次数和平均执行时间都有不小的差距,当计算第30个数的时候一共才执行了345次,而每次的平均执行时间为3571588 ns。
重置时间
在进行基准测试的时候,通常都是用来测试函数的性能,我们上面的函数都是简单的一些示例,但是在日常的工作中,我们测试之前可能需要有一些准备工作,例如测试前读取文件,这样的话在测试的时候就会将读取文件的耗时也计算到测试报告里面去,这个时候我们可以使用到ResetTimer来重置时间,代码如下:
func BenchmarkFib(b *testing.B) { // sleep 3秒,模拟测试前的准备工作 time.Sleep(time.Second * 3) // 重置定时器 b.ResetTimer() for i := 0; i在代码中调用了b.ResetTimer()方法,这样就表示重置定时器,意味着这行代码之前的所有代码的耗时都不会被计算到测试中,这样能够确保代码测试的准确性。