前言
寫了幾噸代碼,實(shí)現(xiàn)了幾百個(gè)接口。功能測(cè)試也通過(guò)了,終于成功的部署上線了
結(jié)果,性能不佳,什么鬼???
想做性能分析
PProf
想要進(jìn)行性能優(yōu)化,首先矚目在 Go 自身提供的工具鏈來(lái)作為分析依據(jù),本文將帶你學(xué)習(xí)、使用 Go 后花園,涉及如下:
- runtime/pprof:采集程序(非 Server)的運(yùn)行數(shù)據(jù)進(jìn)行分析
- net/http/pprof:采集 HTTP Server 的運(yùn)行時(shí)數(shù)據(jù)進(jìn)行分析
是什么
pprof 是用于可視化和分析性能分析數(shù)據(jù)的工具
pprof 以 profile.proto 讀取分析樣本的集合,并生成報(bào)告以可視化并幫助分析數(shù)據(jù)(支持文本和圖形報(bào)告)
profile.proto 是一個(gè) Protocol Buffer v3 的描述文件,它描述了一組 callstack 和 symbolization 信息, 作用是表示統(tǒng)計(jì)分析的一組采樣的調(diào)用棧,是很常見的 stacktrace 配置文件格式
支持什么使用模式
- Report generation:報(bào)告生成
- Interactive terminal use:交互式終端使用
- Web interface:Web 界面
可以做什么
- CPU Profiling:CPU 分析,按照一定的頻率采集所監(jiān)聽的應(yīng)用程序 CPU(含寄存器)的使用情況,可確定應(yīng)用程序在主動(dòng)消耗 CPU 周期時(shí)花費(fèi)時(shí)間的位置
- Memory Profiling:內(nèi)存分析,在應(yīng)用程序進(jìn)行堆分配時(shí)記錄堆棧跟蹤,用于監(jiān)視當(dāng)前和歷史內(nèi)存使用情況,以及檢查內(nèi)存泄漏
- Block Profiling:阻塞分析,記錄 goroutine 阻塞等待同步(包括定時(shí)器通道)的位置
- Mutex Profiling:互斥鎖分析,報(bào)告互斥鎖的競(jìng)爭(zhēng)情況
一個(gè)簡(jiǎn)單的例子
我們將編寫一個(gè)簡(jiǎn)單且有點(diǎn)問題的例子,用于基本的程序初步分析
編寫 demo 文件
(1)demo.go,文件內(nèi)容:
package main
import (
"log"
"net/http"
_ "net/http/pprof"
"github.com/EDDYCJY/go-pprof-example/data"
)
func main() {
go func() {
for {
log.Println(data.Add("https://github.com/EDDYCJY"))
}
}()
http.ListenAndServe("0.0.0.0:6060", nil)
}
(2)data/d.go,文件內(nèi)容:
package data
var datas []string
func Add(str string) string {
data := []byte(str)
sData := string(data)
datas = append(datas, sData)
return sData
}
運(yùn)行這個(gè)文件,你的 HTTP 服務(wù)會(huì)多出 /debug/pprof 的 endpoint 可用于觀察應(yīng)用程序的情況
分析
一、通過(guò) Web 界面
查看當(dāng)前總覽:訪問 http://127.0.0.1:6060/debug/pprof/
/debug/pprof/
profiles:
0 block
5 goroutine
3 heap
0 mutex
9 threadcreate
full goroutine stack dump
這個(gè)頁(yè)面中有許多子頁(yè)面,咱們繼續(xù)深究下去,看看可以得到什么?
- cpu(CPU Profiling):
$HOST/debug/pprof/profile
,默認(rèn)進(jìn)行 30s 的 CPU Profiling,得到一個(gè)分析用的 profile 文件 - block(Block Profiling):
$HOST/debug/pprof/block
,查看導(dǎo)致阻塞同步的堆棧跟蹤 - goroutine:
$HOST/debug/pprof/goroutine
,查看當(dāng)前所有運(yùn)行的 goroutines 堆棧跟蹤 - heap(Memory Profiling):
$HOST/debug/pprof/heap
,查看活動(dòng)對(duì)象的內(nèi)存分配情況 - mutex(Mutex Profiling):
$HOST/debug/pprof/mutex
,查看導(dǎo)致互斥鎖的競(jìng)爭(zhēng)持有者的堆棧跟蹤 - threadcreate:
$HOST/debug/pprof/threadcreate
,查看創(chuàng)建新OS線程的堆棧跟蹤
二、通過(guò)交互式終端使用
(1)go tool pprof http://localhost:6060/debug/pprof/profile?seconds=60
$ go tool pprof http://localhost:6060/debug/pprof/profile\?seconds\=60
Fetching profile over HTTP from http://localhost:6060/debug/pprof/profile?seconds=60
Saved profile in /Users/eddycjy/pprof/pprof.samples.cpu.007.pb.gz
Type: cpu
Duration: 1mins, Total samples = 26.55s (44.15%)
Entering interactive mode (type "help" for commands, "o" for options)
(pprof)
執(zhí)行該命令后,需等待 60 秒(可調(diào)整 seconds 的值),pprof 會(huì)進(jìn)行 CPU Profiling。結(jié)束后將默認(rèn)進(jìn)入 pprof 的交互式命令模式,可以對(duì)分析的結(jié)果進(jìn)行查看或?qū)С觥>唧w可執(zhí)行 pprof help
查看命令說(shuō)明
(pprof) top10
Showing nodes accounting for 25.92s, 97.63% of 26.55s total
Dropped 85 nodes (cum <= 0.13s)
Showing top 10 nodes out of 21
flat flat% sum% cum cum%
23.28s 87.68% 87.68% 23.29s 87.72% syscall.Syscall
0.77s 2.90% 90.58% 0.77s 2.90% runtime.memmove
0.58s 2.18% 92.77% 0.58s 2.18% runtime.freedefer
0.53s 2.00% 94.76% 1.42s 5.35% runtime.scanobject
0.36s 1.36% 96.12% 0.39s 1.47% runtime.heapBitsForObject
0.35s 1.32% 97.44% 0.45s 1.69% runtime.greyobject
0.02s 0.075% 97.51% 24.96s 94.01% main.main.func1
0.01s 0.038% 97.55% 23.91s 90.06% os.(*File).Write
0.01s 0.038% 97.59% 0.19s 0.72% runtime.mallocgc
0.01s 0.038% 97.63% 23.30s 87.76% syscall.Write
- flat:給定函數(shù)上運(yùn)行耗時(shí)
- flat%:同上的 CPU 運(yùn)行耗時(shí)總比例
- sum%:給定函數(shù)累積使用 CPU 總比例
- cum:當(dāng)前函數(shù)加上它之上的調(diào)用運(yùn)行總耗時(shí)
- cum%:同上的 CPU 運(yùn)行耗時(shí)總比例
最后一列為函數(shù)名稱,在大多數(shù)的情況下,我們可以通過(guò)這五列得出一個(gè)應(yīng)用程序的運(yùn)行情況,加以優(yōu)化 ??
(2)go tool pprof http://localhost:6060/debug/pprof/heap
$ go tool pprof http://localhost:6060/debug/pprof/heap
Fetching profile over HTTP from http://localhost:6060/debug/pprof/heap
Saved profile in /Users/eddycjy/pprof/pprof.alloc_objects.alloc_space.inuse_objects.inuse_space.008.pb.gz
Type: inuse_space
Entering interactive mode (type "help" for commands, "o" for options)
(pprof) top
Showing nodes accounting for 837.48MB, 100% of 837.48MB total
flat flat% sum% cum cum%
837.48MB 100% 100% 837.48MB 100% main.main.func1
-inuse_space:分析應(yīng)用程序的常駐內(nèi)存占用情況
-alloc_objects:分析應(yīng)用程序的內(nèi)存臨時(shí)分配情況
(3) go tool pprof http://localhost:6060/debug/pprof/block
(4) go tool pprof http://localhost:6060/debug/pprof/mutex
三、PProf 可視化界面
這是令人期待的一小節(jié)。在這之前,我們需要簡(jiǎn)單的編寫好測(cè)試用例來(lái)跑一下
編寫測(cè)試用例
(1)新建 data/d_test.go,文件內(nèi)容:
package data
import "testing"
const url = "https://github.com/EDDYCJY"
func TestAdd(t *testing.T) {
s := Add(url)
if s == "" {
t.Errorf("Test.Add error!")
}
}
func BenchmarkAdd(b *testing.B) {
for i := 0; i < b.N; i++ {
Add(url)
}
}
(2)執(zhí)行測(cè)試用例
$ go test -bench=. -cpuprofile=cpu.prof
pkg: github.com/EDDYCJY/go-pprof-example/data
BenchmarkAdd-4 10000000 187 ns/op
PASS
ok github.com/EDDYCJY/go-pprof-example/data 2.300s
-memprofile 也可以了解一下
啟動(dòng) PProf 可視化界面
方法一:
$ go tool pprof -http=:8080 cpu.prof
方法二:
$ go tool pprof cpu.prof
$ (pprof) web
如果出現(xiàn) Could not execute dot; may need to install graphviz.
,就是提示你要安裝 graphviz
了 (請(qǐng)右拐谷歌)
查看 PProf 可視化界面
(1)Top
(2)Graph
框越大,線越粗代表它占用的時(shí)間越大哦
(3)Peek
(4)Source
通過(guò) PProf 的可視化界面,我們能夠更方便、更直觀的看到 Go 應(yīng)用程序的調(diào)用鏈、使用情況等,并且在 View 菜單欄中,還支持如上多種方式的切換
你想想,在煩惱不知道什么問題的時(shí)候,能用這些輔助工具來(lái)檢測(cè)問題,是不是瞬間效率翻倍了呢 ??
四、PProf 火焰圖
另一種可視化數(shù)據(jù)的方法是火焰圖,需手動(dòng)安裝原生 PProf 工具:
(1) 安裝 PProf
$ go get -u github.com/google/pprof
(2) 啟動(dòng) PProf 可視化界面:
$ pprof -http=:8080 cpu.prof
(3) 查看 PProf 可視化界面
打開 PProf 的可視化界面時(shí),你會(huì)明顯發(fā)現(xiàn)比官方工具鏈的 PProf 精致一些,并且多了 Flame Graph(火焰圖)
它就是本次的目標(biāo)之一,它的最大優(yōu)點(diǎn)是動(dòng)態(tài)的。調(diào)用順序由上到下(A -> B -> C -> D),每一塊代表一個(gè)函數(shù),越大代表占用 CPU 的時(shí)間更長(zhǎng)。同時(shí)它也支持點(diǎn)擊塊深入進(jìn)行分析!
總結(jié)
在本章節(jié),粗略地介紹了 Go 的性能利器 PProf。在特定的場(chǎng)景中,PProf 給定位、剖析問題帶了極大的幫助
希望本文對(duì)你有所幫助,另外建議能夠自己實(shí)際操作一遍,最好是可以深入琢磨一下,內(nèi)含大量的用法、知識(shí)點(diǎn) ??
思考題
你很優(yōu)秀的看到了最后,那么有兩道簡(jiǎn)單的思考題,希望拓展你的思路
(1)flat 一定大于 cum 嗎,為什么?什么場(chǎng)景下 cum 會(huì)比 flat 大?
(2)本章節(jié)的 demo 代碼,有什么性能問題?怎么解決它?
來(lái),曬出你的想法!??