経験のあるソフトウェア開発者であっても、自分の作っているソフトウェアのパフォーマンスのボトルネックについては、間違った推測をしてしまうことがよくあります。
その時はパフォーマンス上のボトルネックがどこにあり、どこを集中的に最適化しなければならないのかを調べるためにプロファイルをすべきです。
Erlang/OTPにはボトルネックを発見するためのツールがいくつか含まれています。
fprofとeprofはどこで時間が消費されているのか、という情報を、一番詳しく教えてくれるツールです。しかし、プロファイルを計測中はプログラムの実行速度は大幅に落ちます。
もし、あなたが測定しようとしているプログラムが大きすぎて、fprofやeprofで測定できない場合には、まず全体をfporf, eprofで測定してあたりをつけてから、cover, cporfを使用して、徹底的にプロファイルすべき箇所をテストすることができるようになります。
coverは、プロセスごとに、どの行を何回実行したかという情報を出力します。coverはfprof/eprofよりも、パフォーマンス上の劣化は少ないです。実行数というのは潜在的なパフォーマンスのボトルネックがどこにあるのかに注意するうえで使用することができます。もっとも軽いツールはcporfです。しかしこれはプロセス全体の関数の実行回数を知らせる機能しかありません。プロセスごとに集計する機能もありません。
もし巨大なシステムを開発しているのであれば、まずは限定されたシナリオからテストを開始してプロファイラを実行してみるとよいでしょう。しかし、多くの事が同時進行で動いている時に限ってボトルネックが発生したり、問題が起きたりする傾向があります。そのため、理想的には、実際にターゲットになるシステム上にシステムテスト環境を構築してプロファイルを実行するのが理想です。
もしシステムが大きすぎる場合には、システム全体に対してプロファイルを実行したくないでしょう。その場合には、実行時に大部分を占めていたり、中心であると分かっているプロセスやモジュールに集中したいと思うでしょう。
プロファイラが出力した結果ファイルを分析する際には、呼ばれている回数が多い関数と、それ自身の実行時間(他の関数を呼び出している時間は除く)が長い関数を見つけましょう。非常に数多く呼び出されている関数というのも、注目すべき対象です。小さい時間も、頻繁に繰り返されて積もり積もれば、非常に大きな時間になる可能性があるからです。まずは、自分に対して、この時間を減らすには何ができるか?と自分に問いかけてみましょう。
- その関数の呼び出し回数を減らすことはできますか?
- テストの順番を変えるとそのテストの実行回数は少なくなりますか?(訳注:テスト環境依存なのか?ということを意味していると思われる)
- 取り除けるような、余計なテストはありますか?
- 毎回同じ結果を返すような計算式はありますか?
- 同じ処理をより効率的に行う方法はありますか?
- 処理を効率的に行えるような別の内部データ表現を考えることはできないか?
これらの質問は簡単に答えられないものも中にはあるでしょう。あなたの考えを補助するために、追加のベンチマークが必要になることもあるでしょう。もしあなたの考えが間違っていれば、プログラムの速度の低下を招くこともあります。まずはベンチマークをしてみましょう。
fprofは関数ごとの実行時間を測定します。実行時間は、その関数自身が実行するのに使用した時間と、そこから呼び出された関数の時間も積算した時間の両方が測定されます。測定値はプロセスごとに表示されます。また、それぞれの関数ごとに、何回呼び出されたのか、という情報を得ることもできます。fporfは実行時のパフォーマンスへの影響を最小にするために、トレースを元にして実装されています。fprofを使用すると、いくつかライブラリの関数を呼ぶことになるという制約があります。詳しくはapplication toolsの中にあるfprofのマニュアルを参照してください。してください。
fporfはErlang/OTPのR8というバージョンから導入されました。先輩であるeprofはErlangの trace 組み込み関数を使って実装されていました。eporfは現在でも利用可能です。詳しくはapplication tools以下にあるeporfのマニュアルを参照してください。eporfはプロセスごとにどれだけの時間を使用したか、ということを表示してくれました。どのこの時間はどの関数呼び出しで時間を過ごしたかということも分かります。しかし、この時間はトータル時間に占めるパーセンテージで表示されていて、絶対的な時間は分かりませんでした。
coverは主にテストケースが、関連コードをすべてカバーしているかを検証するためのカバレッジ分析に使用されます。coverは、プログラムの実行時にそれぞれの行が何回実行されたかというのをカウントします。これは基本的にモジュールごとに行われます。もちろん、この情報はどのコードが頻繁に実行されているかを明らかにしたり、どの箇所を最適化すればよいのか決めるのに使用することができます。coverを使用すると、いくつかの依存するライブラリ関数への呼び出しが発生するという制約があります。詳しくはapplication toolsのcoverのマニュアルをご覧ください。
cporfはfporfとcoverの中間の機能を備えたものです。cporfは関数が一回のプログラムの実行中に、それぞれの関数が何回呼ばれたのかというのを、モジュールごとにカウントします。cporfはパフォーマンスへの影響が小さい(fporfやeporfと比べて)です。そしてcoverとは異なり、プロファイルを取りたいモジュールに対する再コンパイルも必要ありません。
ツール | 結果 | 結果サイズ | プログラム実行時間への影響 | コール回数の記録 | 実行時間の記録 | 呼び出し元の記録 | ガベージコレクションの記録 |
---|---|---|---|---|---|---|---|
fprof | プロセスごとに画面/ファイルに出力 | 大きい | 極めて大きい | O |
|
O | O |
eprof | プロセス/関数ごとに画面/ファイルに出力 | 中間 | 極めて大きい | O | トータル | X | X |
cover | モジュールごとに画面/ファイルに出力 | 小さい | 中間の低下 | O/行ごと | X | X | X |
eprof | モジュールごとに呼び出し元に結果を通知 | 小さい | 極めて小さい | O | X | X | X |
ベンチマークの主な目的としては与えられたアルゴリズムの実装や関数のうち、どれが最速化を決めるというものです。ベンチマークは正確なコンピュータ科学とはほど遠いものです。今日のオペレーティングシステムでは、一般的には終了するのが難しいタスクはバックグラウンドで実行されます。キャッシュや複数コアを持つCPUは事態を複雑にします。ベンチマークを取るのは、シングルユーザモードで実行しているUNIXのコンピュータが最適だと思われますが、少なくとも、気軽にテストを行うには不便であると言えます。
ベンチマークは壁掛け時計の時間(訳注:実際にプログラムの処理が終わるまでに経過した時間)でも、CPU時間でも測定できます。
timer:tc/3 は壁掛け時計の時間で測定することができます。この時間を使うメリットは、I/O, ディスクのスワップ、その他のオペレーティングシステムのカーネルの行っている活動も含んだ時間が測定できるというものです。デメリットとしてはこの測定結果が幅広く変化する可能性がある、というものです。通常、このベンチマークを使用する場合には何度か実行して、もっとも時間の短いものを記録するのがベストです。テスト環境の中で最良の環境下で達成可能な結果が最小の時間と言えます。
timer:tc/3: http://erlang.org/doc/man/timer.html#tc-3
引数付きで statistics/1 を実行すると、Erlang仮想マシンで使用されたCPU時間を測定することができます。この時間を計測するメリットは、何度実行しても結果が安定しているということにあります。欠点としてはこの時間にはオペレーティングシステムのカーネル内で消費された時間が含まれないため、ディスクのスワップやI/Oの時間が記録されないことです。そのため、CPU時間を測定すると、I/O(ファイルやソケット)が含まれていると誤解を招くことがあります。
statistics/1: http://erlang.org/doc/man/erlang.html#statistics-1
壁掛け時計の時間とCPU時間と両方を測定するのが良い考えだと思われます。
いくつかの追加のアドバイスがあります。
Copyright c 1991-2009 Ericsson AB