Nim で プロファイリング結果を FlameGraph にする

この記事は KMC Advent Calendar 2018 - Adventar 7日目の記事です. ついでに、Nim Advent Calendar 2018 - Qiita の 7日目の記事でもあります. 前日のKMCアドベントカレンダーの記事は dnek_ さんの 運ゲー排除マインスイーパー💣脱Unity計画(Android編)&SATによるソルバー改良 - dnekblog でした. がんばりますねー.

はじめに

Nimっていう最高の言語があるんですけどご存知でしょうか? 過去のアドベントカレンダーでもその素晴らしさを語っているので, ご存知でない人はぜひ御覧ください. この記事では Nim は version 0.19.0 を使用しています.

プロファイリング

コードを書いていてパフォーマンスがあまり出ない時,どこがボトルネックとなっているかを調べることは重要です. プロファイリングをせず,ただ闇雲に時間がかかってそうと思った場所を直すという方法は往々にして思った結果が出ないものです.

幸いなことに Nim ではデフォルトでスタックトレースプロファイラーが搭載されています.

プロファイリングの手順は以下のとおりです.

  1. プロファイリングしたいNimのファイルに import nimprof を記述する.
  2. Nimのコンパイル時のコマンドに --profiler:on --stackTrace:on を加える

これで実行すると, profile_results.txt という以下のようなスタックトレース結果が生成されます.

total executions of each stack trace:
Entry: 1/510 Calls: 961/8114 = 11.84% [sum: 961; 961/8114 = 11.84%]
  assign.nim: genericAssignAux 3388/8114 = 41.75%
  assign.nim: genericAssign 3422/8114 = 42.17%
  assign.nim: genericSeqAssign 3447/8114 = 42.48%
  matrix.nim: deepCopy 3415/8114 = 42.09%
  target.nim: tryUpdate 2486/8114 = 30.64%
Entry: 2/510 Calls: 948/8114 = 11.68% [sum: 1909; 1909/8114 = 23.53%]
  assign.nim: genericAssignAux 3388/8114 = 41.75%
  assign.nim: genericAssignAux 3388/8114 = 41.75%
  assign.nim: genericAssign 3422/8114 = 42.17%
  assign.nim: genericSeqAssign 3447/8114 = 42.48%
  matrix.nim: deepCopy 3415/8114 = 42.09%
  target.nim: tryUpdate 2486/8114 = 30.64%
...
...

例えばこれは最近書いている解析用のプログラムの結果のものなのですが, この結果からtryUpdate関数のdeepCopy関数でのgenericSeqAssign, 要は大きな配列のコピーがネックとなっていることがわかります. これでも十分分かりやすいですが,できればもう少し視覚的に分かりやすく表示したいものです.

Flame Graph

Flame Graph の形式を用いることで, 上図のように視覚的に分かりやすく結果を見ることができます. Flame Graph の読み方などに関しては, (Go言語版の記事ですが) https://deeeet.com/writing/2016/05/29/go-flame-graph/ などが分かりやすいです.

さて, このFlame Graph の SVG を生成するPerlのコードはGithubにて公開されており, Go や Java など色々な言語 の Flame Graph を生成することができます, ただ,残念ながら調べた限りだと Nim の Flame Graph を生成するものはまだ無いようです.

Flame Graph 生成

無いなら作るしかないですね.早速作ってみましょう. 上記リポジトリに有る flamegraph.pl というファイルに,例えば以下のような入力を加えて実行すると, 図のような Flame Graph を作成することができるようです.

func1;func2 10
func3 8
func4;func5;func6 3

分かりやすいですね.

あとは profile_results.txt の結果をこの形式に変換すれば良さそうです. というわけで変換スクリプトを書いてみました.

profile_results.txt を flamegraph.pl 形式にするやつ · GitHub

いい感じに変換できてそうですね. スクリプトのご利用はご自由にどうぞ.

さいごに

以上、プロファイリングは大事でFlameGraphは見やすくて便利ということでした. ここまで読んでくださり、ありがとうございました.

Nimに興味を持たれたら、 Nim Advent Calendar 2018 - Qiita を読んだり、 Nim programming language | Nim を読んだりしてください.

明日のKMC AdventCalendar は Kana_kmc さんによる 16年間を振り返って整理する です.

追記

当初予定していたアドベントカレンダーの内容は,「太古のPythonを眺めてみる」でした. 便利なことに https://www.python.org/ftp/python/src/Python-0.9.1.tar.gz を解凍して configure して make すれば Python 0.9.1 という太古のバージョンを特に苦もなく動かすことができます. 太古のPythonでは,1タブ8スペースだったり !=<> だったり,class__init__が無かったり文法がいろいろ違って楽しい...ということを書こうとしたのですがあまり深い内容が無かったのでやめてしまいました. 他の(virtualenvで取れないような)太古のバージョンも, https://www.python.org/ftp/python/src/ から容易に得ることができ,過去の遺産を残しているPythonはすごいなあと思いました.