Go Wiki: CustomPprofProfiles

元々は https://rakyll.org/custom-profiles/ にて公開されました。


Goには、Goプログラムからプロファイリングデータを収集するために、いくつかのpprofプロファイルが最初から提供されています。

runtime/pprof パッケージによって提供される組み込みプロファイル

  • profile: CPUプロファイルは、プログラムが積極的にCPUサイクルを消費している間(スリープ中やI/O待ち中とは対照的に)、どこに時間を費やしているかを判断します。
  • heap: ヒーププロファイルは、現在生きている割り当てを報告します。現在のメモリ使用量を監視したり、メモリリークをチェックしたりするために使用されます。
  • threadcreate: スレッド作成プロファイルは、新しいOSスレッドの作成につながるプログラムのセクションを報告します。
  • goroutine: ゴルーチンプロファイルは、すべての現在のゴルーチンのスタックトレースを報告します。
  • block: ブロックプロファイルは、ゴルーチンが同期プリミティブ(タイマーチャネルを含む)で待機中にブロックされている場所を示します。ブロックプロファイルはデフォルトでは有効になっていません。有効にするには runtime.SetBlockProfileRate を使用します。
  • mutex: ミューテックスプロファイルは、ロックの競合を報告します。ミューテックスの競合によりCPUが十分に活用されていないと思われる場合は、このプロファイルを使用します。ミューテックスプロファイルはデフォルトでは有効になっていません。有効にするには runtime.SetMutexProfileFraction を参照してください。

組み込みプロファイルに加えて、runtime/pprof パッケージを使用すると、カスタムプロファイルをエクスポートし、コードを計測して、このプロファイルに貢献する実行スタックを記録できます。

ブロブサーバーがあり、そのGoクライアントを作成していると想像してください。ユーザーはクライアントで開いているブロブをプロファイルできるようにしたいと考えています。ブロブの開閉イベントを記録するプロファイルを作成することで、ユーザーはいつでも開いているブロブの数を把握できます。

ここでは、ブロブを開くことができるblobstoreパッケージを示します。新しいカスタムプロファイルを作成し、ブロブの開くことに貢献する実行スタックの記録を開始します

package blobstore

import "runtime/pprof"

var openBlobProfile = pprof.NewProfile("blobstore.Open")

// Open opens a blob, all opened blobs need
// to be closed when no longer in use.
func Open(name string) (*Blob, error) {
    blob := &Blob{name: name}
    // TODO: Initialize the blob...

    openBlobProfile.Add(blob, 2) // add the current execution stack to the profile
    return blob, nil
}

ユーザーがブロブを閉じたい場合、現在のブロブに関連付けられた実行スタックをプロファイルから削除する必要があります

// Close closes the blob and frees the
// underlying resources.
func (b *Blob) Close() error {
    // TODO: Free other resources.
    openBlobProfile.Remove(b)
    return nil
}

そして今、このパッケージを使用するプログラムから、blobstore.Open プロファイルデータを取得し、日常のpprofツールを使用してそれらを調査および視覚化できるようになりました。

いくつかのブロブを開く小さなメインプログラムを書きましょう

package main

import (
    "fmt"
    "math/rand"
    "net/http"
    _ "net/http/pprof" // as a side effect, registers the pprof endpoints.
    "time"

    "myproject.org/blobstore"
)

func main() {
    for i := 0; i < 1000; i++ {
        name := fmt.Sprintf("task-blob-%d", i)
        go func() {
            b, err := blobstore.Open(name)
            if err != nil {
                // TODO: Handle error.
            }
            defer b.Close()

            // TODO: Perform some work, write to the blob.
        }()
    }
    http.ListenAndServe("localhost:8888", nil)
}

サーバーを起動し、goツールを使用してプロファイルデータを読み取り、視覚化します

$ go tool pprof https://:8888/debug/pprof/blobstore.Open
(pprof) top
Showing nodes accounting for 800, 100% of 800 total
      flat  flat%   sum%        cum   cum%
       800   100%   100%        800   100%  main.main.func1 /Users/jbd/src/hello/main.go

800個のオープンなブロブがあり、すべてのオープンが main.main.func1 から来ていることがわかります。この小さな例では、他に何も見るものはありませんが、複雑なサーバーでは、オープンなブロブで動作するホットスポットを調査し、ボトルネックやリークを発見することができます。


このコンテンツはGo Wikiの一部です。