程式語言: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 语言运行时环境变量快速导览 
 
留言
張貼留言