基准测试主要是经过测试CPU和内存的效率问题,来评估被测试代码的性能,进而找到更好的解决方案。html
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=
标记,接受一个表达式做为参数, .
表示运行全部的基准测试node
由于默认状况下 go test
会运行单元测试,为了防止单元测试的输出影响咱们查看基准测试的结果,可使用-run=
匹配一个历来没有的单元测试方法,过滤掉单元测试的输出,咱们这里使用none
,由于咱们基本上不会建立这个名字的单元测试方法。git
也可使用 -run=^$
, 匹配这个规则的,可是没有,因此只会运行benchmarkgithub
go test -bench=. -run=^$
有些时候在benchmark以前须要作一些准备工做,而且,咱们不但愿这些准备工做归入到计时里面,咱们可使用 b.ResetTimer(),表明重置计时为0,以调用时的时刻做为从新计时的开始。golang
看到函数后面的-8
了吗?这个表示运行时对应的GOMAXPROCS的值。web
接着的20000000
表示运行for循环的次数也就是调用被测试代码的次数windows
最后的117 ns/op
表示每次须要话费117纳秒。(执行一次操做话费的时间)浏览器
以上是测试时间默认是1秒,也就是1秒的时间,调用两千万次,每次调用花费117纳秒。框架
若是想让测试运行的时间更长,能够经过-benchtime指定,好比3秒。svg
➜ 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
火焰图(Flame Graph)是 Bredan Gregg 建立的一种性能分析图表,由于它的样子近似火焰而得名。
火焰图 svg 文件能够经过浏览器打开,它对于调用图的最优势是它是动态的:能够经过点击每一个方块来 zoom in 分析它上面的内容。
火焰图的调用顺序从下到上,每一个方块表明一个函数,它上面一层表示这个函数会调用哪些函数,方块的大小表明了占用 CPU 使用的长短。火焰图的配色并无特殊的意义,默认的红、黄配色是为了更像火焰而已。
runtime/pprof分析项目, 会在当前文件夹内导出profile文件。而后用火焰图去分析,就不能指定域名了,要指定文件。
网上介绍大部分使用uber的开源工具
go-torch。这是 uber 开源的一个工具,能够直接读取 golang profiling 数据,并生成一个火焰图的 svg 文件。
go-torch 工具的使用很是简单,没有任何参数的话,它会尝试从 http://localhost:8080/debug/pprof/profile 获取 profiling 数据。它有三个经常使用的参数能够调整:
从 Go 1.11 开始, 火焰图被集成进入 Go 官方的 pprof 库.
# This will listen on :8081 and open a browser. # Change :8081 to a port of your choice. $ go tool pprof -http=":8081" [binary] [profile]
若是低于1.11版本那么请从git pprof
# Get the pprof tool directly $ go get -u github.com/google/pprof $ pprof -http=":8081" [binary] [profile]
一个web 小例子
package main import ( "fmt" "log" "net/http" _ "net/http/pprof" "time" ) func sayHelloHandler(w http.ResponseWriter, r *http.Request) { hellowold(10000) fmt.Println("path", r.URL.Path) fmt.Println("scheme", r.URL.Scheme) fmt.Fprintf(w, "Hello world!\n") //这个写入到w的是输出到客户端的 } func main() { http.HandleFunc("/", sayHelloHandler) // 设置访问路由 log.Fatal(http.ListenAndServe(":8080", nil)) } func hellowold(times int) { time.Sleep(time.Second) var counter int for i := 0; i < times; i++ { for j := 0; j < times; j++ { counter++ } } }
使用下面的命令开启监控,而后访问几回localhost:8080
go tool pprof -http=":8081" http://localhost:8080/debug/pprof/profile
过一下子会产生个web窗口, 选择 VIEW->Flame Graph 获得火焰图形
http://localhost:8081/ui/flamegraph
go 测试后面能够跟哪些参数
经常使用flag
Go 1.7中开始支持 sub-test的概念。
Profile your golang benchmark with pprof
PS: 以为不错的请点个赞吧!! (ง •̀_•́)ง