golang 性能分析

性能分析

  • 從業務目標角度,通常我們的瓶頸出現在業務的通量和時延兩個問題上
    • 比如一個MySQL數據庫,你從其他機器上對它發請求,每秒它能處理10萬個請求,這個就是通量性能;每個請求的反應時間是0.5ms,這個就是時延性能
    • 通量和時延是相互相承的兩個量,當通量達到系統上限,時延就會大幅提高
  • 一般而言,性能分析主要關注 CPU、內存、磁盤 IO、網絡這些指標

pprof

三種採樣方式

runtime/pprof

  • 適用於程序只運行一次的,例如每天只跑一次的離線預處理程序

  • 採樣

    • 運行程序後生成cpu.profmem.prof兩個文件
import "runtime/pprof"

func main() {
    f, _ := os.Create("cpu.prof")
    pprof.StartCPUProfile(f)
	defer pprof.StopCPUProfile()
    
    ff, _ := os.Create("mem.prof")
    pprof.WriteHeapProfile(ff)
    
    // 待測試的程序
}

net/http/pprof

  • 適用於程序長期運行的線上服務
    • 底層也是調用的 runtime/pprof 提供的函數,封裝成接口對外提供網絡訪問
  • 如果是使用了HTTP包的路由,則只需要import _ "net/http/pprof"即可,會自動註冊路由
  • 如果使用了第三方路由(例如gin),則需要手動註冊路由
    • gin還可以直接使用github.com/DeanThompson/ginpprof
    • 只需要ginpprof.Wrap(router)即可
import _ "net/http/pprof"

// 如果不是使用默認的 http.DefaultServeMux而是自定義的MUX,則需要手動註冊路由
r.HandleFunc("/debug/pprof/", pprof.Index)
r.HandleFunc("/debug/pprof/cmdline", pprof.Cmdline)
r.HandleFunc("/debug/pprof/profile", pprof.Profile)
r.HandleFunc("/debug/pprof/symbol", pprof.Symbol)
r.HandleFunc("/debug/pprof/trace", pprof.Trace)

benchmark pprof

  • 命令行
    • 直接運行 go test -bench <bench_func> -run none -cpuprofile <cpu.prof>
    • 除了-cpuprofile, 還可-memprofile
  • Goland

在這裏插入圖片描述

報告查看

# 命令行交互模式
go tool pprof http://<address:port>/debug/pprof/<profile>
# cpu profile默認採樣30s,可以自行修改
go tool pprof http://<address:port>/debug/pprof/<profile>?seconds=120
# 在瀏覽器彙總交互
go tool pprof --http=:<port> http://<address:port>/debug/pprof/<profile> 
```
  • <profile>包括profile(CPU), heap(內存),goroutine,block,mutex

命令行

  • 總的思路就是通過topweb 找出關鍵函數,再通過list Func 查看函數代碼,找到關鍵代碼行並確認優化方案

  • top <num>查看佔用最高的num項

[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-hMEpZJ6A-1582979745691)(C:\Users\35135\AppData\Roaming\Typora\typora-user-images\1582978820785.png)]

img

  • list <func name>查看匹配的具體函數CPU和內存等佔用數據

    • 支持正則匹配
    • 如果函數名不明確,會進行模糊匹配
    • 使用list命令的前提是程序的源碼在當前機器

    [外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-nCJzWFvK-1582979745701)(C:\Users\35135\AppData\Roaming\Typora\typora-user-images\1582979123139.png)]

  • traces打印所有調用棧,以及調用棧的指標信息

    • 每個- - - - - 隔開的是一個調用棧

[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-8HQhc6Bo-1582979788042)(C:\Users\35135\AppData\Roaming\Typora\typora-user-images\1582979362653.png)]

瀏覽器

  • 需要先安裝 graphviz 工具
    在這裏插入圖片描述

指標

CPU

  • CPU 性能分析啓用後,Go runtime 會每 10ms 就暫停一下,記錄當前運行的 goroutine 的調用堆棧及相關數據

在這裏插入圖片描述

內存

  • 內存性能分析則是在一段時間內對在堆分配進行採樣

    • 棧分配由於會隨時釋放,因此不會被內存分析所記錄
    • 內存profiling是基於抽樣和它跟蹤的是已分配的內存,而不是使用中的內存
      • 比如有些內存已經分配,但實際以及不使用的內存,比如內存泄露的那部分
      • 所以不能使用內存profiling衡量程序總體的內存使用情況
        在這裏插入圖片描述
  • 有兩種內存分析策略:

    • 一種是當前的(這一次採集)內存或對象的分配,稱爲 inuse
    • 另一種是從程序運行到現在所有的內存分配,不管是否已經被 gc 過了,稱爲 alloc
    • 加上 -sample_index 參數後,可以切換內存分析的類型
    • 例如 go tool pprof -sample_index=alloc_space http://<address:port>/debug/pprof/heap
-inuse_space      未釋放空間數
-inuse_objects    未釋放對象數
-alloc_space      累計分配空間數
-alloc_objects    累計分配對象數

goroutine

goroutine profile: total 32023
32015 @ 0x42e15a 0x42e20e 0x40534b 0x4050e5 0x6d8559 0x6d831b 0x45abe1
#    0x6d8558    main.alloc2.func1+0xf8    /home/ubuntu/heap/leak_demo.go:53
#    0x6d831a    main.alloc2+0x2a    /home/ubuntu/heap/leak_demo.go:54
  • goroutine?debug=1
    • goroutine profile: total 32023
      • 32023是goroutine的總數量
    • 32015 @ 0x42e15a 0x42e20e 0x40534b 0x4050e5 ...
      • 32015代表當前有32015個goroutine運行這個調用棧,並且停在相同位置
    • # 0x6d8558 main.alloc2.func1+0xf8 /home/ubuntu/heap/leak_demo.go:53
      • goroutine的調用棧,列出了函數和所在文件的行數
goroutine 20 [chan send, 2 minutes]:
main.alloc2.func1(0xc42015e060)
    /home/ubuntu/heap/leak_demo.go:53 +0xf9 
main.alloc2(0xc42015e060)
    /home/ubuntu/heap/leak_demo.go:54 +0x2b
created by main.alloc1
    /home/ubuntu/heap/leak_demo.go:42 +0x3f
  • goroutine?debug=2
    • 和第1種方式是互補的,可以看到每個goroutine的信息
    • goroutine 20 [chan send, 2 minutes]
      • 20是goroutine的id,
      • []中是當前goroutine的狀態: 阻塞在寫channel,並且阻塞了2分鐘

火焰圖

  • y軸表示調用方法的先後
  • x軸表示在每個採樣調用時間內,方法所佔的時間/空間百分比
    • 越寬代表佔據cpu時間/mem大小越多

參考

trace

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章