<ruby id="bdb3f"></ruby>

    <p id="bdb3f"><cite id="bdb3f"></cite></p>

      <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
        <p id="bdb3f"><cite id="bdb3f"></cite></p>

          <pre id="bdb3f"></pre>
          <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

          <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
          <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

          <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                <ruby id="bdb3f"></ruby>

                合規國際互聯網加速 OSASE為企業客戶提供高速穩定SD-WAN國際加速解決方案。 廣告
                ![](https://img.kancloud.cn/31/6a/316a580bf0606a8e7a4d8554cf1dce7a_1615x710.png)![](https://img.kancloud.cn/ef/6b/ef6b39fa8ccbd4190c3ca5bf0028580f_694x552.png)在計算機性能調試領域里,profiling 是指對應用程序的畫像,畫像就是應用程序使用 CPU 和內存的情況。 Go語言是一個對性能特別看重的語言,因此語言中自帶了 profiling 的庫,這篇文章就要講解怎么在 golang 中做 profiling。 ### 前言 寫了幾噸代碼,實現了幾百個接口。功能測試也通過了,終于成功的部署上線了 結果,性能不佳,什么鬼? ### pprof 想要進行性能優化,首先矚目在 Go 自身提供的工具鏈來作為分析依據,本文將帶你學習、使用 Go 后花園,涉及如下: + runtime/pprof:采集程序(非 Server)的運行數據進行分析 + net/http/pprof:采集 HTTP Server 的運行時數據進行分析 pprof開啟后,每隔一段時間(10ms)就會收集下當前的堆棧信息,獲取格格函數占用的CPU以及內存資源;最后通過對這些采樣數據進行分析,形成一個性能分析報告。 注意,我們只應該在性能測試的時候才在代碼中引入pprof。 ### 是什么 pprof 是用于可視化和分析性能分析數據的工具 pprof 以 profile.proto 讀取分析樣本的集合,并生成報告以可視化并幫助分析數據(支持文本和圖形報告) profile.proto 是一個 Protocol Buffer v3 的描述文件,它描述了一組 callstack 和 symbolization 信息, 作用是表示統計分析的一組采樣的調用棧,是很常見的 stacktrace 配置文件格式 ### 支持什么使用模式 + Report generation:報告生成 + Interactive terminal use:交互式終端使用 + Web interface:Web 界面 ### 可以做什么 + CPU Profiling:CPU 分析,按照一定的頻率采集所監聽的應用程序 CPU(含寄存器)的使用情況,可確定應用程序在主動消耗 CPU 周期時花費時間的位置 + Memory Profiling:內存分析,在應用程序進行堆分配時記錄堆棧跟蹤,用于監視當前和歷史內存使用情況,以及檢查內存泄漏 + Block Profiling:阻塞分析,記錄 goroutine 阻塞等待同步(包括定時器通道)的位置 + Mutex Profiling:互斥鎖分析,報告互斥鎖的競爭情況 ### 工具型應用 如果你的應用程序是運行一段時間就結束退出類型。那么最好的辦法是在應用退出的時候把 profiling 的報告保存到文件中,進行分析。對于這種情況,可以使用runtime/pprof庫。 首先在代碼中導入runtime/pprof工具: ``` import "runtime/pprof" ``` ### CPU性能分析 開啟CPU性能分析: ``` pprof.StartCPUProfile(w io.Writer) ``` 停止CPU性能分析: ``` pprof.StopCPUProfile() ``` 應用執行結束后,就會生成一個文件,保存了我們的 CPU profiling 數據。得到采樣數據之后,使用go tool pprof工具進行CPU性能分析。 ### 內存性能優化 記錄程序的堆棧信息 ``` pprof.WriteHeapProfile(w io.Writer) ``` 得到采樣數據之后,使用go tool pprof工具進行內存性能分析。 go tool pprof默認是使用-inuse_space進行統計,還可以使用-inuse-objects查看分配對象的數量 ### 服務型應用(net/http/pprof) 如果你的應用程序是一直運行的,比如 web 應用,那么可以使用net/http/pprof庫,它能夠在提供 HTTP 服務進行分析。 如果使用了默認的http.DefaultServeMux(通常是代碼直接使用 http.ListenAndServe(“0.0.0.0:8000”, nil)),只需要在你的web server端代碼中按如下方式導入net/http/pprof ``` import _ "net/http/pprof" ``` 如果你使用自定義的 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) ``` 如果你使用的是gin框架,那么推薦使用"github.com/DeanThompson/ginpprof"。 **源碼示例** ```go package main import ( "flag" "log" "net/http" _ "net/http/pprof" "sync" "time" ) func Counter(wg *sync.WaitGroup) { time.Sleep(time.Second) var counter int for i := 0; i < 1000000; i++ { time.Sleep(time.Millisecond * 200) counter++ } wg.Done() } func main() { flag.Parse() //遠程獲取pprof數據 go func() { log.Println(http.ListenAndServe("localhost:8080", nil)) }() var wg sync.WaitGroup wg.Add(10) for i := 0; i < 10; i++ { go Counter(&wg) } wg.Wait() // sleep 10mins, 在程序退出之前可以查看性能參數. time.Sleep(60 * time.Second) } ``` 編譯運行之后在瀏覽器訪問 http://localhost:8080/debug/pprof/ 這個路徑下還有幾個子頁面: + /debug/pprof/profile:訪問這個鏈接會自動進行 CPU profiling,持續 30s,并生成一個文件供下載 + /debug/pprof/heap: Memory Profiling 的路徑,訪問這個鏈接會得到一個內存 Profiling 結果的文件 + /debug/pprof/block:block Profiling 的路徑 + /debug/pprof/goroutines:運行的 goroutines 列表,以及調用關系 ### 通過交互式終端使用 不管是工具型應用還是服務型應用,我們使用相應的pprof庫獲取數據之后,下一步的都要對這些數據進行分析,我們可以使用go tool pprof命令行工具。 go tool pprof最簡單的使用方式為: ``` go tool pprof [binary] [source] ``` 其中: + binary 是應用的二進制文件,用來解析各種符號; + source 表示 profile 數據的來源,可以是本地的文件,也可以是 http 地址。 注意事項: 獲取的 Profiling 數據是動態的,要想獲得有效的數據,請保證應用處于較大的負載(比如正在生成中運行的服務,或者通過其他工具模擬訪問壓力)。否則如果應用處于空閑狀態,得到的結果可能沒有任何意義。 示例代碼: ```go // runtime_pprof/main.go package main import ( "flag" "fmt" "os" "runtime/pprof" "time" ) // 一段有問題的代碼 func logicCode() { var c chan int // nil for { select { case v := <-c: // 阻塞 fmt.Printf("recv from chan, value:%v\n", v) default: time.Sleep(time.Millisecond * 500) } } } func main() { var isCPUPprof bool // 是否開啟CPUprofile的標志位 var isMemPprof bool // 是否開啟內存profile的標志位 flag.BoolVar(&isCPUPprof, "cpu", false, "turn cpu pprof on") flag.BoolVar(&isMemPprof, "mem", false, "turn mem pprof on") flag.Parse() if isCPUPprof { f1, err := os.Create("./cpu.pprof") // 在當前路徑下創建一個cpu.pprof文件 if err != nil { fmt.Printf("create cpu pprof failed, err:%v\n", err) return } pprof.StartCPUProfile(f1) // 往文件中記錄CPU profile信息 defer func() { pprof.StopCPUProfile() f1.Close() }() } for i := 0; i < 6; i++ { go logicCode() } time.Sleep(20 * time.Second) if isMemPprof { f2, err := os.Create("./mem.pprof") if err != nil { fmt.Printf("create mem pprof failed, err:%v\n", err) return } pprof.WriteHeapProfile(f2) f2.Close() } } ``` 執行 ``` go run main.go -cpu ``` 等一會就出在同級目錄下生成一個cpu.pprof文件 ### 通過交互式終端使用 我們使用go工具鏈里的pprof來分析一下。 ``` go tool pprof cpu.pprof ``` 執行上面的代碼會進入交互界面如下: ``` Type: cpu Time: Nov 14, 2019 at 11:21am (CST) Duration: 20s, Total samples = 50ms ( 0.25%) Entering interactive mode (type "help" for commands, "o" for options) (pprof) ``` 我們可以在交互界面輸入top3來查看程序中占用CPU前3位的函數: ``` Type: cpu Time: Nov 14, 2019 at 11:21am (CST) Duration: 20s, Total samples = 50ms ( 0.25%) Entering interactive mode (type "help" for commands, "o" for options) (pprof) top3 Showing nodes accounting for 40ms, 80.00% of 50ms total Showing top 3 nodes out of 19 flat flat% sum% cum cum% 20ms 40.00% 40.00% 20ms 40.00% runtime.stdcall1 10ms 20.00% 60.00% 10ms 20.00% runtime.casgstatus 10ms 20.00% 80.00% 10ms 20.00% runtime.findrunnable ``` 結束后將默認進入 pprof 的交互式命令模式,可以對分析的結果進行查看或導出。具體可執行 pprof help 查看命令說明 + flat:給定函數上運行耗時 + flat%:同上的 CPU 運行耗時總比例 + sum%:給定函數累積使用 CPU 總比例 + cum:當前函數加上它之上的調用運行總耗時 + cum%:同上的 CPU 運行耗時總比例 最后一列為函數名稱,在大多數的情況下,我們可以通過這五列得出一個應用程序的運行情況,加以優化 我們還可以使用list 函數名命令查看具體的函數分析,例如執行list logicCode查看我們編寫的函數的詳細分析。 ``` (pprof) list logicCode Total: 50ms ``` 結合代碼可以找到需要優化的代碼行數 ### 圖形化 或者可以直接輸入web,通過svg圖的方式查看程序中詳細的CPU占用情況。 想要查看圖形化的界面首先需要安裝graphviz圖形化工具。 **Mac:** ``` brew install graphviz ``` Windows: 下載[graphviz](https://graphviz.gitlab.io/_pages/Download/Download_windows.html "graphviz") 將graphviz安裝目錄下的bin文件夾添加到Path環境變量中。 在終端輸入dot -version查看是否安裝成功。 關于圖形的說明: 每個框代表一個函數,理論上框的越大表示占用的CPU資源越多。 方框之間的線條代表函數之間的調用關系。 線條上的數字表示函數調用的次數。 方框中的第一行數字表示當前函數占用CPU的百分比,第二行數字表示當前函數累計占用CPU的百分比。 ### go-torch和火焰圖 火焰圖(Flame Graph)是 Bredan Gregg 創建的一種性能分析圖表,因為它的樣子近似 ??而得名。上面的 profiling 結果也轉換成火焰圖,如果對火焰圖比較了解可以手動來操作,不過這里我們要介紹一個工具:go-torch。這是 uber 開源的一個工具,可以直接讀取 golang profiling 數據,并生成一個火焰圖的 svg 文件。 安裝go-touch ``` go get -v github.com/uber/go-torch ``` 火焰圖 svg 文件可以通過瀏覽器打開,它對于調用圖的最優點是它是動態的:可以通過點擊每個方塊來 zoom in 分析它上面的內容。 火焰圖的調用順序從下到上,每個方塊代表一個函數,它上面一層表示這個函數會調用哪些函數,方塊的大小代表了占用 CPU 使用的長短。火焰圖的配色并沒有特殊的意義,默認的紅、黃配色是為了更像火焰而已。 go-torch 工具的使用非常簡單,沒有任何參數的話,它會嘗試從http://localhost:8080/debug/pprof/profile獲取 profiling 數據。它有三個常用的參數可以調整: + -u –url:要訪問的 URL,這里只是主機和端口部分 + -s –suffix:pprof profile 的路徑,默認為 /debug/pprof/profile + –seconds:要執行 profiling 的時間長度,默認為 30s 安裝 FlameGraph 要生成火焰圖,需要事先安裝 FlameGraph工具,這個工具的安裝很簡單(需要perl環境支持),只要把對應的可執行文件加入到環境變量中即可。 1.下載安裝perl:https://www.perl.org/get.html 2.下載FlameGraph:git clone https://github.com/brendangregg/FlameGraph.git 3.將FlameGraph目錄加入到操作系統的環境變量中。 4.Windows平臺的同學,需要把go-torch/render/flamegraph.go文件中的GenerateFlameGraph按如下方式修改,然后在go-torch目錄下執行go install即可。 ``` // GenerateFlameGraph runs the flamegraph script to generate a flame graph SVG. func GenerateFlameGraph(graphInput []byte, args ...string) ([]byte, error) { flameGraph := findInPath(flameGraphScripts) if flameGraph == "" { return nil, errNoPerlScript } if runtime.GOOS == "windows" { return runScript("perl", append([]string{flameGraph}, args...), graphInput) } return runScript(flameGraph, args, graphInput) } ``` ### 壓測工具wrk 推薦使用 https://github.com/wg/wrk 或 https://github.com/adjust/go-wrk ### 使用go-torch 使用wrk進行壓測:go-wrk -n 50000 http://127.0.0.1:8080/book/list 在上面壓測進行的同時,打開另一個終端執行go-torch -u http://127.0.0.1:8080 -t 30,30秒之后終端會初夏如下提示:Writing svg to torch.svg 然后我們使用瀏覽器打開torch.svg就能看到火焰圖了。 火焰圖的y軸表示cpu調用方法的先后,x軸表示在每個采樣調用時間內,方法所占的時間百分比,越寬代表占據cpu時間越多。通過火焰圖我們就可以更清楚的找出耗時長的函數調用,然后不斷的修正代碼,重新采樣,不斷優化。 ### pprof與性能測試結合 go test命令有兩個參數和 pprof 相關,它們分別指定生成的 CPU 和 Memory profiling 保存的文件: + -cpuprofile:cpu profiling 數據要保存的文件地址 + -memprofile:memory profiling 數據要報文的文件地址 我們還可以選擇將pprof與性能測試相結合,比如: 比如下面執行測試的同時,也會執行 CPU profiling,并把結果保存在 cpu.prof 文件中: ``` go test -bench . -cpuprofile=cpu.prof ``` 比如下面執行測試的同時,也會執行 Mem profiling,并把結果保存在 cpu.prof 文件中: ``` go test -bench . -memprofile=./mem.prof ``` 需要注意的是,Profiling 一般和性能測試一起使用,這個原因在前文也提到過,只有應用在負載高的情況下 Profiling 才有意義。
                  <ruby id="bdb3f"></ruby>

                  <p id="bdb3f"><cite id="bdb3f"></cite></p>

                    <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
                      <p id="bdb3f"><cite id="bdb3f"></cite></p>

                        <pre id="bdb3f"></pre>
                        <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

                        <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
                        <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

                        <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                              <ruby id="bdb3f"></ruby>

                              哎呀哎呀视频在线观看