基准测试主要是通过测试CPU和内存的效率问题,来评估被测试代码的性能,进而找到更好的解决方案。
func BenchmarkSprintf(b *testing.B){
num:=10
b.ResetTimer()
for i:=0;i<b.N;i++{
fmt.Sprintf("%d",num)
}
}
➜ go test -bench=. -run=none
BenchmarkSprintf-8 20000000 117 ns/op
PASS
ok flysnow.org/hello 2.474s
使用 go test
命令,加上 -bench=
标记,接受一个表达式作为参数, .
表示运行所有的基准测试
因为默认情况下 go test
会运行单元测试,为了防止单元测试的输出影响我们查看基准测试的结果,可以使用-run=
匹配一个从来没有的单元测试方法,过滤掉单元测试的输出,我们这里使用none
,因为我们基本上不会创建这个名字的单元测试方法。
也可以使用 -run=^$
, 匹配这个规则的,但是没有,所以只会运行benchmark
go test -bench=. -run=^$
有些时候在benchmark之前需要做一些准备工作,并且,我们不希望这些准备工作纳入到计时里面,我们可以使用 b.ResetTimer(),代表重置计时为0,以调用时的时刻作为重新计时的开始。
看到函数后面的-8
了吗?这个表示运行时对应的GOMAXPROCS的值。
接着的20000000
表示运行for循环的次数也就是调用被测试代码的次数
最后的117 ns/op
表示每次需要话费117纳秒。(执行一次操作话费的时间)
以上是测试时间默认是1秒,也就是1秒的时间,调用两千万次,每次调用花费117纳秒。
如果想让测试运行的时间更长,可以通过-benchtime指定,比如3秒。
➜ hello go test -bench=. -benchtime=3s -run=none
// Benchmark 名字 - CPU 循环次数 平均每次执行时间
BenchmarkSprintf-8 50000000 109 ns/op
PASS
// 哪个目录下执行go test 累计耗时
ok flysnow.org/hello 5.628s
可以发现,我们加长了测试时间,测试的次数变多了,但是最终的性能结果:每次执行的时间,并没有太大变化。一般来说这个值最好不要超过3秒,意义不大。
上面那个基准测试的例子,其实是一个int类型转为string类型的例子,标准库里还有几种方法,我们看下哪种性能更加.
func BenchmarkSprintf(b *testing.B){
num:=10
b.ResetTimer()
for i:=0;i<b.N;i++{
fmt.Sprintf("%d",num)
}
}
func BenchmarkFormat(b *testing.B){
num:=int64(10)
b.ResetTimer()
for i:=0;i<b.N;i++{
strconv.FormatInt(num,10)
}
}
func BenchmarkItoa(b *testing.B){
num:=10
b.ResetTimer()
for i:=0;i<b.N;i++{
strconv.Itoa(num)
}
}
➜ hello go test -bench=. -run=none
BenchmarkSprintf-8 20000000 117 ns/op
BenchmarkFormat-8 50000000 33.3 ns/op
BenchmarkItoa-8 50000000 34.9 ns/op
PASS
ok flysnow.org/hello 5.951s
从结果上看strconv.FormatInt
函数是最快的,其次是strconv.Itoa
,然后是fmt.Sprintf
最慢,前两个函数性能达到了最后一个的3倍多。那么最后一个为什么这么慢的,我们再通过-benchmem
找到根本原因。
➜ hello go test -bench=. -benchmem -run=none
BenchmarkSprintf-8 20000000 110 ns/op 16 B/op 2 allocs/op
BenchmarkFormat-8 50000000 31.0 ns/op 2 B/op 1 allocs/op
BenchmarkItoa-8 50000000 33.1 ns/op 2 B/op 1 allocs/op
PASS
ok flysnow.org/hello 5.610s
-benchmem
可以提供每次操作分配内存的次数,以及每次操作分配的字节数。从结果我们可以看到,性能高的两个函数,每次操作都是进行1次内存分配,而最慢的那个要分配2次;性能高的每次操作分配2个字节内存,而慢的那个函数每次需要分配16字节的内存。从这个数据我们就知道它为什么这么慢了,内存分配都占用都太高。
在代码开发中,对于我们要求性能的地方,编写基准测试非常重要,这有助于我们开发出性能更好的代码。不过性能、可用性、复用性等也要有一个相对的取舍,不能为了追求性能而过度优化。
pprof 性能监控
package bench
import "testing"
func Fib(n int) int {
if n < 2 {
return n
}
return Fib(n-1) + Fib(n-2)
}
func BenchmarkFib10(b *testing.B) {
// run the Fib function b.N times
for n := 0; n < b.N; n++ {
Fib(10)
}
}
go test -bench=. -benchmem -cpuprofile profile.out
还可以同时看内存
go test -bench=. -benchmem -memprofile memprofile.out -cpuprofile profile.out
然后就可以用输出的文件使用pprof
go tool pprof profile.out
File: bench.test
Type: cpu
Time: Apr 5, 2018 at 4:27pm (EDT)
Duration: 2s, Total samples = 1.85s (92.40%)
Entering interactive mode (type "help" for commands, "o" for options)
(pprof) top
Showing nodes accounting for 1.85s, 100% of 1.85s total
flat flat% sum% cum cum%
1.85s 100% 100% 1.85s 100% bench.Fib
0 0% 100% 1.85s 100% bench.BenchmarkFib10
0 0% 100% 1.85s 100% testing.(*B).launch
0 0% 100% 1.85s 100% testing.(*B).runN
这个是使用cpu 文件, 也可以使用内存文件
然后你也可以用list命令检查函数需要的时间
(pprof) list Fib
1.84s 2.75s (flat, cum) 148.65% of Total
. . 1:package bench
. . 2:
. . 3:import "testing"
. . 4:
530ms 530ms 5:func Fib(n int) int {
260ms 260ms 6: if n < 2 {
130ms 130ms 7: return n
. . 8: }
920ms 1.83s 9: return Fib(n-1) + Fib(n-2)
. . 10:}
或者使用web命令生成图像(png,pdf,...)
报错:Failed to execute dot. Is Graphviz installed? Error: exec: "dot": executable file not found in %PATH%
是你电脑没有安装gvedit导致的
fq进入gvedit官网https://graphviz.gitlab.io/_pages/Download/Download_windows.html 下载稳定版
mac 安装, 安装好后就可以使用web进行展现了
brew install graphviz
go 测试后面可以跟哪些参数
Testing flags
常用flag
Go 1.7中开始支持 sub-test的概念。
golang性能测试与调优
Go语言实战笔记(二十一)| Go 单元测试
Go语言实战笔记(二十二)| Go 基准测试
Profile your golang benchmark with pprof
深入Go语言 - 12
Go单元测试&性能测试
golang性能测试与调优
PS: 觉得不错的请点个赞吧!! (ง •̀_•́)ง
米鼠网自成立以来一直专注于从事软件项目、人才招聘、软件商城等,始终秉承“专业的服务,易用的产品”的经营理念,以“提供高品质的服务、满足客户的需求、携手共创双赢”为企业目标,为中国境内企业提供国际化、专业化、个性化、的软件项目解决方案,我司拥有一流的项目经理团队,具备过硬的软件项目设计和实施能力,为全国不同行业客户提供优质的产品和服务,得到了客户的广泛赞誉。
评论留言