基準測試主要是經過測試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: 以爲不錯的請點個贊吧!! (ง •̀_•́)ง