如何编写压力测试 压力测试用来检测函数(方法)的性能,和编写单元功能测试的方法类似,此处不再赘述,但需要注意以下几点:
压力测试用例必须遵循如下格式,其中XXX可以是任意字母数字的组合,但是首字母不能是小写字母
go test不会默认执行压力测试的函数,如果要执行压力测试需要带上参数-bench,语法: -bench="test_name_regex",例如go test -bench=".*"表示测试全部的压力测试函数
在压力测试用例中,请记得在循环体内使用testing.B.N,以使测试可以正常的运行
文件名也必须以_test.go结尾
下面我们新建一个压力测试文件webbench_test.go,代码如下所示:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 package mainimport ( "testing" ) func Benchmark_Division (b *testing.B) { for i := 0 ; i < b.N; i++ { Division(4 , 5 ) } } func Benchmark_TimeConsumingFunction (b *testing.B) { b.StopTimer() b.StartTimer() for i := 0 ; i < b.N; i++ { Division(4 , 5 ) } }
我们执行命令go test -test.bench=".*" ,可以看到如下结果:
1 2 3 4 PASS Benchmark_Division 500000000 7.76 ns/op Benchmark_TimeConsumingFunction 500000000 7.80 ns/op ok gotest 9.364 s
上面的结果显示我们没有执行任何TestXXX的单元测试函数,显示的结果只执行了压力测试函数,第一条显示了Benchmark_Division执行了500000000次,每次的执行平均时间是7.76纳秒,第二条显示了Benchmark_TimeConsumingFunction执行了500000000,每次的平均执行时间是7.80纳秒。最后一条显示总共的执行时间。
我们执行命令go test -bench=".*" -count=5,可以看到如下结果: (使用-count可以指定执行多少次)
1 2 3 4 5 6 7 8 9 10 11 12 PASS Benchmark_Division-2 300000000 4.60 ns/op Benchmark_Division-2 300000000 4.57 ns/op Benchmark_Division-2 300000000 4.63 ns/op Benchmark_Division-2 300000000 4.60 ns/op Benchmark_Division-2 300000000 4.63 ns/op Benchmark_TimeConsumingFunction-2 300000000 4.64 ns/op Benchmark_TimeConsumingFunction-2 300000000 4.61 ns/op Benchmark_TimeConsumingFunction-2 300000000 4.60 ns/op Benchmark_TimeConsumingFunction-2 300000000 4.59 ns/op Benchmark_TimeConsumingFunction-2 300000000 4.60 ns/op ok _/home/diego/GoWork/src/app/testing 18.546 s
go test -run=文件名字 -bench=bench名字 -cpuprofile=生产的cprofile文件名称 文件夹
例子:
testBenchMark下有个popcnt文件夹,popcnt中有文件popcunt_test.go
1 ➜ testBenchMark ls popcnt
popcunt_test.go的问价内容:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 package mainimport ( "testing" ) const m1 = 0x5555555555555555 const m2 = 0x3333333333333333 const m4 = 0x0f0f0f0f0f0f0f0f const h01 = 0x0101010101010101 func popcnt (x uint64 ) uint64 { x -= (x >> 1 ) & m1 x = (x & m2) + ((x >> 2 ) & m2) x = (x + (x >> 4 )) & m4 return (x * h01) >> 56 } func BenchmarkPopcnt (b *testing.B) { for i := 0 ; i < b.N; i++ { x := i x -= (x >> 1 ) & m1 x = (x & m2) + ((x >> 2 ) & m2) x = (x + (x >> 4 )) & m4 _ = (x * h01) >> 56 } }
然后运行go test -bench=".*" -cpuprofile=cpu.profile ./popcnt
1 2 3 4 5 6 7 8 9 10 11 12 ➜ testBenchMark go test -bench=".*" -cpuprofile=cpu.profile ./popcnt testing: warning: no tests to run PASS BenchmarkPopcnt-8 1000000000 2.01 ns/op ok app/testBenchMark/popcnt 2.219 s ➜ testBenchMark ll total 6704 drwxr-xr-x 5 diego staff 170 5 6 13 :57 . drwxr-xr-x 3 diego staff 102 5 6 11 :12 .. -rw-r--r-- 1 diego staff 5200 5 6 13 :57 cpu.profile drwxr-xr-x 4 diego staff 136 5 6 11 :47 popcnt -rwxr-xr-x 1 diego staff 3424176 5 6 13 :57 popcnt.test ➜ testBenchMark
生产 cpu.profile问价和popcnt.test文件
1 2 3 4 5 6 7 8 ➜ testBenchMark ll total 6704 drwxr-xr-x 5 diego staff 170 5 6 13 :57 . drwxr-xr-x 3 diego staff 102 5 6 11 :12 .. -rw-r--r-- 1 diego staff 5200 5 6 13 :57 cpu.profile drwxr-xr-x 3 diego staff 102 5 6 14 :01 popcnt -rwxr-xr-x 1 diego staff 3424176 5 6 13 :57 popcnt.test ➜ testBenchMark
1 go tool pprof popcnt.test cpu.profile 进入交互模式
1 2 3 4 5 6 7 8 9 10 11 12 13 14 ➜ testBenchMark go tool pprof popcnt.test cpu.profile Entering interactive mode (type "help" for commands) (pprof) top 1880 ms of 1880 ms total ( 100 %) flat flat% sum% cum cum% 1790 ms 95.21 % 95.21 % 1790 ms 95.21 % app/testBenchMark/popcnt.BenchmarkPopcnt90 ms 4.79 % 100 % 90 ms 4.79 % runtime.usleep0 0 % 100 % 1790 ms 95.21 % runtime.goexit0 0 % 100 % 90 ms 4.79 % runtime.mstart0 0 % 100 % 90 ms 4.79 % runtime.mstart10 0 % 100 % 90 ms 4.79 % runtime.sysmon0 0 % 100 % 1790 ms 95.21 % testing.(*B).launch0 0 % 100 % 1790 ms 95.21 % testing.(*B).runN(pprof)
go tool pprof --web popcnt.test cpu.profile 进入web模式
1 $ go tool pprof --text mybin http://myserver:6060:/debug/pprof/profile
这有几个可用的输出类型,最有用的几个为: --text,--web 和 --list 。运行 go tool pprof 来得到最完整的列表。
下面分享一点go test的参数解读。来源
格式形如:
1 go test [-c] [-i] [build flags] [packages] [flags for test binary]
参数解读:
-c : 编译go test成为可执行的二进制文件,但是不运行测试。
-i : 安装测试包依赖的package,但是不运行测试。
关于build flags,调用go help build,这些是编译运行过程中需要使用到的参数,一般设置为空
关于packages,调用go help packages,这些是关于包的管理,一般设置为空
关于flags for test binary,调用go help testflag,这些是go test过程中经常使用到的参数
-test.v : 是否输出全部的单元测试用例(不管成功或者失败),默认没有加上,所以只输出失败的单元测试用例。
-test.run pattern: 只跑哪些单元测试用例
-test.bench patten: 只跑那些性能测试用例
-test.benchmem : 是否在性能测试的时候输出内存情况
-test.benchtime t : 性能测试运行的时间,默认是1s
-test.cpuprofile cpu.out : 是否输出cpu性能分析文件
-test.memprofile mem.out : 是否输出内存性能分析文件
-test.blockprofile block.out : 是否输出内部goroutine阻塞的性能分析文件
-test.memprofilerate n : 内存性能分析的时候有一个分配了多少的时候才打点记录的问题。这个参数就是设置打点的内存分配间隔,也就是profile中一个sample代表的内存大小。默认是设置为512 * 1024的。如果你将它设置为1,则每分配一个内存块就会在profile中有个打点,那么生成的profile的sample就会非常多。如果你设置为0,那就是不做打点了。
你可以通过设置memprofilerate=1和GOGC=off来关闭内存回收,并且对每个内存块的分配进行观察。
-test.blockprofilerate n: 基本同上,控制的是goroutine阻塞时候打点的纳秒数。默认不设置就相当于-test.blockprofilerate=1,每一纳秒都打点记录一下
-test.parallel n : 性能测试的程序并行cpu数,默认等于GOMAXPROCS。
-test.timeout t : 如果测试用例运行时间超过t,则抛出panic
-test.cpu 1,2,4 : 程序运行在哪些CPU上面,使用二进制的1所在位代表,和nginx的nginx_worker_cpu_affinity是一个道理
-test.short : 将那些运行时间较长的测试用例运行时间缩短
例子 1 2 3 4 // 压力测试 默认3s go test -bench=".*" -run didong_test.go -benchtime="3s" -count=5 -test.benchmem go test -bench="BenchmarkAdd" -run didong_test.go -benchtime="3s" -count=5 -benchmem