Golang 性能測試 (2) 性能分析
本文介紹 golang 如何做性能分析。
對服務做了基準性能測試後,如果服務出現問題,可以通過性能分析工具,查出消耗資源的瓶頸,並做針對性的性能優化。
Golang 語言也爲我們提供了方便的性能分析工具pprof,方便我們做必要的服務優化。pprof 可以做cpu分析,統計所有調用方法執行的時間片(通過採樣); 可以查看內存分配,找到是否有內存泄漏,哪裏泄露了(調用棧);還可以查看Block、事件調用,互斥鎖等。可謂麻雀雖小,五臟俱全。Golang 提供了兩種分析的工具,一種是web工具,直接引入即可;另一種是命令行交互工具,需要抓取prof 數據,再做詳細分析。
WEB 工具
golang 性能分析工具主要有幾種,最常用的是使用web 界面的工具。我們舉個簡單的例子,將一個map數據做編碼,編碼100w次,例子如下:
package main import "encoding/json" import _ "net/http/pprof" import "net/http" func main() { mapData := mapData := map[string]string{ "abcdefg1": "aaaaaaaaaaaaaaaaaaaa", "abcdefg2": "aaaaaaaaaaaaaaaaaaaa", "abcdefg3": "aaaaaaaaaaaaaaaaaaaa", "abcdefg4": "aaaaaaaaaaaaaaaaaaaa", "abcdefg5": "aaaaaaaaaaaaaaaaaaaa", "abcdefg6": "aaaaaaaaaaaaaaaaaaaa", "abcdefg7": "aaaaaaaaaaaaaaaaaaaa", "abcdefg8": "aaaaaaaaaaaaaaaaaaaa", "abcdefg9": "aaaaaaaaaaaaaaaaaaaa", "abcdefg10": "aaaaaaaaaaaaaaaaaaaa", } go func() { for i := 0; i < 100000000; i++ { _, _ = json.Marshal(data) } }() http.ListenAndServe("0.0.0.0:8080", nil) }
引入 "net/http/pprof" 包,將自動在默認的http中添加相關 pprof 的處理方法(當然也可以自己添加了)。
我們通過訪問 /debug/pprof/ 就可以打開對應的web 界面。
- allocs 過去所有內存分配的採樣。
- block 查看阻塞同步的堆棧
- cmdline 當前進程的命令行
- goroutine 所有協程的調用棧
- heap 當前活動對象的內存分配
- mutex 競態互斥鎖的調用棧
- profile 獲取一個30s(可以通過seconds 參數指定)的cpu 採樣prof 文件 (可以用 go tool pprof 分析)
- threadcreate 導致創建了新系統線程的調用棧
- trace 抓一個當前執行的trace包,可以捕獲各種事件(可以用go tool trace 做可視化分析)
命令行交互
命令行工具,需要先抓取一段採樣數據,採樣數據可以通過web 的 profile 鏈接直接下載,也可以不啓動web服務,直接採樣。直接採樣的好處是,可以直接採樣我們需要優化的代碼段的數據,而web採樣的數據不一定會抓到我們執行的代碼段(畢竟是通過採樣實現的)。下面我們寫一個直接採樣的例子:
package main import "encoding/json" import "runtime/pprof" import "os" import "log" func main() { cpuprofile := "json_map.prof" mapData := map[string]string{ "abcdefg1": "aaaaaaaaaaaaaaaaaaaa", "abcdefg2": "aaaaaaaaaaaaaaaaaaaa", "abcdefg3": "aaaaaaaaaaaaaaaaaaaa", "abcdefg4": "aaaaaaaaaaaaaaaaaaaa", "abcdefg5": "aaaaaaaaaaaaaaaaaaaa", "abcdefg6": "aaaaaaaaaaaaaaaaaaaa", "abcdefg7": "aaaaaaaaaaaaaaaaaaaa", "abcdefg8": "aaaaaaaaaaaaaaaaaaaa", "abcdefg9": "aaaaaaaaaaaaaaaaaaaa", "abcdefg10": "aaaaaaaaaaaaaaaaaaaa", } if cpuprofile != "" { f, err := os.Create(cpuprofile) if err != nil { log.Fatal(err) } pprof.StartCPUProfile(f) defer pprof.StopCPUProfile() } for i := 0; i < 1000000; i++ { _, _ = json.Marshal(mapData) } }
然後我們通過如下命令進入交互模式:
[root@localhost pprof]# go tool pprof json_map.prof File: json_map_1 Type: cpu Time: Apr 11, 2020 at 6:49pm (CST) Duration: 7.38s, Total samples = 7.12s (96.46%) Entering interactive mode (type "help" for commands, "o" for options) (pprof)
交互模式,也提供了豐富的命令查看prof文件中的數據,例如如下使用top10 查看代碼執行cpu佔比top10 的方法。
(pprof) top10 Showing nodes accounting for 3470ms, 48.74% of 7120ms total Dropped 78 nodes (cum <= 35.60ms) Showing top 10 nodes out of 87 flat flat% sum% cum cum% 570ms 8.01% 8.01% 1100ms 15.45% encoding/json.(*encodeState).string 550ms 7.72% 15.73% 1850ms 25.98% runtime.mallocgc 460ms 6.46% 22.19% 460ms 6.46% runtime.memmove 410ms 5.76% 27.95% 540ms 7.58% runtime.mapaccess2 320ms 4.49% 32.44% 350ms 4.92% runtime.heapBitsSetType 290ms 4.07% 36.52% 970ms 13.62% runtime.typedmemmove 230ms 3.23% 39.75% 230ms 3.23% runtime.nextFreeFast 220ms 3.09% 42.84% 220ms 3.09% runtime.memclrNoHeapPointers 210ms 2.95% 45.79% 210ms 2.95% cmpbody 210ms 2.95% 48.74% 6720ms 94.38% encoding/json.mapEncoder.encode
還有其他功能,例如繪製調用圖,內存分配圖等,可以通過help查看:
除此之外, go tool profile
還有另外的打開模式。例如,通過web服務查看prof 文件。
執行如下命令,通過web服務查看prof文件:
[root@localhost pprof]# go tool pprof -http=:8080 json_map.prof
可以查看進程調用圖,看到各調用函數的執行事件。
可以查看火焰圖,具體分析哪些方法有優化空間。
- 還可以查看Peek (調用者與被調用者匹配關係)
- 可以從源碼角度查看執行時間佔比。
- 也可以通過反彙編的代碼角度查看執行時間佔比。
除此之外,還可以命令行方式直接抓取web工具中的profile 數據做分析。(實際看來和自己抓取沒什麼區別,只是方便了而已)
其他
golang 目前提供的性能分析工具已經比較齊全了。本文只是對目前已經使用的功能做簡單總結,其他功能還待我們一起去探索。
本文使用的go版本爲1.13
下一篇將對 go tool 的另一神器 go tool trace
做簡單總結。
歡迎關注我們的微信公衆號,每天學習Go知識