目录

与普通测试的区别

举例说明

指令与结果解读

性能比较

并行测试

 

与普通测试的区别

函数参数类型为*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

 

 

精彩文章

评论可见,请评论后查看内容,谢谢!!!评论后请刷新页面。