Go言語でプロファイリング(CPU profiling検証編)

Go言語ではプロファイリングを行えるようになっています。

  • CPU profiling
  • MEMORY recycling

まずは「Go Profiling - John Graham-Cumming」のスライドを元にCPU profilingを試してみました。
※試してみただけでどう対応すべきかについては述べません

CPU profiling

CPUプロファイリングはコード内のどの箇所に時間がかかっているかを調べるのに使用できます。
検証コードはこちらの内容から利用させていただいてます。

type Item struct {
    key   string
    value string
    last  time.Time
}

type Cache struct {
    cap  int
    data map[string]*Item
}

func NewCache(cap int) *Cache {
    return &Cache{cap, make(map[string]*Item)}
}

func (c *Cache) makeSpace() {
    old := &Item{last: time.Now()}
    var key string
    for k, v := range c.data {
        if v.last.Before(old.last) {
            old = v
            key = k
        }
    }

    delete(c.data, key)
}

func (c *Cache) Put(key, value string) {
    if len(c.data) == c.cap {
        c.makeSpace()
    }

    c.data[key] = &Item{key, value, time.Now()}
}

func (c *Cache) Get(key string) *Item {
    if c.data[key] != nil {
        c.data[key].last = time.Now()
    }

    return c.data[key]
}

これはキャッシュの実装です。キャッシュする最大件数を指定しておき、最大数を超えた場合はもっともアクセスのなかった古いものから削除されていくというものです。
この実装に対してキャッシュサイズ10000、対象データ数1000000件、キーの最大数15000(古いキャッシュを削除するために10000以上に設定)で実行します。
一度実行しみます。

$ go build -o lrutest1.exe lrutest1.go
$ lrutest1.exe < top10M.csv
1000000 total 337773 misses
1m53.7145041s passed.

1000000件中337773がキャッシュにヒットせず、1分53秒程度かかっているということです。
これをCPUプロファイリングしていきたいと思います。
まずはmain関数の最初に

// lrutest1.cpuprofileは結果の出力先、ファイル名は任意
f, _ := os.Create("lrutest1.cpuprofile")
pprof.StartCPUProfile(f)
defer pprof.StopCPUProfile()

を追記します。先程と同じように実行すると今度はlrutest1.cpuprofileというファイルが出力されるので、以下のコマンドを実行します。

$ go tool pprof lrutest1.exe lrutest1.cpuprofile
lrutest1.cpuprofile: need a 64-bit perl to process this 64-bit profile.

はい、きました。いつものWindowsいじめです。現在の環境にはmsysgit(これ32bit)が入っていてそのなかのperl.exeを参照しているようです。
解決策はEx Nihilo: Profiling Go with pprof under Windowsに書いてあるとおりで、

  1. ActivePerlをいれてパスを先にそこに通す
  2. %GOROOT%\pkg\tool\windows_amd64\pprofに直接引数渡して起動

と。ちなみにUbuntuでは上記コマンド1発でOKです。
で環境を整えて再挑戦。

perl %GOROOT%\pkg\tool\windows_amd64\pprof lrutest1.exe lrutest1.cpuprofile
Welcome to pprof!  For help, type 'help'.
(pprof)

はい、やっとでました。(pprof)というプロンプトで止まっているので、コマンドを入力します。

(pprof) top
Total: 11044 samples
    4343  39.3%  39.3%    10902  98.7% github.com/taknb2nch/go-profiling/lru1.(*Cache).makeSpace
    3128  28.3%  67.6%     3128  28.3% hash_next
    2216  20.1%  87.7%     2982  27.0% runtime.mapiter2
     572   5.2%  92.9%      572   5.2% runtime.memcopy64
     441   4.0%  96.9%     3572  32.3% runtime.mapiternext
     195   1.8%  98.7%      195   1.8% runtime.strcopy
      23   0.2%  98.9%       24   0.2% bufio.(*Reader).ReadRune
      22   0.2%  99.1%       30   0.3% runtime.mallocgc
      11   0.1%  99.2%       66   0.6% encoding/csv.(*Reader).parseField
      10   0.1%  99.2%       29   0.3% bytes.(*Buffer).WriteRune

topコマンドはプロファイル数、-cumオプションをつけると、累積数が出るようです。(それが何なのかというツッコミはなしで)

(pprof) top -cum
Total: 11044 samples
       0   0.0%   0.0%    11039 100.0% runtime.gosched0
       0   0.0%   0.0%    11039 100.0% runtime.main
       1   0.0%   0.0%    11039 100.0% text
       1   0.0%   0.0%    10915  98.8% github.com/taknb2nch/go-profiling/lru1.(*Cache).Put
    4343  39.3%  39.3%    10902  98.7% github.com/taknb2nch/go-profiling/lru1.(*Cache).makeSpace
     441   4.0%  43.3%     3572  32.3% runtime.mapiternext
    3128  28.3%  71.7%     3128  28.3% hash_next
    2216  20.1%  91.7%     2982  27.0% runtime.mapiter2
     572   5.2%  96.9%      572   5.2% runtime.memcopy64
     195   1.8%  98.7%      195   1.8% runtime.strcopy

Cache.makeSpaceメソッドにコストがかかっているのがわかります。
またこの結果を視覚化することもできます。視覚化するためにはGraphvizをインストールしなくてはなりません。リンク先からダウンロードして、パスを通しておいてください。(Ubuntuならapt-get instal graphvizでいけます)
インストール後以下のコマンドを実行します。

$ perl %GOROOT%\pkg\tool\windows_amd64\pprof --web lrutest1.exe lrutest1.cpuprofile
Dropping nodes with <= 55 samples; edges with <= 11 abs(samples)
'dot' は、内部コマンドまたは外部コマンド、
操作可能なプログラムまたはバッチ ファイルとして認識されていません。
...

Ubuntuでは $ go tool pprof --web lrutest1.exe lrutest1.cpuprofile)
はい、またきました、動きません。
本らならデフォルトのブラウザが起動して、出力結果のが表示されるのですが、perlスクリプトWindowsに対応していないようです。(Ubuntuでは問題なく動きます)
しかしsvgのファイル自体はtempに出力されるのでブラウザで開けばOKです。

このようにグラフで表示されます。やはり視覚化すると非常に分かりやすいです。結果が一目瞭然なので、その部分を修正していくことになります。


pprof には他にもたくさんオプションがあるようなのですが、今回はここまでに止めておきたいと思います。