C語言中文網 目錄

go pprof命令(Go語言性能分析命令)完全攻略

< 上一頁go test命令 “避坑”與技巧下一頁 >

Go 語言工具鏈中的 go pprof 可以幫助開發者快速分析及定位各種性能問題,如 CPU 消耗、內存分配及阻塞分析。

性能分析首先需要使用 runtime.pprof 包嵌入到待分析程序的入口和結束處。runtime.pprof 包在運行時對程序進行每秒 100 次的采樣,最少采樣 1 秒。然后將生成的數據輸出,讓開發者寫入文件或者其他媒介上進行分析。

go pprof 工具鏈配合 Graphviz 圖形化工具可以將 runtime.pprof 包生成的數據轉換為 PDF 格式,以圖片的方式展示程序的性能分析結果。

安裝第三方圖形化顯式分析數據工具(Graphviz)

Graphviz 是一套通過文本描述的方法生成圖形的工具包。描述文本的語言叫做 DOT。

在 www.graphviz.org(http://www.graphviz.org)網站可以獲取到最新的 Graphviz 各平臺的安裝包。

CentOS 下,可以使用 yum 指令直接安裝:

$ yum install graphiviz

安裝第三方性能分析來分析代碼包

runtime.pprof 提供基礎的運行時分析的驅動,但是這套接口使用起來還不是太方便,例如:
  • 輸出數據使用 io.Writer 接口,雖然擴展性很強,但是對于實際使用不夠方便,不支持寫入文件。
  • 默認配置項較為復雜。

很多第三方的包在系統包 runtime.pprof 的技術上進行便利性封裝,讓整個測試過程更為方便。這里使用 github.com/pkg/profile 包進行例子展示,使用下面代碼安裝這個包:

$ go get github.com/pkg/profile

性能分析代碼

下面代碼故意制造了一個性能問題,同時使用 github.com/pkg/profile 包進行性能分析。
本套教程所有源碼下載地址:https://pan.baidu.com/s/1ORFVTOLEYYqDhRzeq0zIiQ    提取密碼:hfyf
基準測試代碼如下(具體文件:./src/chapter11/profile/cpu.go):
package main

import (
    "github.com/pkg/profile"
    "time"
)

func joinSlice() []string {

    var arr []string

    for i := 0; i < 100000; i++ {
     // 故意造成多次的切片添加(append)操作, 由于每次操作可能會有內存重新分配和移動, 性能較低
        arr = append(arr, "arr")
    }

    return arr
}

func main() {
    // 開始性能分析, 返回一個停止接口
    stopper := profile.Start(profile.CPUProfile, profile.ProfilePath("."))

    // 在main()結束時停止性能分析
    defer stopper.Stop()

    // 分析的核心邏輯
    joinSlice()

    // 讓程序至少運行1秒
    time.Sleep(time.Second)
}
代碼說明如下:
  • 第 4 行,引用 github.com/pkg/profile 第三方包封裝。
  • 第 14 行,為了進行性能分析,這里在已知元素大小的情況下,還是使用 append() 函數不斷地添加切片。性能較低,在實際中應該避免,這里為了性能分析,故意這樣寫。
  • 第 22 行,使用 profile.Start 調用 github.com/pkg/profile 包的開啟性能分析接口。這個 Start 函數的參數都是可選項,這里需要指定的分析項目是 profile.CPUProfile,也就是 CPU 耗用。profile.ProfilePath(".") 指定輸出的分析文件路徑,這里指定為當前文件夾。profile.Start() 函數會返回一個 Stop 接口,方便在程序結束時結束性能分析。
  • 第 25 行,使用 defer,將性能分析在 main() 函數結束時停止。
  • 第 28 行,開始執行分析的核心。
  • 第 31 行,為了保證性能分析數據的合理性,分析的最短時間是 1 秒,使用 time.Sleep() 在程序結束前等待 1 秒。如果你的程序默認可以運行 1 秒以上,這個等待可以去掉。

性能分析需要可執行配合才能生成分析結果,因此使用命令行對程序進行編譯,代碼如下:

$ go build -o cpu cpu.go
$ ./cpu
$ go tool pprof --pdf cpu cpu.pprof > cpu.pdf

代碼說明如下:
  • 第 1 行將 cpu.go 編譯為可執行文件 cpu。
  • 第 2 行運行可執行文件,在當前目錄輸出 cpu.pprof 文件。
  • 第 3 行,使用 go tool 工具鏈輸入 cpu.pprof 和 cpu 可執行文件,生成 PDF 格式的輸出文件,將輸出文件重定向為 cpu.pdf 文件。這個過程中會調用 Graphviz 工具,Windows 下需將 Graphviz 的可執行目錄添加到環境變量 PATH 中。

最終生成 cpu.pdf 文件,使用 PDF 查看器打開文件,觀察后發現下圖所示的某個地方可能存在瓶頸。


圖:性能分析

圖中的每一個框為一個函數調用的路徑,第 3 個方框中 joinSlice 函數耗費了 50% 的 CPU 時間,存在性能瓶頸。重新優化代碼,在已知切片元素數量的情況下直接分配內存,代碼如下:
func joinSlice() []string {

    const count = 100000

    var arr []string = make([]string, count)

    for i := 0; i < count; i++ {
        arr[i] = "arr"
    }

    return arr
}
代碼說明如下:
  • 第 5 行,將切片預分配 count 個數量,避免之前使用 append() 函數的多次分配。
  • 第 8 行,預分配后,直接對每個元素進行直接賦值。

重新運行上面的代碼進行性能分析,最終得到的 cpu.pdf 中將不會再有耗時部分。
< 上一頁go test命令 “避坑”與技巧下一頁 >

精美而實用的網站,提供C語言C++STLLinuxShellJavaGo語言等教程,以及socketGCCviSwing設計模式JSP等專題。

Copyright ?2011-2018 biancheng.net, 陜ICP備15000209號

底部Logo