パフォーマンス分析では、まず runtime.pprof パッケージを使用して、分析対象のプログラムの入り口と終わりに埋め込む必要があります。 runtime.pprof パッケージは、実行時にプログラムを 1 秒あたり 100 回サンプリングします。最小サンプリング時間は 1 秒です。次に、開発者が分析のためにファイルまたは他のメディアに書き込むことができるように、生成されたデータを出力します。
go pprof ツール チェーンと Graphviz グラフィカル ツールは、runtime.pprof パッケージによって生成されたデータを PDF 形式に変換し、プログラムのパフォーマンス分析結果を画像の形式で表示できます。
サードパーティのグラフィカルで明示的な分析データ ツール (Graphviz) をインストールする
Graphviz は、テキストの説明を通じてグラフィックを生成するためのツールキットです。テキストを記述するための言語は DOT と呼ばれます。
各プラットフォームの最新の Graphviz インストール パッケージはwww.graphviz.orgで入手できます。
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 パッケージを使用します。
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 ツールのツール チェーンを使用して、cpu.pprof および cpu 実行可能ファイルを入力し、PDF 形式で出力ファイルを生成し、出力ファイルを cpu.pdf ファイルにリダイレクトします。このプロセスでは、Graphviz ツールが呼び出され、Graphviz の実行可能ディレクトリを Windows の環境変数 PATH に追加する必要があります。
各ボックスは関数呼び出しパスであり、3 番目のボックスの joinSlice 関数は CPU 時間の 50% を消費し、パフォーマンスのボトルネックが存在します。スライス要素の数がわかっている場合は、コードを再最適化し、メモリを直接割り当てます。コードは次のとおりです。
func joinSlice() []string {
const count = 100000
var arr []string = make([]string, count)
for i := 0; i < count; i++ {
arr[i] = "arr"
}
return arr
}
コードの説明は次のとおりです。
- 5行目では、前にappend()関数を使用して複数の割り当てを避けるために、スライスのcount数を事前に割り当てます。
- 8行目は、事前割り当て後、各要素に値を直接割り当てます。
パフォーマンス分析のために上記のコードを再実行すると、最終的な cpu.pdf には時間のかかる部分はなくなります。