Go 中的性能測量

Go 中的性能測量

“A Journey With Go” 專屬插圖,由 Renee French 根據原始 Go Gopher 製作。

:information_source: 本文基於 Go 1.13.

go test 命令提供了許多出色的功能,比如代碼覆蓋率,CPU 和 內存分析。要提供這些統計信息,Go 就需要一種方式來跟蹤 CPU 使用率,或在代碼覆蓋中跟蹤一個函數何時被用到。

性能測量

Go 使用多種方式來產生這些統計信息:

  • 動態插入性能測量語句,使其可以跟蹤到代碼何時進入一個函數或條件。這個策略在 代碼覆蓋率 中使用。
  • 每秒記錄多次程序樣本。這個策略在 CPU 分析 中用到。
  • 在代碼中使用靜態 hook,以便在執行期間調用所需函數。這個策略在內存分析中用到。

我們來寫一個簡單的程序並回顧所有內容。這是我們在後面的章節將使用的代碼:

<code>package main

import "math/rand"

func main() {
\tprintln(run())
}

//go:noinline
func run() int {
\ta := 0
\tfor i:= 0; i < rand.Intn(100000); i++ {
\t\tif i % 2 == 0 {
\t\t\tadd(&a)
\t\t} else {
\t\t\tsub(&a)
\t\t}
\t}

\treturn a
}

//go:noinline
func add(a *int) {
\t*a += rand.Intn(10)
}

//go:noinline
func sub(a *int) {
\t*a -= rand.Intn(10)
}/<code>

main.go 託管在 [GitHub] (https://github.com/) 查看

代碼覆蓋率

通過 GOSSAFUNC=run Go test -cover 命令生成的 SSA 代碼,我們可以查看 Go 對程序進行了什麼樣的修改:

Go 中的性能測量

變量 GoCover_0_313837343662366134383538 是一個標誌數組,其中每個鍵是一個代碼塊,當代碼實際進入這一塊時對應的標誌設置為 1.

你可以在我的文章 “Go: Compiler Phases” 中找到更多關於 SSA 的信息。

生成的代碼將稍後在管理代碼覆蓋率報告的函數中使用。 我們可以通過使用 objdump 命令反彙編代碼覆蓋期間生成的目標文件來進行驗證。 運行 go test -cover -o main.o && Go tool objdump main.go 將反彙編代碼並顯示缺少的部分。 它首先初始化並在自動生成的 init 函數中註冊 coverage:

Go 中的性能測量

test.go 添加的 init 方法

然後,如前所述,測試將在執行期間收集覆蓋率數據並且會觸發一個方法來實際寫入和顯示覆蓋率:

Go 中的性能測量

go test 調用的 after 函數

CPU 分析

跟蹤 CPU 使用率的策略則有所不同。Go 會停止程序並收集正在運行程序的樣本。這裡是未開啟 CPU 分析的代碼的 trace:

Go 中的性能測量

這裡是相同代碼開啟了 CPU 分析的 trace:

Go 中的性能測量

增加的 trace 與 pprof 及性能分析相關。這裡是其中一個的放大圖:

Go 中的性能測量

profileWriter 方法將循環調用,每 100 毫秒收集 CPU 數據,以在性能分析結束時最終生成報告。

內存分析

內存分析包含在源碼中,並已集成在內存分配系統中。在使用 -memprofile 開啟內存分析 的情況下,位於

malloc.go 中的內存分配器,將 對已分配的內存進行分析 。這裡,依然可以通過反彙編代碼進行驗證。這裡是內存分配器的使用:

Go 中的性能測量

開啟了內存分配分析


分享到:


相關文章: