gprof
両方でいくつかの C++ 数値計算コードをプロファイリングするkcachegrind
と、実行時間 (入力に応じて 50 ~ 80%) に最も寄与する関数については同様の結果が得られますが、10 ~ 30% の間の関数については、これらのツールの両方で異なる結果が得られます。それらの1つが信頼できないということですか?ここで何をしますか?
2 に答える
gprofは実際には非常に原始的です。これが何をするかです。1) プログラム カウンターを一定のレートでサンプリングし、各関数に到達するサンプル数を記録します (排他時間)。2) 関数 A が関数 B を呼び出した回数をカウントします。そこから、各関数が合計で呼び出された回数と、その平均排他時間が何であるかを知ることができます。各関数の平均包括時間を取得するために、呼び出しグラフで排他時間を上方に伝播します。
これにある程度の精度があることを期待している場合は、いくつかの問題に注意する必要があります。まず、処理中の CPU 時間のみをカウントします。つまり、I/O やその他のシステム コールを認識しません。第二に、再帰はそれを混乱させます。第 3 に、関数がいつ呼び出されても、誰が呼び出しても、常に平均実行時間に従うという前提は非常に疑わしいものです。第 4 に、知っておくべきことはコード行ではなく関数 (およびその呼び出しグラフ) であるという考えは、単なる一般的な仮定であり、それ以上のものではありません。第 5 に、測定の精度が「ボトルネック」の発見にも関係しているという考えも、一般的な仮定に過ぎず、それ以上のものではありません。
Callgrindは回線レベルで機能します。これは良いことです。残念ながら、それは他の問題を共有しています。
目標が (一般的な測定値を取得するのではなく) 「ボトルネック」を見つけることである場合は、Zoomなど、行ごとにパーセントで報告するウォールクロック タイム スタック サンプラーを調べる必要があります。理由は単純ですが、おそらくなじみのないものです。
合計 10 秒かかる一連の関数が相互に呼び出されるプログラムがあるとします。また、プログラム カウンターだけでなく、コール スタック全体をサンプリングするサンプラーもあり、1 秒あたり 100 回などの一定の速度で常にサンプリングを行います。(今のところ、他のプロセスは無視してください。)
したがって、最後にコール スタックの 1000 サンプルが得られます。それらの複数に現れるコード L の任意の行を選択します。その行を回避するか、削除するか、または非常に高速なプロセッサに渡すことにより、何らかの方法でその行を最適化できるとします。
それらのサンプルはどうなりますか?
そのコード行 L は (本質的に) まったく時間がかからないため、サンプルがヒットすることはありません。そのため、これらのサンプルは消えるだけで、サンプルの総数が減り、したがって合計時間が短縮されます! 実際、全体の時間は、L がスタック上にあった時間の割合 (おおよそ、L を含むサンプルの割合) だけ短縮されます。
あまり統計を取りたくないのですが、多くの人は、測定の精度が重要だと考えているため、多くのサンプルが必要だと考えています。これを行っている理由が、高速化のために何を修正するかを見つけることである場合は、そうではありません。重要なのは、それを測定することではなく、何を修正するかを見つけることです。行 L がスタック上にあるのは、時間の一部 F ですよね? つまり、各サンプルには、ヒットする確率 F がありますよね? コインを投げるようなものです。これには、継承の法則と呼ばれる理論があります。(単純化するが一般的な仮定の下で)、コインを N 回投げ、「表」が S 回見られる場合、コイン F の公平性を (平均で) と見積もることができます。したがって、わずか3 つのサンプルを取得し、L がオンになっている場合(S+1)/(N+2)
そのうちの2つ、Fが何であるか知っていますか? もちろん違います。
しかし、平均で (2+1)/(3+2) または60%であることはわかっています。これが、ライン L を「最適化する」ことで (平均して) どれだけの時間を節約できるかということです。もちろん、スタック サンプルは、ライン L (「ボトルネック」**) がどこにあるかを正確に示しています。小数点以下 2 桁または 3 桁まで測定しなかったことは本当に問題でしたか?
ところで、それは上記の他のすべての問題の影響を受けません。
**私は「ボトルネック」を引用し続けます。なぜなら、ほとんどのソフトウェアを遅くする原因はボトルネックとは何の共通点もないからです。より良い比喩は「ドレイン」です。これは、不必要に時間を無駄にするものです。
gprof
のタイミング データは統計的です (詳細については、プロファイリングドキュメントを参照してください)。
一方、実際にすべてのコードを解釈するものをKCacheGrind
使用します。valgrind
したがって、モデル化された CPUが実際の CPU に近い場合KCacheGrind
は、(より多くのオーバーヘッドを犠牲にして) 「より正確」になる可能性があります。valgrind
どちらを選択するかは、処理できるオーバーヘッドの種類によっても異なります。私の経験では、gprof
実行時のオーバーヘッド (実行時間) は少なくなりますが、より邪魔になります (つまり-pg
、関数のすべてにコードを追加します)。したがって、状況に応じて、どちらか一方がより適切です。
「より良い」gprof
データを得るには、コードをより長く実行します (そして、できる限り広い範囲のテスト データに対して実行します)。数が多ければ多いほど、測定値は統計的に優れたものになります。