程式語言:Go
- Package:
- runtime/pprof
- net/http/pprof
官方文件:Profiling Go Programs
官方文件:net/http/pprof
官方文件:runtime/pprof
官方文件:runtime
官方文件:runtime/debug
功能:檢查性能瓶頸
主要檢測功能
- CPU Profiling
- CPU 分析,按照一定的頻率採集所監聽的應用程序 CPU(含寄存器)的使用情況
可確定應用程序在主動消耗 CPU 週期時花費時間的位置
- Memory Profiling
- Heap 分析,包含每个 goroutine 分配大小,分配堆栈等
- Block Profiling
- 阻塞分析,記錄 goroutine 阻塞等待同步(包括定時器通道)的位置
- Mutex Profiling
go test
- 透過 "go test -bench ." 生成相關訊息
go test -bench . -cpuprofile cpu.prof -memprofile mem.prof -blockprofile block.prof -mutexprofile mutex.prof
- 查詢指令用法
go test -h
- 使用內建 go tool 觀看數據
go tool pprof FileName
top # 顯示前幾名
web # 用瀏覽器觀看圖,需安裝 Graphviz
- 安裝原生 PProf 工具觀看數據,具有 Flame Graph
go get -u github.com/google/pprof # 安裝方法
pprof -http=:8080 FileName
runtime/pprof
net/http/pprof
- 透過 http 服務得到 Profile 採樣文檔,可使用在 server 上
- 查看數據
URI/debug/pprof/
- 使用內建 go tool 觀看數據
go tool pprof URI/debug/pprof/heap
go tool pprof URI/debug/pprof/profile?seconds=30
- 安裝原生 PProf 工具觀看數據,具有 Flame Graph
go get -u github.com/google/pprof # 安裝方法
pprof -http=:8080 URI/debug/pprof/heap
pprof -http=:8080 URI/debug/pprof/profile?seconds=1
測試程式
main.go
package main
import (
"log"
"net/http"
"os"
"runtime"
"runtime/pprof"
"sync"
"time"
)
var mutex sync.Mutex
func main() {
// 需設定,不然 block profile 不會有值
runtime.SetBlockProfileRate(1)
// 需設定,不然 mutex profile 不會有值
runtime.SetMutexProfileFraction(1)
startPProfCPU()
fabonacci(10000000)
stopPProfCPU()
mutex.Lock()
go test()
time.Sleep(2 * time.Millisecond)
mutex.Unlock()
pprofBlock()
pprofMutex()
pprofMem()
log.Println(http.ListenAndServe("localhost:6060", nil))
}
func fabonacci(index int) int {
if index <= 0 {
log.Fatal("index is less than 1")
}
pre0 := 0
pre1 := 1
for i := 1; i < index; i++ {
pre1, pre0 = pre0+pre1, pre1
}
return pre1
}
func test() {
go func() {
m := make(map[int]int)
for i := 0; i < 1000000; i++ {
m[i] = i
}
}()
mutex.Lock()
time.Sleep(1 * time.Second)
mutex.Unlock()
}
func startPProfCPU() {
cpuprofile := "cpu.out"
f, err := os.Create(cpuprofile)
if err != nil {
log.Fatal("could not create CPU profile: ", err)
}
if err := pprof.StartCPUProfile(f); err != nil {
log.Fatal("could not start CPU profile: ", err)
}
}
func stopPProfCPU() {
pprof.StopCPUProfile()
}
func pprofMem() {
memprofile := "mem.out"
f, err := os.Create(memprofile)
if err != nil {
log.Fatal("could not create memory profile: ", err)
}
defer f.Close()
runtime.GC() // get up-to-date statistics
if err := pprof.WriteHeapProfile(f); err != nil {
log.Fatal("could not write memory profile: ", err)
}
}
func pprofBlock() {
blockprofile := "block.out"
f, err := os.Create(blockprofile)
if err != nil {
log.Fatal("could not create block profile: ", err)
}
defer f.Close()
p := pprof.Lookup("block")
err = p.WriteTo(f, 0)
if err != nil {
log.Fatal("could not write write profile: ", err)
}
}
func pprofMutex() {
mutexprofile := "mutex.out"
f, err := os.Create(mutexprofile)
if err != nil {
log.Fatal("could not create block profile: ", err)
}
defer f.Close()
p := pprof.Lookup("mutex")
err = p.WriteTo(f, 0)
if err != nil {
log.Fatal("could not write write profile: ", err)
}
}
main_test.go
package main
import (
"testing"
"time"
)
func BenchmarkFabonacci(b *testing.B) {
for i := 0; i < b.N; i++ {
fabonacci(10)
mutex.Lock()
go test()
time.Sleep(2 * time.Millisecond)
mutex.Unlock()
}
}
main_http.go
不能與 main.go 同個資料夾
package main
import (
"log"
"net/http"
_ "net/http/pprof"
"sync"
)
var mutex sync.Mutex
func main() {
// 重覆運行,以便讓 http/pprof 抓取資料
go func() {
for {
fabonacci(10000000)
}
}()
log.Fatal(http.ListenAndServe("localhost:6060", nil))
}
func fabonacci(index int) int {
if index <= 0 {
log.Fatal("index is less than 1")
}
pre0 := 0
pre1 := 1
for i := 1; i < index; i++ {
pre1, pre0 = pre0+pre1, pre1
}
return pre1
}
其他有用工具
- race 檢測
go run -race main.go
- 用 panic 取代 return 觀察是否還有未關閉的 goroutine
參考
go pprof 性能分析
Golang 大杀器之性能剖析 PProf
golang 使用pprof和go-torch做性能分析
Go 语言运行时环境变量快速导览
留言
張貼留言