目录
与普通测试的区别
举例说明
指令与结果解读
性能比较
并行测试
函数参数类型为*testing.B
测试函数名称必须以Benchmark 开头
执行基准测试时,需要添加-bench参数
运行所有基准测试函数
go test –bench=.*
编写一个对于for循环的基准测试
func NewStringSlice(n int) []string {
rand.Seed(time.Now().UnixNano())
arr := make([]string, 0, n)
for i := 0; i < n; i++ {
arr = append(arr, strconv.Itoa(rand.Int()))
}
return arr
}
func BenchmarkStringSlice100(b *testing.B) {
for i := 0; i < b.N; i++ {
NewStringSlice(100)
}
}
func BenchmarkStringSlice1000(b *testing.B) {
for i := 0; i < b.N; i++ {
NewStringSlice(1000)
}
}
func BenchmarkStringSlice8000(b *testing.B) {
for i := 0; i < b.N; i++ {
NewStringSlice(8000)
}
}
func BenchmarkStringSlice9000(b *testing.B) {
for i := 0; i < b.N; i++ {
NewStringSlice(9000)
}
}
func BenchmarkStringSlice10000(b *testing.B) {
for i := 0; i < b.N; i++ {
NewStringSlice(10000)
}
}
运行所有BenchmarkStringSlice开头的基准测试函数:
go test -bench=^BenchmarkStringSlice -benchtime=5s .\testt\ -benchmem
结果如下:
BenchmarkStringSlice100-16 512133 12192 ns/op 4191 B/op 101 allocs/op
BenchmarkStringSlice1000-16 116264 49866 ns/op 40375 B/op 1001 allocs/op
BenchmarkStringSlice8000-16 17469 341199 ns/op 323003 B/op 8001 allocs/op
BenchmarkStringSlice9000-16 15597 383283 ns/op 363379 B/op 9001 allocs/op
BenchmarkStringSlice10000-16 14016 425329 ns/op 403754 B/op 10001 allocs/op
PASS
ok awesomeProject/testt 43.135s
BenchmarkStringSlice100-16结尾的数字16表示GOMAXPROCS的值
-bench=^BenchmarkStringSlice表示所执行的基准测试范围包括以IntSlice结尾的基准测试函数,如果是-bench=abc$则表示所有以abc结尾的基准测试函数
-benchtime=5s表示性能测试运行的时间,默认是1s(1秒是最小值),有时候为了产生更准确的结果,可以增加-benchtime参数,用来指定最小基准时间。
后面的.\testt\表示执行的是testt目录下的测试文件。
-benchmem参数可用来获取执行性能测试时的内存分配数据
【结果】
512133表示发生的调用次数
12192 ns/op表示512133次调用消耗的平均时间为12192纳秒
4191 B/op表示每次操作内存分配了4191字节
101 allocs/op表示每次操作进行的内存分配次数,为101次。
当算法不同时,我们仅需要以不同的输入来比较不同算法的性能差异,举个例子平时常见的比较有两种:
1、同一个函数,将不同的入参输入,比较不同参数的效果
2、不同的函数,将相同的入参输入,比较各自的效果
显然,做性能比较就是后者,而前者是对一个函数在不同条件下的性能测试。
作者编写了如下三种斐波那契数列的算法实现:
Fibonacci1
func Fibonacci1(num int) int {
if num < 1 {
return 0
}
if num == 1 || num == 2 {
return 1
}
return Fibonacci1(num-1) + Fibonacci1(num-2)
}
Fibonacci2
func Fibonacci2(num int) int {
if num < 1 {
return 0
}
backMap := make(map[int]int)
return fib(backMap, num)
}
func fib(backMap map[int]int, num int) int {
if num == 1 || num == 2 {
return 1
}
if backMap[num] != 0 {
return backMap[num]
}
backMap[num] = fib(backMap, num-1) + fib(backMap, num-2)
return backMap[num]
}
Fibonacci3
func Fibonacci3(num int) int {
if num < 1 {
return 0
}
if num == 1 || num == 2 {
return 1
}
left, right, res := 1, 1, 0
for i := 3; i <= num; i++ {
res = left + right
left, right = right, res
}
return res
}
如上都需要一个数值参数,获得一个数值,我们要做的就是计算在相同的入参情况下,三种算法的各自表现。
编写三种算法的基准测试基础函数
func benchmarkFib1(b *testing.B, num int) {
for i := 0; i < b.N; i++ {
Fibonacci1(num)
}
}
func benchmarkFib2(b *testing.B, num int) {
for i := 0; i < b.N; i++ {
Fibonacci2(num)
}
}
func benchmarkFib3(b *testing.B, num int) {
for i := 0; i < b.N; i++ {
Fibonacci3(num)
}
}
另外编写外层调用,以入参为10举例:
func BenchmarkFib10_1(b *testing.B) { benchmarkFib1(b, 10) }
func BenchmarkFib10_2(b *testing.B) { benchmarkFib2(b, 10) }
func BenchmarkFib10_3(b *testing.B) { benchmarkFib3(b, 10) }
想比较什么值,传不同的值即可。
第一波,验证下入参都为10时的各自效果:
go test -bench=^BenchmarkFib10 -benchtime=10s .\testt\ -benchmem
BenchmarkFib10_1-16 94737864 129.1 ns/op 0 B/op 0 allocs/op
BenchmarkFib10_2-16 75052676 163.5 ns/op 0 B/op 0 allocs/op
BenchmarkFib10_3-16 1000000000 1.272 ns/op 0 B/op 0 allocs/op
PASS
ok awesomeProject/testt 26.942s
就结果而言,算法3明显更优,能支持的执行次数更大,每次调用时间更短,百倍数量级差异。
接着,验证下入参都为50时的各自效果。由于传入数值越大消耗时间就越长,因此为得到更准确的结果,我们将-benchtime调大:
go test -bench=^BenchmarkFib50 -benchtime=50s .\testt\ -benchmem
BenchmarkFib50_1-16 1 70255751500 ns/op 2360 B/op 8 allocs/op
BenchmarkFib50_2-16 18577252 9417 ns/op 2401 B/op 9 allocs/op
BenchmarkFib50_3-16 706275607 81.14 ns/op 0 B/op 0 allocs/op
PASS
ok awesomeProject/testt 369.285s
当入参为50,在相同的时间内(50秒),算法1只被调用了1次,而算法2则是其千万级的数量级优化,算法3更甚。
如果你对第二列被调用了多少次不敏感,或者没什么概念的话,可以这么理解:
执行入参为50的斐波那契数列,在给定的相同时间内,算法1只能执行1次,换句话说它没有机会再执行第二次了;
算法2在相同条件下却可以执行18577252次,消耗160秒;算法3干相同的活在相同条件下可以干706275607次,一共只花了70秒。
即以并行的方式执行给定的基准测试。
b.RunParallel 会创建出多个goroutine,并将b.N分配给这些goroutine执行,其中goroutine数量的默认值为GOMAXPROCS。
如果想要增加非CPU受限(non-CPU-bound)基准测试的并行性, 那么可以在RunParallel之前调用 SetParallelism 。举例如下:
func BenchmarkFibParallel10_1(b *testing.B) {
b.SetParallelism(2)
b.RunParallel(func(pb *testing.PB) {
for pb.Next() {
Fibonacci1(10)
}
})
}
执行:
go test -bench=^BenchmarkFibParallel10 -benchtime=10s .\testt\ -benchmem -cpu=1
BenchmarkFibParallel10_1 74194387 310.1 ns/op 0 B/op 0 allocs/op
BenchmarkFibParallel10_2 71352631 165.9 ns/op 0 B/op 0 allocs/op
BenchmarkFibParallel10_3 1000000000 1.203 ns/op 0 B/op 0 allocs/op
PASS
ok awesomeProject/testt 37.271s