Linux で実行されている C++ アプリケーションがあり、最適化の過程にあります。コードの実行速度が遅い領域を特定するにはどうすればよいですか?
19 に答える
Valgrindは次のオプションで使用できます
valgrind --tool=callgrind ./(Your binary)
と呼ばれるファイルを生成しますcallgrind.out.x
。その後、kcachegrind
ツールを使用してこのファイルを読み取ることができます。それはあなたに物事のグラフィカルな分析を与え、どの行がどれくらいの費用がかかるかなどの結果をもたらします。
新しいカーネル (最新の Ubuntu カーネルなど) には、新しい「perf」ツール ( apt-get install linux-tools
) AKA perf_eventsが付属しています。
これらには、クラシックなサンプリング プロファイラー (マニュアル ページ) とすばらしいタイムチャートが付属しています。
重要なことは、これらのツールはプロセスのプロファイリングだけでなく、システムのプロファイリングにもなり得るということです。これらのツールは、スレッド、プロセス、およびカーネル間の相互作用を示し、プロセス間のスケジューリングと I/O の依存関係を理解できるようにします。
実行するための答えvalgrind --tool=callgrind
は、いくつかのオプションがないと完全ではありません。通常、Valgrind での 10 分間の遅い起動時間をプロファイルしたくはなく、プログラムが何らかのタスクを実行しているときにプログラムをプロファイルしたいと考えています。
というわけでこれがオススメ。最初にプログラムを実行します:
valgrind --tool=callgrind --dump-instr=yes -v --instr-atstart=no ./binary > tmp
これが機能し、プロファイリングを開始したい場合は、別のウィンドウで実行する必要があります。
callgrind_control -i on
これにより、プロファイリングがオンになります。オフにしてタスク全体を停止するには、次を使用できます。
callgrind_control -k
これで、現在のディレクトリに callgrind.out.* という名前のファイルがいくつかあります。プロファイリング結果を表示するには、次を使用します。
kcachegrind callgrind.out.*
次のウィンドウで「Self」列ヘッダーをクリックすることをお勧めします。そうしないと、「main()」が最も時間のかかるタスクであることがわかります。「Self」は、依存関係を合わせたものではなく、各関数自体にかかった時間を示します。
これはNazgob の Gprof answerへの応答です。
私はここ数日間 Gprof を使用してきましたが、すでに 3 つの重大な制限を発見しました。
回避策を使用しない限り、マルチスレッド コードでは正しく動作しません。
コール グラフは、関数ポインターによって混乱します。例:
multithread()
指定された配列 (両方とも引数として渡される) に対して指定された関数をマルチスレッド化できるようにする関数が呼び出されています。ただし、gprof は、multithread()
子で費やされた時間を計算する目的で、すべての呼び出しを同等と見なします。私が渡す一部の関数は他の関数multithread()
よりもはるかに時間がかかるため、呼び出しグラフはほとんど役に立ちません。(ここでスレッド化が問題かどうか疑問に思っている人へ:いいえ、multithread()
オプションでできます。この場合は、呼び出し元のスレッドでのみすべてを順次実行します)。ここには、「...呼び出し数の数値は、サンプリングではなくカウントによって導出されます。完全に正確です...」と書かれています。それでも、コール グラフが 5345859132+784984078 を、最もよく呼び出される関数への呼び出し統計として示しているのを見つけました。最初の番号は直接呼び出しであり、2 番目の再帰呼び出し (すべてそれ自体からのもの) です。これはバグがあることを暗示していたので、長い (64 ビット) カウンターをコードに入れ、同じ実行を再度行いました。私のカウント: 5345859132 直接、および 78094395406 自己再帰呼び出し。そこには多くの桁があるので、私が測定した再帰呼び出しは 780 億であるのに対して、Gprof からの 784 メートルであると指摘しておきます。100 倍の違いがあります。どちらの実行もシングル スレッドで最適化されていないコードで、一方はコンパイルされ
-g
、もう一方は-pg
.
これは、64 ビットの Debian Lenny で実行されている GNU Gprof (GNU Binutils for Debian) 2.18.0.20080103 でした。
これらは、コードを高速化するために使用する 2 つの方法です。
CPU バウンド アプリケーションの場合:
- DEBUG モードでプロファイラーを使用して、コードの疑わしい部分を特定します
- 次に、RELEASE モードに切り替えて、パフォーマンスに変化が見られるまで、コードの疑わしいセクションをコメントアウトします (何も付けずにスタブします)。
I/O バウンドのアプリケーションの場合:
- RELEASE モードでプロファイラーを使用して、コードの疑わしい部分を特定します。
注意
プロファイラーを持っていない場合は、貧乏人のプロファイラーを使用してください。アプリケーションのデバッグ中に一時停止します。ほとんどの開発者スイートは、コメント付きの行番号でアセンブリに分割されます。統計的に、CPU サイクルのほとんどを消費している地域に到達する可能性があります。
CPU の場合、DEBUGモードでプロファイリングを行う理由は、 RELEASEモードでプロファイリングを試みた場合、コンパイラが数学を削減し、ループをベクトル化し、関数をインライン化するためです。これらの関数は、アセンブル時にコードをマップ不可能な混乱にグロブする傾向があります。マッピングできない混乱は、アセンブリが最適化中のソース コードに対応していない可能性があるため、プロファイラーが何に時間がかかっているかを明確に識別できないことを意味します。RELEASEモードのパフォーマンス (タイミングに敏感など) が必要な場合は、必要に応じてデバッガー機能を無効にして、使用可能なパフォーマンスを維持してください。
I/O バウンドの場合、プロファイラーはRELEASEモードでも I/O 操作を識別できます。これは、I/O 操作が (ほとんどの場合) 共有ライブラリに外部的にリンクされているか、最悪の場合、sys-割り込みベクトルを呼び出します (プロファイラーでも簡単に識別できます)。