Haskellのプログラムをプロファイルしてみる。
GHCではプログラムのプロファイルをとってくれる機能があります。
数独ソルバーのパ実行フォーマンスを見るのに使ってみました。
GHCのマニュアルを見れば出ていることなのですが、GHCのプロファイラはいくつかモードがあって、ものによってはメモリの消費情報をグラフ化してくれたりするものもあるようですが、今回は「ふつう」のやつをやってみました。実行速度とメモリの消費を関数ごとに計測してくれます。
プロファイルを行うには2つのステップが必要です。
まずはプログラムを次のオプションを追加してコンパイルします。
- O2 -prof -auto-all -o Main
そして、出来上がった実行ファイルを次のオプションをつけて実行してみます。
- RTS -p
実行が終了した後に *.profというファイルが生成されています。これがプロファイルの結果の書かれたファイルになります。
プロファイル結果の例はこんな感じです。
Time and Allocation Profiling Report (Final) foo +RTS -p -RTS total time = 4.34 secs (217 ticks @ 20 ms) total alloc = 812,398,524 bytes (excl. profiling overheads)
COST CENTRE | %time | %alloc |
exclude | 38.2 | 35.8 |
propagate | 33.2 | 26.1 |
set | 18.9 | 27.0 |
excludeM | 3.7 | 1.3 |
uniq | 2.8 | 4.6 |
tryChoices | 1.8 | 5.1 |
onlyOne | 1.4 | 0.0 |
- | - | - | individual | inherited | ||
COST CENTRE | no. | entries | time | alloc | time | alloc |
MAIN | 1 | 0 | 0.0 | 0.0 | 100.0 | 100.0 |
main | 196 | 1 | 0.0 | 0.0 | 100.0 | 99.9 |
tryChoices | 220 | 32444 | 1.8 | 5.1 | 100.0 | 99.9 |
set | 222 | 155293 | 18.9 | 27.0 | 97.2 | 94.8 |
exclude | 225 | 437059 | 6.5 | 6.0 | 6.5 | 6.0 |
propagate | 223 | 155293 | 33.2 | 26.1 | 71.9 | 61.9 |
onlyOne | 229 | 721311 | 0.5 | 0.0 | 0.5 | 0.0 |
excludeM | 226 | 1302379 | 3.7 | 1.3 | 12.4 | 9.2 |
exclude | 227 | 3963625 | 8.8 | 7.9 | 8.8 | 7.9 |
uniq | 224 | 155293 | 2.8 | 4.6 | 25.8 | 26.6 |
exclude | 228 | 1282200 | 23.0 | 21.9 | 23.0 | 21.9 |
onlyOne | 221 | 322671 | 0.9 | 0.0 | 0.9 | 0.0 |
showSudoku | 215 | 6 | 0.0 | 0.0 | 0.0 | 0.0 |
showChoice | 218 | 162 | 0.0 | 0.0 | 0.0 | 0.0 |
printGrid | 217 | 162 | 0.0 | 0.0 | 0.0 | 0.0 |
CAF | 190 | 33 | 0.0 | 0.0 | 0.0 | 0.1 |
printGrid | 219 | 0 | 0.0 | 0.0 | 0.0 | 0.0 |
showSudoku | 216 | 0 | 0.0 | 0.0 | 0.0 | 0.0 |
initialConstraintsC | 213 | 1 | 0.0 | 0.0 | 0.0 | 0.0 |
initialConstraintsY | 212 | 1 | 0.0 | 0.0 | 0.0 | 0.0 |
initialConstraintsX | 205 | 1 | 0.0 | 0.0 | 0.0 | 0.0 |
initialBoard | 203 | 2 | 0.0 | 0.0 | 0.0 | 0.0 |
initialSudoku | 202 | 1 | 0.0 | 0.0 | 0.0 | 0.0 |
p4 | 201 | 1 | 0.0 | 0.0 | 0.0 | 0.0 |
sample | 200 | 1 | 0.0 | 0.0 | 0.0 | 0.0 |
allPos | 199 | 1 | 0.0 | 0.0 | 0.0 | 0.0 |
sampleInput | 198 | 1 | 0.0 | 0.0 | 0.0 | 0.0 |
main | 197 | 0 | 0.0 | 0.0 | 0.0 | 0.0 |
set | 204 | 18 | 0.0 | 0.0 | 0.0 | 0.0 |
exclude | 208 | 54 | 0.0 | 0.0 | 0.0 | 0.0 |
propagate | 206 | 18 | 0.0 | 0.0 | 0.0 | 0.0 |
onlyOne | 214 | 327 | 0.0 | 0.0 | 0.0 | 0.0 |
excludeM | 209 | 327 | 0.0 | 0.0 | 0.0 | 0.0 |
exclude | 210 | 2529 | 0.0 | 0.0 | 0.0 | 0.0 |
uniq | 207 | 18 | 0.0 | 0.0 | 0.0 | 0.0 |
exclude | 211 | 327 | 0.0 | 0.0 | 0.0 | 0.0 |
CAF | 169 | 8 | 0.0 | 0.0 | 0.0 | 0.0 |
CAF | 164 | 1 | 0.0 | 0.0 | 0.0 | 0.0 |
CAF | 121 | 4 | 0.0 | 0.0 | 0.0 | 0.0 |
まず、最初の部分で全体でどれくらい時間がかかってどれくらいメモリを消費したかが記録されています。
total time = 4.34 secs (217 ticks @ 20 ms) total alloc = 812,398,524 bytes (excludes profiling overheads)
これはわりと難度の高いパズルを一つ解くのにかかった時間とメモリです。初期のころのコードでした。4秒ちょっとというのはまぁまぁだとしても、9x9の数独を解くのに 700Mバイト超のメモリーを使うのはなんともいただけませんね…
そして、その次の部分でメモリ、時間ともに特に消費している関数がリストされています。
COST CENTRE | %time | %alloc |
exclude | 38.2 | 35.8 |
propagate | 33.2 | 26.1 |
set | 18.9 | 27.0 |
excludeM | 3.7 | 1.3 |
uniq | 2.8 | 4.6 |
tryChoices | 1.8 | 5.1 |
onlyOne | 1.4 | 0.0 |
- | - | individual | inherited | ||
COST CENTRE | entries | %time | %alloc | %time | %alloc |
MAIN | 0 | 0.0 | 0.0 | 100.0 | 100.0 |
main | 1 | 0.0 | 0.0 | 100.0 | 99.9 |
tryChoices | 32444 | 1.8 | 5.1 | 100.0 | 99.9 |
set | 155293 | 18.9 | 27.0 | 97.2 | 94.8 |
exclude | 437059 | 6.5 | 6.0 | 6.5 | 6.0 |
propagate | 155293 | 33.2 | 26.1 | 71.9 | 61.9 |
onlyOne | 721311 | 0.5 | 0.0 | 0.5 | 0.0 |
excludeM | 1302379 | 3.7 | 1.3 | 12.4 | 9.2 |
exclude | 3963625 | 8.8 | 7.9 | 8.8 | 7.9 |
uniq | 155293 | 2.8 | 4.6 | 25.8 | 26.6 |
exclude | 1282200 | 23.0 | 21.9 | 23.0 | 21.9 |
onlyOne | 322671 | 0.9 | 0.0 | 0.9 | 0.0 |
showSudoku | 6 | 0.0 | 0.0 | 0.0 | 0.0 |