Go言語でプロファイリング(MEMORY recyclingというよりメモリ使用状況の調査編)

前エントリーではCPU profilingを試してみましたが、今度は「Go Profiling - John Graham-Cumming」のスライドを元にMEMORY recyclingを試してみたいと思います。
※MEMORY recyclingというよりもメモリ使用状況の調査
※試してみただけでどう対応すべきかについては述べません

MEMORY recycling(メモリ使用状況の調査)

MEMORYプロファイリングはメモリリークの発見やGCの発生率を調べることができます。
まずは試してみたコードを記載しておきます。今回も検証コードはこちらの内容から利用させていただいてます。

func makeBuffer() []byte {
    return make([]byte, rand.Intn(5000000)+5000000)
}

func main() {
    pool := make([][]byte, 20)

    var m runtime.MemStats
    makes := 0
    for {
        b := makeBuffer()
        makes += 1
        i := rand.Intn(len(pool))
        pool[i] = b

        time.Sleep(time.Second)

        bytes := 0

        for i := 0; i < len(pool); i++ {
            if pool[i] != nil {
                bytes += len(pool[i])
            }
        }

        runtime.ReadMemStats(&m)
        fmt.Printf("%d,%d,%d,%d,%d,%d\n", m.HeapSys, bytes, m.HeapAlloc,
            m.HeapIdle, m.HeapReleased, makes)
    }
}

これはサイズが20のプールを用意しておいて、1秒毎に5000000以上10000000未満バイトのスライスを作成し、プールのいずれかの場所に格納するというサンプルです。
CPU profilingの時と同じで、プロファイリングするためには簡単ですがコードに追記する必要があります。
MEMORY recyclingの場合は、ReadMemStatsを使用します。

runtime.ReadMemStats(&m)
fmt.Printf("%d,%d,%d,%d,%d,%d\n", m.HeapSys, bytes, m.HeapAlloc, m.HeapIdle, m.HeapReleased, makes)

適当なタイミングでメモリの状況を出力しておいて後ほど(リアルタイムでもできそう?)その内容を検証するという方法のようです。
以下のコマンドで実行し、適当なタイミングで終了させます。

$ go build -g garbagetest1.exe garbagetest1.go
$ garbagetest1.exe > garbagetest1.dat

gnuplotで描画したグラフがこれらになります。


恐らく定期的にGCが発生しているのだと思われますが、リーク等はみられないようです。(実行時間が10分間ほどですが)
このグラフを見ながらチューニング、修正していくものと思われます。
ここから先の話はまた別のエントリーで。(いつになるか分かりませんが)

まとめ

CPU profilingにしろMEMORY recycling(メモリ使用状況の調査)にしろ、Goのデフォルトのツール(Windowsでは例外がありますが)で行えるというのは手軽で便利です。
ただプロファイルするためにオリジナルのコードに手を入れなくてはならないのがなんとも言えないところです。(コンパイル型の言語なので仕方ないです)