Linuxで実行されているC ++コードをプロファイルするにはどうすればよいですか?


1816

Linuxで実行しているC ++アプリケーションがあります。これは最適化の最中です。コードのどの領域の実行が遅いのかを特定するにはどうすればよいですか?


27
開発スタックについてより多くのデータを提供すると、より良い答えが得られる可能性があります。IntelとSunのプロファイラがありますが、コンパイラを使用する必要があります。それはオプションですか?
Nazgob 2008

2
:それは、すでに次のリンクを答えている stackoverflow.com/questions/2497211/...
カピル・グプタ

4
答えのほとんどはcodeプロファイラーです。ただし、優先順位の逆転、キャッシュのエイリアシング、リソースの競合などはすべて、最適化とパフォーマンスの要因となる可能性があります。人々は私の遅いコードに情報を読んだと思います。FAQがこのスレッドを参照しています。
アートレスノイズ2013


3
私はpstackをランダムに使用していましたが、ほとんどの場合、プログラムがほとんどの場合に最も典型的なスタックを出力するため、ボトルネックを示しています。
ホセマヌエルゴメスアルバレス2016

回答:


1406

プロファイラーを使用することが目的の場合は、推奨されているプロファイラーのいずれかを使用してください。

ただし、急いでいて、主観的に遅いときにデバッガーの下でプログラムを手動で中断できる場合は、パフォーマンスの問題を見つける簡単な方法があります。

数回停止するだけで、毎回コールスタックを確認できます。時間の何パーセントか、20%または50%などを浪費しているコードがある場合、それは各サンプルの動作でそれをキャッチする確率です。だから、それはあなたがそれを見るサンプルのおよそのパーセンテージです。教育を受けた当て推量は必要ありません。問題が何であるかについて推測がある場合、これはそれを証明または反証します。

サイズの異なる複数のパフォーマンスの問題がある場合があります。それらのいずれかをクリーンアップすると、残りのパスのパーセンテージが大きくなり、後続のパスで見つけやすくなります。この拡大効果は、複数の問題にまたがって組み合わされると、非常に大きなスピードアップ要因につながる可能性があります。

警告:プログラマは、自分で使用しない限り、この手法に懐疑的です。プロファイラーからこの情報が得られると言われますが、コールスタック全体をサンプリングし、ランダムなサンプルのセットを調べられる場合にのみ当てはまります。(要約は洞察が失われる場所です。)コールグラフは同じ情報を提供しません。

  1. 命令レベルで要約しない、そして
  2. 再帰が存在すると混乱を招きます。

彼らはまた、実際にはどのプログラムでも機能する場合、おもちゃのプログラムでのみ機能することを示します。また、より大きなプログラムでより効果的に機能するようです。彼らはそれが問題ではないものを見つけることが時々あると言いますが、それはあなたが一度何かを見た場合にのみ当てはまります。複数のサンプルに問題がある場合、それは本当です。

PSこれは、Javaのように、ある時点でスレッドプールのコールスタックサンプルを収集する方法がある場合、マルチスレッドプログラムでも実行できます。

PPSおおまかな一般性として、ソフトウェアに抽象化の層が多ければ多いほど、それがパフォーマンスの問題の原因であることに気づく可能性が高くなります(そして高速化する機会が得られます)。

追加されました:明白ではないかもしれませんが、スタックサンプリングテクニックは、再帰が存在する場合でも同様に機能します。その理由は、命令の削除によって節約される時間は、サンプル内で発生する可能性のある回数に関係なく、命令を含むサンプルの割合によって概算されるためです。

私がよく聞く別の異論は、「どこかでランダムに停止し、本当の問題を見逃す」です。これは、実際の問題が何であるかについての事前の概念を持っていることに由来します。パフォーマンスの問題の主要な特性は、期待に反することです。サンプリングは何かが問題であることを示し、あなたの最初の反応は不信です。それは当たり前のことですが、それが本当の問題を見つけた場合は確信が持て、逆もまた同様です。

追加:それがどのように機能するかのベイジアン説明をさせてください。I呼び出しスタックfにある時間の一部の命令(呼び出しまたはその他)があるとします(したがって、その分コストがかかります)。簡単にするために、私たちは何を知らないと仮定しますfは、0.1、0.2、0.3、... 0.9、1.0のいずれかであり、これらの各可能性の事前確率は0.1であるため、これらのすべてのコストは等しく可能性がありますアプリオリ。

次に、2つのスタックサンプルのみを取得Iし、両方のサンプルについての説明をObservationと表示するとしo=2/2ます。これは私たちに、周波数の新しい推計与えfのをIこれによると、:

Prior                                    
P(f=x) x  P(o=2/2|f=x) P(o=2/2&&f=x)  P(o=2/2&&f >= x)  P(f >= x | o=2/2)

0.1    1     1             0.1          0.1            0.25974026
0.1    0.9   0.81          0.081        0.181          0.47012987
0.1    0.8   0.64          0.064        0.245          0.636363636
0.1    0.7   0.49          0.049        0.294          0.763636364
0.1    0.6   0.36          0.036        0.33           0.857142857
0.1    0.5   0.25          0.025        0.355          0.922077922
0.1    0.4   0.16          0.016        0.371          0.963636364
0.1    0.3   0.09          0.009        0.38           0.987012987
0.1    0.2   0.04          0.004        0.384          0.997402597
0.1    0.1   0.01          0.001        0.385          1

                  P(o=2/2) 0.385                

最後の列は、たとえば、 f 0.5以上の、以前の仮定である60%から92%であることを示しています。

以前の仮定が異なると仮定します。P(f=0.1).991(ほぼ確実)であり、他のすべての可能性はほぼ不可能(0.001)であると想定します。言い換えれば、私たちの事前の確実性はそれIが安いということです。それから私達は得る:

Prior                                    
P(f=x) x  P(o=2/2|f=x) P(o=2/2&& f=x)  P(o=2/2&&f >= x)  P(f >= x | o=2/2)

0.001  1    1              0.001        0.001          0.072727273
0.001  0.9  0.81           0.00081      0.00181        0.131636364
0.001  0.8  0.64           0.00064      0.00245        0.178181818
0.001  0.7  0.49           0.00049      0.00294        0.213818182
0.001  0.6  0.36           0.00036      0.0033         0.24
0.001  0.5  0.25           0.00025      0.00355        0.258181818
0.001  0.4  0.16           0.00016      0.00371        0.269818182
0.001  0.3  0.09           0.00009      0.0038         0.276363636
0.001  0.2  0.04           0.00004      0.00384        0.279272727
0.991  0.1  0.01           0.00991      0.01375        1

                  P(o=2/2) 0.01375                

現在ではP(f >= 0.5)、以前の想定であった0.6%から26%増加しています。ベイズでは、の推定コストの見積もりを更新できますI。データの量が少ない場合、コストは正確にはわかりません。修正するだけの十分な大きさがあるということだけです。

それを見る別の方法は、継承ルールと呼ばれます。コインを2回裏返して、両方とも表に出た場合、コインの予想される重み付けについて何がわかりますか?尊敬される答えは、それが平均値を持つベータ分布であると言うことです(number of hits + 1) / (number of tries + 2) = (2+1)/(2+2) = 75%

(重要なのは、I2回以上表示されることです。1回しか表示されない場合、f> 0 以外はあまりわかりません。)

そのため、ごく少数のサンプルでも、表示される指示のコストについて多くを知ることができます。(そして、それは場合。そのコストに比例し、平均で、頻度でそれらを見ることができますnサンプルが取られ、そしてfコストで、その後Iに表示されますnf+/-sqrt(nf(1-f))サンプル。例、n=10f=0.3、つまり3+/-1.4、サンプル)。


追加:測定とランダムスタックサンプリングの違いを直感的に理解できるようにするために:実
時間でさえ、スタックをサンプリングするプロファイラーがありますが、出てくるのは測定(またはホットパス、またはホットスポット)です「ボトルネック」は簡単に隠すことができます)。彼らがあなたに見せていない(そして彼らは簡単にそうすることができた)のは実際のサンプルそのものです。そして、あなたの目標がボトルネックを見つけることであるならば、あなたが見る必要があるそれらの数は、平均して、それを要する時間の割合で割った2です。したがって、30%の時間がかかる場合、平均して2 / .3 = 6.7サンプルで表示され、20サンプルで表示される可能性は99.2%です。

これは、測定値の調査とスタックサンプルの調査の違いを示した、わかりやすいイラストです。ボトルネックは、このような1つの大きなblobでも、多数の小さなblobでもかまいません。違いはありません。

ここに画像の説明を入力してください

測定は水平です。特定のルーチンにかかる時間の割合がわかります。サンプリングは垂直です。その時点でプログラム全体が実行していることを回避する方法があり、2番目のサンプルでそれを確認した場合、ボトルネックが見つかりました。それが違いを生む理由です。どれだけ時間がかかっているのかではなく、費やされた時間の全体的な理由を見ることです。


292
これは基本的に貧乏人のサンプリングプロファイラーであり、すばらしいですが、サンプルサイズが小さすぎて完全に偽の結果が得られる可能性があります。
Crashworks、

100
@Crash:「貧乏人」の部分については議論しません:-)統計測定の精度には多くのサンプルが必要ですが、測定と問題の場所という2つの相反する目標があります。私は後者に焦点を合わせています。後者には、測定の精度ではなく、位置の精度が必要です。したがって、たとえば、スタックの途中で、単一の関数呼び出しA()が存在する可能性があります。これは時間の50%を占めますが、コストのかからないA()への他の多くの呼び出しと共に、別の大きな関数Bに含まれる可能性があります。関数時間の正確な要約は手掛かりになる可能性がありますが、他のすべてのスタックサンプルは問題を正確に示します。
マイクダンレービー、2009年

41
...世界は、コールカウントや平均タイミングで注釈が付けられたコールグラフで十分だと考えているようです。そうではない。そして悲しい部分は、コールスタックをサンプリングする人にとって、最も有用な情報は彼らの目の前にありますが、「統計」の利益のためにそれを捨てます。
Mike Dunlavey、2009年

30
私はあなたのテクニックに反対するつもりはありません。明らかに、私はスタックウォーキングサンプリングプロファイラーに大きく依存しています。今、自動化されたツールがいくつかあることを指摘しています。これは、25%から15%に関数を取得するポイントを超え、1.2%から0.6%。
Crashworks、

13
-1:端正なアイデアですが、中程度のパフォーマンス志向の環境でも仕事にお金を払っている場合、これは全員の時間の無駄です。実際のプロファイラーを使用して、実際に問題を修正する必要がないようにします。
サムハーウェル2010

583

次のオプションでValgrindを使用できます

valgrind --tool=callgrind ./(Your binary)

というファイルが生成されcallgrind.out.xます。その後、kcachegrindツールを使用してこのファイルを読み取ることができます。それはあなたに物事のグラフィカルな分析を与え、どのラインがどれくらいの費用がかかるかのような結果をもたらします。


51
valgrindはすばらしいですが、プログラムが遅くなることに注意してください
neves

30
出力を視覚化するための驚くべき代替方法については、Gprof2Dotも確認してください。./gprof2dot.py -f callgrind callgrind.out.x | dot -Tsvg -o output.svg
セバスチャン

2
@nevesはいValgrindは、「gstreamer」および「opencv」アプリケーションをリアルタイムでプロファイリングする速度に関しては、あまり役に立ちません。
enthusiasticgeek

1
stackoverflow.com/questions/375913/…は、速度の問題の部分的な解決策です。
トヌ・サミュエル

3
@Sebastian:gprof2dotis now here:github.com/jrfonseca/gprof2dot
John Zwinck

348

GCCを使用していると思います。標準的な解決策は、gprofでプロファイルすることです。

-pgプロファイリングの前に必ずコンパイルに追加してください。

cc -o myprog myprog.c utils.c -g -pg

まだ試していませんが、google-perftoolsについて良いことは聞いています。それは間違いなく試してみる価値があります。

関連質問はこちら

Valgrind、Intel VTune、Sun DTraceの場合、他の流行語がうまくgprof機能しません。


3
gprofが現在の標準であることに同意します。ただし、Valgrindはメモリリークやプログラムの他のメモリ関連の側面をプロファイルするために使用され、速度の最適化のためではありません。
リザードに請求する

68
ビル、vaglrindスイートでは、callgrindとmassifを見つけることができます。どちらもアプリのプロファイルに非常に便利です
dario minonne 2008

7
@ビル- -トカゲ:上のいくつかのコメントは、gprofstackoverflow.com/questions/1777556/alternatives-to-gprof/...
マイクDunlavey

6
gprof -pgは、コールスタックプロファイリングの近似にすぎません。mcount呼び出しを挿入して、どの関数が他のどの関数を呼び出しているかを追跡します。ええと、標準的な時間ベースのサンプリングを使用しています。次に、関数foo()でサンプリングされた時間を、呼び出しの番号付けに比例して、foo()の呼び出し元に割り当てます。したがって、異なるコストの呼び出しを区別しません。
Krazy Glew

1
clang / clang ++では、gperftoolsのCPUプロファイラーの使用を検討する場合があります。警告:自分で行ったことはありません。
einpoklum

257

新しいカーネル(たとえば、最新のUbuntuカーネル)には、新しい「perf」ツール(apt-get install linux-tools)、別名perf_eventsが付属しています。

これらには、クラシックなサンプリングプロファイラー(マンページ)と素晴らしいタイムチャートが付属しています

重要なことは、これらのツールはプロセスプロファイリングだけでなく、システムプロファイリングにもなり得ます。これらは、スレッド、プロセス、およびカーネル間の相互作用を示し、プロセス間のスケジューリングとI / Oの依存関係を理解させます。

代替テキスト


12
素晴らしいツールです!とにかく、「main-> func1-> fun2」スタイルから始まる典型的な「バタフライ」ビューを取得する方法はありますか?私はそれを理解することができないようです... perf report呼び出しの親を持つ関数名を私に与えているようです...(それは一種の逆バタフライビューです)
kizzx2

Will、perfはスレッドアクティビティのタイムチャートを表示できます。CPU番号情報が追加された?すべてのCPUでいつどのスレッドが実行されているかを確認したい。
osgx

2
@ kizzx2 -あなたが使用することができるgprof2dotperf script。とても良いツールです!
12

2
4.13のようなさらに新しいカーネルには、プロファイリング用のeBPFがあります。brendangregg.com/blog/2015-05-15/ebpf-one-small-step.htmlおよびbrendangregg.com/ebpf.html
Andrew Stern

別の素晴らしい紹介がperfに存在するarchive.li/9r927#selection-767.126-767.271 (SOの神々は、SOの知識ベースからそのページを削除することを決めたのはなぜ....私を超えている)
ragerdl

75

私はプロファイリングツールスイートのベースとしてValgrindとCallgrindを使用します。知っておくべき重要なことは、Valgrindは基本的に仮想マシンであることです。

(wikipedia)Valgrindは本質的に、動的再コンパイルを含むジャストインタイム(JIT)コンパイル技術を使用する仮想マシンです。元のプログラムから何もホストプロセッサで直接実行されることはありません。代わりに、Valgrindは最初にプログラムを、中間表現(IR)と呼ばれる一時的で単純な形式に変換します。これは、プロセッサに依存しない、SSAベースの形式です。変換後、ValgrindがIRをマシンコードに変換して戻し、ホストプロセッサで実行できるようになる前に、ツール(以下を参照)はIRで必要な変換を自由に実行できます。

Callgrindは、その上に構築されたプロファイラーです。主な利点は、信頼できる結果を得るために何時間もアプリケーションを実行する必要がないことです。Callgrindは非プロービングプロファイラーであるため、1秒の実行でさえ、確実で信頼できる結果を得るには十分です。

Valgrindに基づくもう1つのツールはMassifです。ヒープメモリ使用量のプロファイルに使用します。それは素晴らしい働きをします。それが行うことは、メモリ使用量のスナップショットを提供することです-詳細情報WHATはメモリのWHATパーセンテージを保持し、WHOはそこに配置しました。このような情報は、アプリケーション実行のさまざまな時点で利用できます。


70

実行する答え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()」が最も時間がかかるタスクであることを示しています。「自己」は、各機能自体が依存関係と一緒にではなく、どれだけの時間を費やしたかを示します。


9
何らかの理由で、callgrind.out。*ファイルは常に空でした。callgrind_control -dの実行は、データをディスクに強制的にダンプするのに役立ちました。
トヌ・サミュエル

3
できません。私の通常のコンテキストは、MySQL全体やPHPのようなものか、それに似た大きなものです。最初は何を分離したいのかわからないこともしばしばです。
–TõnuSamuel 2015

2
または、私の場合、プログラムは実際に大量のデータをLRUキャッシュにロードしますが、それをプロファイリングしたくありません。そのため、起動時にキャッシュのサブセットを強制的にロードし、そのデータのみを使用してコードをプロファイリングします(OS + CPUにキャッシュ内のメモリ使用を管理させます)。それは機能しますが、そのキャッシュのロードは遅く、異なるコンテキストでプロファイルしようとしているコード全体でCPUを集中的に使用するため、callgrindはひどく汚染された結果を生成します。
Code Abominator 2016年

2
CALLGRIND_TOGGLE_COLLECTプログラムで収集を有効/無効にすることもできます。stackoverflow.com/a/13700817/288875を
Andre Holzner、2017

1
うわー、これが存在することを知りませんでした、ありがとう!
Vincent Fourmond

59

これは、NazgobのGprof回答に対する応答です。

私は過去2日間にGprofを使用しており、すでに3つの重要な制限が見つかりました。

  1. 回避策を使用しない限り、マルチスレッドコードでは正しく機能しません。

  2. 呼び出しグラフは、関数ポインターによって混乱します。例:私は、multithread()指定された配列(両方とも引数として渡されます)に対して指定された関数をマルチスレッド化できるようにする関数を呼び出しました。ただし、Gprofは、multithread()子供たちの時間を計算するために、すべての呼び出しを同等と見なしています。一部の関数は他の関数multithread()よりも時間がかかるため、コールグラフはほとんど役に立ちません。(ここでスレッド化が問題かどうか疑問に思う人には、いいえ、multithread()オプションで可能で、この場合は、呼び出しスレッドでのみすべてを順番に実行します)。

  3. それは言うここで、「サンプリングしていない、...数の-通話数値は計数により派生しています。彼らは完全に正確である...」ということ。それでも、私のコールグラフでは、5345859132 + 784984078が私の最も呼び出された関数の呼び出し統計として得られます。最初の番号は直接呼び出しで、2番目の再帰呼び出し(すべてそれ自体)です。これはバグがあることを意味していたので、コードに長い(64ビット)カウンターを挿入し、同じようにもう一度実行しました。私のカウント:5345859132直接、および78094395406自己再帰呼び出し。桁数が多いので、測定した再帰呼び出しが78bnであるのに対し、Gprofからの784mは100倍異なることを指摘しておきます。どちらの実行も、1つはコンパイルさ-gれ、もう1つはシングルスレッドの最適化されていないコード-pgでした。

これは、64ビットのDebian Lennyで動作するGNU Gprof(GNU Binutils for Debian)2.18.0.20080103でした。


はい、サンプリングは行いますが、呼び出し回数の数値は対象外です。興味深いことに、あなたのリンクをたどると、最終的に私が投稿にリンクしたマニュアルページの更新版に移動しました。新しいURL:sourceware.org/binutils/docs/gprof/… これは私の回答のパート(iii)の引用を繰り返します。しかし、「マルチスレッドアプリケーション、またはマルチスレッドライブラリとリンクするシングルスレッドアプリケーションでは、カウント関数がスレッドセーフである場合にのみカウントが決定的です。(注:glibcのmcountカウント関数はスレッドではないことに注意してください。 -安全)。"
Rob_before_edits

これが(iii)の私の結果を説明するものかどうかは、はっきりしません。私のコードは-lpthread -lmにリンクされ、シングルスレッドで実行されている場合でも、 "pthread_t * thr"と "pthread_mutex_t nextLock = PTHREAD_MUTEX_INITIALIZER"静的変数の両方を宣言しました。私は通常、「マルチスレッドライブラリとリンクする」とは、実際にそれらのライブラリを使用することを意味し、これよりも範囲が広いと思いますが、私は間違っている可能性があります。
Rob_before_edits

23

Valgrind、callgrind、kcachegrindを使用します。

valgrind --tool=callgrind ./(Your binary)

callgrind.out.xを生成します。kcachegrindを使用して読んでください。

gprofを使用(-pgを追加):

cc -o myprog myprog.c utils.c -g -pg 

(マルチスレッド、関数ポインターにはあまり適していません)

google-perftoolsを使用します。

時間サンプリングを使用して、I / OとCPUのボトルネックが明らかになりました。

インテルVTuneは最高です(教育目的で無料)。

その他: AMD Codeanalyst(AMD CodeXLに置き換えられたため)、OProfile、 'perf'ツール(apt-get install linux-tools)


10

C ++プロファイリング技術の調査

この回答では、いくつかの異なるツールを使用して、いくつかの非常に単純なテストプログラムを分析し、それらのツールの動作を具体的に比較します。

次のテストプログラムは非常に単純で、次のことを行います。

  • main呼び出しfastmaybe_slow3回、maybe_slow呼び出しの1つが遅い

    の遅い呼び出しmaybe_slowは10倍長く、子関数の呼び出しを考慮するとランタイムを支配しますcommon。理想的には、プロファイリングツールが特定の遅い呼び出しを示すことができるようになります。

  • プログラム実行の大部分を占めるfastmaybe_slow呼び出しの両方common

  • プログラムインターフェイスは次のとおりです。

    ./main.out [n [seed]]

    そしてプログラムはO(n^2)合計でループします。seedランタイムに影響を与えずに異なる出力を取得するだけです。

main.c

#include <inttypes.h>
#include <stdio.h>
#include <stdlib.h>

uint64_t __attribute__ ((noinline)) common(uint64_t n, uint64_t seed) {
    for (uint64_t i = 0; i < n; ++i) {
        seed = (seed * seed) - (3 * seed) + 1;
    }
    return seed;
}

uint64_t __attribute__ ((noinline)) fast(uint64_t n, uint64_t seed) {
    uint64_t max = (n / 10) + 1;
    for (uint64_t i = 0; i < max; ++i) {
        seed = common(n, (seed * seed) - (3 * seed) + 1);
    }
    return seed;
}

uint64_t __attribute__ ((noinline)) maybe_slow(uint64_t n, uint64_t seed, int is_slow) {
    uint64_t max = n;
    if (is_slow) {
        max *= 10;
    }
    for (uint64_t i = 0; i < max; ++i) {
        seed = common(n, (seed * seed) - (3 * seed) + 1);
    }
    return seed;
}

int main(int argc, char **argv) {
    uint64_t n, seed;
    if (argc > 1) {
        n = strtoll(argv[1], NULL, 0);
    } else {
        n = 1;
    }
    if (argc > 2) {
        seed = strtoll(argv[2], NULL, 0);
    } else {
        seed = 0;
    }
    seed += maybe_slow(n, seed, 0);
    seed += fast(n, seed);
    seed += maybe_slow(n, seed, 1);
    seed += fast(n, seed);
    seed += maybe_slow(n, seed, 0);
    seed += fast(n, seed);
    printf("%" PRIX64 "\n", seed);
    return EXIT_SUCCESS;
}

gprof

gprofは、ソフトウェアを計装で再コンパイルする必要があり、また、その計装とともにサンプリングアプローチを使用します。したがって、精度(サンプリングは常に完全に正確であるとは限らず、関数をスキップできる)と実行速度の低下(計装とサンプリングは実行速度をあまり低下させない比較的高速な手法)の間でバランスを取ります。

gprofはGCC / binutilsに組み込まれ-pgているため、gprofを有効にするオプションを指定してコンパイルするだけで済みます。次に、通常は数秒(10000)の妥当な期間の実行を生成するサイズCLIパラメーターを使用してプログラムを実行します。

gcc -pg -ggdb3 -O3 -std=c99 -Wall -Wextra -pedantic -o main.out main.c
time ./main.out 10000

教育上の理由から、最適化を有効にせずに実行も行います。通常、最適化されたプログラムのパフォーマンスの最適化のみに関心があるため、これは実際には役に立たないことに注意してください。

gcc -pg -ggdb3 -O0 -std=c99 -Wall -Wextra -pedantic -o main.out main.c
./main.out 10000

最初に、time実行時間と実行時間-pgは同じだったと言っています。ただし、たとえばこのチケットに示されているように、複雑なソフトウェアの2倍から3倍の速度低下の説明を見たことがある。

でコンパイルしたため-pg、プログラムを実行すると、gmon.outプロファイリングデータを含むファイルファイルが生成されます。

次のgprof2dot質問で、そのファイルをグラフィカルに観察できます。gprofの結果をグラフィカルに表示することはできますか?

sudo apt install graphviz
python3 -m pip install --user gprof2dot
gprof main.out > main.gprof
gprof2dot < main.gprof | dot -Tsvg -o output.svg

ここで、gprofツールはgmon.outトレース情報を読み取り、人間が読めるレポートをで生成します。レポートはmain.gprofgprof2dotグラフを生成するために読み取られます。

gprof2dotのソース:https : //github.com/jrfonseca/gprof2dot

-O0実行時に次のことが観察されます。

ここに画像の説明を入力してください

そして-O3実行のために:

ここに画像の説明を入力してください

-O0出力はかなり自明です。たとえば、3つのmaybe_slow呼び出しとその子呼び出しが合計実行時間の97.56%を占めることを示していますが、子maybe_slowなしでそれ自体を実行すると、合計実行時間の0.00%を占めます。つまり、その関数で費やされた時間のほとんどすべてが子が呼び出します。

TODO:GDBで見ることができるのにmain、なぜ-O3出力から欠落しているのbtですか?GProfの出力から関数が欠落しているのは、gprofがコンパイルされたインストルメンテーションに加えてサンプリングベースであるためだと思い-O3 mainます。高速すぎてサンプルがありません。

PNGではなくSVG出力を選択します。SVGはCtrl + Fで検索可能であり、ファイルサイズは約10倍小さくできるためです。また、生成された画像の幅と高さは、複雑なソフトウェアでは数万ピクセルeogと非常に不自然で、PNGの場合はGNOME 3.28.1でバグが発生しますが、SVGはブラウザーによって自動的に開かれます。ただし、gimp 2.8はうまく機能しました。以下も参照してください。

しかし、それでも、画像をたくさんドラッグして必要なものを見つけます。たとえば、このチケットから取得した「実際の」ソフトウェアの例からこの画像を参照してください。

ここに画像の説明を入力してください

並べ替えられていない小さなすべてのスパゲッティラインが互いに重なり合うことで、最も重要なコールスタックを簡単に見つけることができますか?dot確かにもっと良いオプションがあるかもしれませんが、今はそこに行きたくありません。私たちが本当に必要としているのは、そのための適切な専用ビューアですが、まだ見つかりません。

ただし、カラーマップを使用して、これらの問題を少し軽減することができます。たとえば、前の巨大な画像で、緑が赤の後に続き、最後に濃い青が続くという見事な演繹を行ったとき、私はようやく左側のクリティカルパスを見つけることができました。

または、gprof以前に保存した組み込みのbinutilsツールのテキスト出力を確認することもできます。

cat main.gprof

デフォルトでは、これにより、出力データの意味を説明する非常に詳細な出力が生成されます。それ以上は説明できないので、自分で読んでもらいましょう。

データ出力形式を理解したら、冗長性を減らして、-bオプションなしのチュートリアルなしでデータのみを表示できます。

gprof -b main.out

この例では、出力は次のものでした-O0

Flat profile:

Each sample counts as 0.01 seconds.
  %   cumulative   self              self     total           
 time   seconds   seconds    calls   s/call   s/call  name    
100.35      3.67     3.67   123003     0.00     0.00  common
  0.00      3.67     0.00        3     0.00     0.03  fast
  0.00      3.67     0.00        3     0.00     1.19  maybe_slow

            Call graph


granularity: each sample hit covers 2 byte(s) for 0.27% of 3.67 seconds

index % time    self  children    called     name
                0.09    0.00    3003/123003      fast [4]
                3.58    0.00  120000/123003      maybe_slow [3]
[1]    100.0    3.67    0.00  123003         common [1]
-----------------------------------------------
                                                 <spontaneous>
[2]    100.0    0.00    3.67                 main [2]
                0.00    3.58       3/3           maybe_slow [3]
                0.00    0.09       3/3           fast [4]
-----------------------------------------------
                0.00    3.58       3/3           main [2]
[3]     97.6    0.00    3.58       3         maybe_slow [3]
                3.58    0.00  120000/123003      common [1]
-----------------------------------------------
                0.00    0.09       3/3           main [2]
[4]      2.4    0.00    0.09       3         fast [4]
                0.09    0.00    3003/123003      common [1]
-----------------------------------------------

Index by function name

   [1] common                  [4] fast                    [3] maybe_slow

そしてのために-O3

Flat profile:

Each sample counts as 0.01 seconds.
  %   cumulative   self              self     total           
 time   seconds   seconds    calls  us/call  us/call  name    
100.52      1.84     1.84   123003    14.96    14.96  common

            Call graph


granularity: each sample hit covers 2 byte(s) for 0.54% of 1.84 seconds

index % time    self  children    called     name
                0.04    0.00    3003/123003      fast [3]
                1.79    0.00  120000/123003      maybe_slow [2]
[1]    100.0    1.84    0.00  123003         common [1]
-----------------------------------------------
                                                 <spontaneous>
[2]     97.6    0.00    1.79                 maybe_slow [2]
                1.79    0.00  120000/123003      common [1]
-----------------------------------------------
                                                 <spontaneous>
[3]      2.4    0.00    0.04                 fast [3]
                0.04    0.00    3003/123003      common [1]
-----------------------------------------------

Index by function name

   [1] common

各セクションの非常に簡単な要約として:

                0.00    3.58       3/3           main [2]
[3]     97.6    0.00    3.58       3         maybe_slow [3]
                3.58    0.00  120000/123003      common [1]

インデントされたままの関数を中心に配置します(maybe_flow)。[3]その関数のIDです。関数の上に呼び出し元があり、その下に呼び出し先があります。

については-O3、ここではグラフィカル出力のように、既知の親がないことmaybe_slowを参照してくださいfast。これは、ドキュメントに書かれているとおりです<spontaneous>

gprofで行ごとのプロファイリングを行う良い方法があるかどうかはわかりません特定のコード行で費やされた `gprof`時間

valgrind callgrind

valgrindは、valgrind仮想マシンを介してプログラムを実行します。これにより、プロファイリングは非常に正確になりますが、プログラムの速度が非常に遅くなります。また、以前にkcachegrindについて言及しました:コードの絵の関数呼び出しグラフを取得するツール

callgrindは、コードをプロファイルするvalgrindのツールであり、kcachegrindは、cachegrind出力を視覚化できるKDEプログラムです。

最初に-pg、通常のコンパイルに戻るにはフラグを削除する必要がProfiling timer expiredあります。そうしないと、実行は実際にで失敗します。そうです、これは非常に一般的であり、スタックオーバーフローの質問がありました。

したがって、次のようにコンパイルして実行します。

sudo apt install kcachegrind valgrind
gcc -ggdb3 -O3 -std=c99 -Wall -Wextra -pedantic -o main.out main.c
time valgrind --tool=callgrind valgrind --dump-instr=yes \
  --collect-jumps=yes ./main.out 10000

--dump-instr=yes --collect-jumps=yesこれは、比較的小さな追加のオーバーヘッドコストで、アセンブリラインごとのパフォーマンスの内訳を表示できる情報もダンプするので、有効にします。

バット、timeプログラムを実行するために29.5秒かかったので、私たちは、この例の15倍程度の減速を持っていたことを教えてくれる。明らかに、このスローダウンは、より大きなワークロードにとって深刻な制限となるでしょう。ここ言及した「実際のソフトウェアの例」では、80倍の速度低下が見られました

実行すると、callgrind.out.<pid>たとえばcallgrind.out.8554私の場合はという名前のプロファイルデータファイルが生成されます。そのファイルは次のように表示されます。

kcachegrind callgrind.out.8554

これは、テキストのgprof出力に類似したデータを含むGUIを示しています。

ここに画像の説明を入力してください

また、右下の「コールグラフ」タブに移動すると、右クリックしてエクスポートできるコールグラフが表示され、次の画像に無理な量の白い境界線が表示されます:-)

ここに画像の説明を入力してください

fastkcachegrindは視覚化を単純化する必要があるため、そのグラフに表示されていないと思います。これは、呼び出しにかかる時間が少なすぎるためです。これは、実際のプログラムで必要な動作になる可能性があります。右クリックメニューには、そのようなノードをカリングするタイミングを制御するためのいくつかの設定がありますが、すばやく試行した後、そのような短い呼び出しを表示することができませんでした。fast左側のウィンドウをクリックすると、で呼び出しグラフが表示されるfastため、スタックが実際にキャプチャされました。完全なグラフ呼び出しグラフを表示する方法はまだ誰も見つけていませんでした:callgrindにすべての関数呼び出しをkcachegrind呼び出しグラフに表示させる

複雑なC ++ソフトウェアでTODOを実行すると、typeのエントリがいくつか表示されます<cycle N>。たとえば<cycle 11>、関数名を予期している場合、それはどういう意味ですか?オンとオフを切り替える「サイクル検出」ボタンがあることに気づきましたが、どういう意味ですか?

perf から linux-tools

perfLinuxカーネルサンプリングメカニズムのみを使用しているようです。これにより、セットアップが非常に簡単になりますが、完全に正確ではありません。

sudo apt install linux-tools
time perf record -g ./main.out 10000

これにより、実行に0.2秒が追加されたので、時間的には問題ありませんcommonが、キーボードの右矢印でノードを展開した後は、あまり関心がありません。

Samples: 7K of event 'cycles:uppp', Event count (approx.): 6228527608     
  Children      Self  Command   Shared Object     Symbol                  
-   99.98%    99.88%  main.out  main.out          [.] common              
     common                                                               
     0.11%     0.11%  main.out  [kernel]          [k] 0xffffffff8a6009e7  
     0.01%     0.01%  main.out  [kernel]          [k] 0xffffffff8a600158  
     0.01%     0.00%  main.out  [unknown]         [k] 0x0000000000000040  
     0.01%     0.00%  main.out  ld-2.27.so        [.] _dl_sysdep_start    
     0.01%     0.00%  main.out  ld-2.27.so        [.] dl_main             
     0.01%     0.00%  main.out  ld-2.27.so        [.] mprotect            
     0.01%     0.00%  main.out  ld-2.27.so        [.] _dl_map_object      
     0.01%     0.00%  main.out  ld-2.27.so        [.] _xstat              
     0.00%     0.00%  main.out  ld-2.27.so        [.] __GI___tunables_init
     0.00%     0.00%  main.out  [unknown]         [.] 0x2f3d4f4944555453  
     0.00%     0.00%  main.out  [unknown]         [.] 0x00007fff3cfc57ac  
     0.00%     0.00%  main.out  ld-2.27.so        [.] _start              

それで、-O0プログラムをベンチマークして、それが何かを示しているかどうかを確認しようとしましたが、ついに、ようやくコールグラフが表示されるようになりました。

Samples: 15K of event 'cycles:uppp', Event count (approx.): 12438962281   
  Children      Self  Command   Shared Object     Symbol                  
+   99.99%     0.00%  main.out  [unknown]         [.] 0x04be258d4c544155  
+   99.99%     0.00%  main.out  libc-2.27.so      [.] __libc_start_main   
-   99.99%     0.00%  main.out  main.out          [.] main                
   - main                                                                 
      - 97.54% maybe_slow                                                 
           common                                                         
      - 2.45% fast                                                        
           common                                                         
+   99.96%    99.85%  main.out  main.out          [.] common              
+   97.54%     0.03%  main.out  main.out          [.] maybe_slow          
+    2.45%     0.00%  main.out  main.out          [.] fast                
     0.11%     0.11%  main.out  [kernel]          [k] 0xffffffff8a6009e7  
     0.00%     0.00%  main.out  [unknown]         [k] 0x0000000000000040  
     0.00%     0.00%  main.out  ld-2.27.so        [.] _dl_sysdep_start    
     0.00%     0.00%  main.out  ld-2.27.so        [.] dl_main             
     0.00%     0.00%  main.out  ld-2.27.so        [.] _dl_lookup_symbol_x 
     0.00%     0.00%  main.out  [kernel]          [k] 0xffffffff8a600158  
     0.00%     0.00%  main.out  ld-2.27.so        [.] mmap64              
     0.00%     0.00%  main.out  ld-2.27.so        [.] _dl_map_object      
     0.00%     0.00%  main.out  ld-2.27.so        [.] __GI___tunables_init
     0.00%     0.00%  main.out  [unknown]         [.] 0x552e53555f6e653d  
     0.00%     0.00%  main.out  [unknown]         [.] 0x00007ffe1cf20fdb  
     0.00%     0.00%  main.out  ld-2.27.so        [.] _start              

TODO:-O3実行時に何が起こりましたか?それは単にそれでmaybe_slowあり、fast速すぎてサンプルを取得しませんでしたか?-O3実行に時間がかかる大きなプログラムでもうまく機能しますか?一部のCLIオプションを見逃しましたか?-Fヘルツでサンプル周波数を制御しようとしましたが、デフォルトで許可されている最大値-F 39500(で増加可能sudo)まで上げましたが、明確な呼び出しが表示されません。

perfすばらしい点の1つは、Brendan GreggのFlameGraphツールです。これにより、コールスタックのタイミングが非常にきちんと表示され、大きなコールをすばやく確認できます。このツールは、https//github.com/brendangregg/FlameGraphで入手できます。また、彼のperfチュートリアルでも言及されています。http://www.brendangregg.com/perf.html#FlameGraphs perfなしで実行するsudoERROR: No stack counts found、今私はそれでやりますsudo

git clone https://github.com/brendangregg/FlameGraph
sudo perf record -F 99 -g -o perf_with_stack.data ./main.out 10000
sudo perf script -i perf_with_stack.data | FlameGraph/stackcollapse-perf.pl | FlameGraph/flamegraph.pl > flamegraph.svg

しかし、このような単純なプログラムでは、グラフmaybe_slowfastグラフも簡単に確認できないため、出力を理解するのは簡単ではありません。

ここに画像の説明を入力してください

より複雑な例では、グラフの意味が明らかになります。

ここに画像の説明を入力してください

TODO [unknown]その例には関数のログがありますが、それはなぜですか?

価値があると思われる別のパフォーマンスGUIインターフェイスには、次のものがあります。

  • Eclipse Trace Compassプラグイン:https : //www.eclipse.org/tracecompass/

    ただし、これには、最初にデータをCommon Trace Formatに変換する必要があるという欠点があります。これは、で実行できますがperf data --to-ctf、ビルド時に有効にする必要があります/ perf十分に新しくする必要があります。どちらも、 Ubuntu 18.04

  • https://github.com/KDAB/hotspot

    これの欠点は、Ubuntuパッケージがないように見えることであり、Ubuntu 18.04がQt 5.9であるのに、ビルドにはQt 5.10が必要です。

gperftools

以前は「Google Performance Tools」と呼ばれていました。ソース:https : //github.com/gperftools/gperftoolsサンプルベース。

最初にgperftoolsをインストールします:

sudo apt install google-perftools

次に、実行時とビルド時の2つの方法でgperftools CPUプロファイラーを有効にできます。

実行時に、をLD_PRELOAD指すようにsetを渡すlibprofiler.so必要があります。これはlocate libprofiler.so、たとえば、私のシステムで、で見つけることができます。

gcc -ggdb3 -O3 -std=c99 -Wall -Wextra -pedantic -o main.out main.c
LD_PRELOAD=/usr/lib/x86_64-linux-gnu/libprofiler.so \
  CPUPROFILE=prof.out ./main.out 10000

または、リンク時にライブラリをビルドして、LD_PRELOAD実行時にパスをディスペンスすることもできます。

gcc -Wl,--no-as-needed,-lprofiler,--as-needed -ggdb3 -O3 -std=c99 -Wall -Wextra -pedantic -o main.out main.c
CPUPROFILE=prof.out ./main.out 10000

参照:gperftools-ダンプされないプロファイルファイル

これまでに見つけたこのデータを表示する最も良い方法は、pprofの出力をkcachegrindが入力として取るのと同じ形式(そう、Valgrind-project-viewer-tool)にし、kcachegrindを使用してそれを表示することです。

google-pprof --callgrind main.out prof.out  > callgrind.out
kcachegrind callgrind.out

これらの方法のいずれかで実行した後prof.out、出力としてプロファイルデータファイルを取得します。以下のようにして、そのファイルをSVGとしてグラフィカルに表示できます。

google-pprof --web main.out prof.out

ここに画像の説明を入力してください

これは、他のツールと同じように、おなじみのコールグラフとして提供されますが、秒数ではなくサンプル数の不格好な単位を使用しています。

あるいは、次のようにしてテキストデータを取得することもできます。

google-pprof --text main.out prof.out

それは与える:

Using local file main.out.
Using local file prof.out.
Total: 187 samples
     187 100.0% 100.0%      187 100.0% common
       0   0.0% 100.0%      187 100.0% __libc_start_main
       0   0.0% 100.0%      187 100.0% _start
       0   0.0% 100.0%        4   2.1% fast
       0   0.0% 100.0%      187 100.0% main
       0   0.0% 100.0%      183  97.9% maybe_slow

参照:Google Perfツールの使用方法

Ubuntu 18.04、gprof2dot 2019.11.30、valgrind 3.13.0、perf 4.15.18、Linuxカーネル4.15.0、FLameGraph 1a0dc6985aad06e76857cf2a354bd5ba0c9ce96b、gperftools 2.5-2でテスト済み。


2
デフォルトでは、perfレコードはフレームポインタレジスタを使用します。最新のコンパイラはフレームアドレスを記録せず、代わりにレジスタを汎用目的で使用します。代替案は、-fno-omit-frame-pointerフラグを付けてコンパイルするか、別の代替案を使用することです。シナリオに応じて、--call-graph "dwarf"または--call-graph "lbr"シナリオに応じて記録します。
ホルヘベロン

5

シングルスレッドプログラムの場合は、igprof、The Ignominous Profiler:https ://igprof.org/を使用できます

これはサンプリングプロファイラーであり、...長い...マイクダンラベイによる回答です。これは、結果をブラウズ可能なコールスタックツリーにギフトラップし、累積または各関数で費やされた時間またはメモリで注釈が付けられます関数ごと。


面白そうですが、GCC 9.2でコンパイルできません。(Debian / Sid)githubで問題を作りました。
バジルスタリンケビッチ

5

また言及する価値があります

  1. HPCToolkit(http://hpctoolkit.org/)-オープンソース、並列プログラムで動作し、結果を複数の方法で確認できるGUIを備えています
  2. Intel VTune(https://software.intel.com/en-us/vtune)-Intelコンパイラを使用している場合、これは非常に優れています
  3. TAU(http://www.cs.uoregon.edu/research/tau/home.php

私はHPCToolkitとVTuneを使用しましたが、テントの長い極を見つけるのに非常に効果的で、コードを再コンパイルする必要はありません(意味のある出力を取得するために、CMakeで-g -OまたはRelWithDebInfoタイプのビルドを使用する必要がある場合を除く) 。TAUの機能は似ていると聞きました。


4

これらは、コードを高速化するために使用する2つの方法です。

CPUバウンドアプリケーションの場合:

  1. DEBUGモードでプロファイラーを使用して、コードの問題のある部分を特定する
  2. 次にRELEASEモードに切り替えて、パフォーマンスの変化が見られるまで、コードの問題のあるセクションをコメント化します(何もない状態でスタブします)。

I / Oバウンドアプリケーションの場合:

  1. コードの疑わしい部分を識別するには、リリースモードでプロファイラーを使用します。

NB

プロファイラーがない場合は、貧しい人のプロファイラーを使用してください。アプリケーションのデバッグ中に一時停止を押します。ほとんどの開発者スイートは、コメント付きの行番号でアセンブリに分割されます。CPUサイクルの大部分を消費している領域に統計的に着陸する可能性があります。

CPUの場合、デバッグモードでプロファイリングを行う理由は、RELEASEモードでプロファイリングを試みた場合、コンパイラーが数学を削減し、ループをインライン化し、コードを組み立てたときにマップできない混乱にコードをインライン化する傾向があるためです。マップできない混乱は、アセンブリが最適化中のソースコードに対応していない可能性があるため、プロファイラーがそれほど時間がかかっていることを明確に識別できないことを意味しますRELEASEモードのパフォーマンス(たとえば、タイミング依存)が必要な場合は、使用可能なパフォーマンスを維持するために、必要に応じてデバッガー機能を無効にします。

I / Oバウンドの場合でも、プロファイラはRELEASEモードのI / O操作を識別できます。これは、I / O操作が共有ライブラリに外部リンクされている(ほとんどの場合)か、最悪の場合、結果としてsys-割り込みベクトルを呼び出します(これもプロファイラーによって簡単に識別できます)。


2
+1貧乏人の方法は、CPUバウンドと同様にI / Oバウンドでも同様に機能します。すべてのパフォーマンスチューニングをデバッグモードで行うことをお勧めします。チューニングが終了したら、RELEASEをオンにします。プログラムがコード内でCPUにバインドされている場合は、改善されます。これは、大まかなプロセスの短いビデオです。
Mike Dunlavey、2014年

3
パフォーマンスプロファイリングにはデバッグビルドを使用しません。DEBUGモードのパフォーマンスが重要な部分は、リリースモードでは完全に最適化されていることがよくあります。別の問題は、パフォーマンスにノイズを追加するデバッグコードでのアサートの使用です。
gast128 2014

3
私の投稿を全部読んだ?"RELEASEモードのパフォーマンス(たとえば、タイミング依存)が必要な場合は、使用可能なパフォーマンスを維持するために必要に応じてデバッガー機能を無効にしてください"、 "RELEASEモードに切り替えて、コードの問題のあるセクションにコメントする(何もない状態でスタブする)。パフォーマンスの変化。」私は、デバッグモードで問題の可能性のある領域をチェックし、リリースモードでそれらの問題を確認して、あなたが言及した落とし穴を回避すると述べました。
seo 2014


2

次のようなロギングフレームワークを使用できloguruます。これには、プロファイリングに適切に使用できるタイムスタンプと合計稼働時間が含まれています。


1

職場には、スケジューリングに関して必要なものを監視するのに役立つ非常に優れたツールがあります。これは何度も役に立ちました。

これはC ++で提供されており、ニーズに合わせてカスタマイズする必要があります。残念ながら、コードを共有することはできません。概念だけです。volatileタイムスタンプとイベントIDを含む「大きな」バッファーを使用します。これは、事後分析またはロギングシステムの停止後にダンプできます(これをファイルにダンプするなど)。

すべてのデータを含むいわゆる大きなバッファーを取得し、小さなインターフェイスがそれを解析して、オシロスコープが色(.hppファイルで構成されている)で行うように、名前(up / down + value)でイベントを表示します。

生成するイベントの量をカスタマイズして、必要なものだけに焦点を当てます。1秒あたりのログに記録されたイベントの量に基づいて必要なCPUの量を消費しながら、スケジュールの問題を解決するのに大いに役立ちました。

3つのファイルが必要です。

toolname.hpp // interface
toolname.cpp // code
tool_events_id.hpp // Events ID

コンセプトはtool_events_id.hppそのようにイベントを定義することです:

// EVENT_NAME                         ID      BEGIN_END BG_COLOR NAME
#define SOCK_PDU_RECV_D               0x0301  //@D00301 BGEEAAAA # TX_PDU_Recv
#define SOCK_PDU_RECV_F               0x0302  //@F00301 BGEEAAAA # TX_PDU_Recv

また、いくつかの関数を定義しますtoolname.hpp

#define LOG_LEVEL_ERROR 0
#define LOG_LEVEL_WARN 1
// ...

void init(void);
void probe(id,payload);
// etc

コードのどこにでも使用できます:

toolname<LOG_LEVEL>::log(EVENT_NAME,VALUE);

このprobe関数は、いくつかのアセンブリラインを使用してクロックタイムスタンプをできるだけ早く取得し、エントリをバッファに設定します。ログイベントを格納するインデックスを安全に見つけるためのアトミックな増分もあります。もちろんバッファは循環しています。

アイデアがサンプルコードの欠如によって難読化されていないことを願っています。


1

実際には少し驚いて、多くのgoogle / benchmarkについて言及していませんが、特にコードベースが少し大きい場合、コードの特定の領域を固定するのは少し面倒ですが、これをcallgrind

ボトルネックを引き起こしている部分を特定する私見は、ここでの鍵です。ただし、まず以下の質問に答えて、それに基づいてツールを選択します

  1. 私のアルゴリズムは正しいですか?
  2. ボトルネックであることが証明されているロックはありますか?
  3. 犯人であることが判明しているコードの特定のセクションはありますか?
  4. IOはどうですか?

valgrind組み合わせでcallrindkcachegrind上記の点にまともな推定を提供する必要があり、コードのいくつかの部分に問題があることを確立していますと、私はマイクロベンチマークを行うことをお勧めしたいことはgoogle benchmark開始するには良い場所です。


1

-pgコードをコンパイルおよびリンクするときにフラグを使用し、実行可能ファイルを実行します。このプログラムの実行中、プロファイリングデータはファイルa.outに収集されます。
2つの異なるタイプのプロファイリングがあります。

1-フラットプロファイリング:
コマンドgprog --flat-profile a.outを実行すると、次のデータが得られます
-関数に費やされた全体の時間のパーセンテージ、
- 関数に費やされた秒
数- サブ関数の呼び出しを含め、除外して、-通話、
- 通話あたりの平均時間。

2-グラフプロファイリングを使用し
て、gprof --graph a.out各関数について次のデータを取得するコマンドを含めます
。各セクションでは、1つの関数にインデックス番号が付いています。
-関数の上に、関数を呼び出す関数のリストがあります。
-関数の下に、関数によって呼び出される関数のリストがあります。

詳細については、https://sourceware.org/binutils/docs-2.32/gprof/をご覧ください。


0

Arm MAPについて誰も触れなかったので、個人的にはMapを使用してC ++科学プログラムのプロファイルを作成したので、追加します。

Arm MAPは、並列、マルチスレッド、またはシングルスレッドのC、C ++、Fortran、およびF90コードのプロファイラーです。詳細な分析と、ソースラインへのボトルネックのピンポイントを提供します。ほとんどのプロファイラーとは異なり、pthread、OpenMPまたはMPIを並列およびスレッド化されたコード用にプロファイリングできるように設計されています。

MAPは商用ソフトウェアです。


0

デバッグソフトウェア を使用して、コードの実行速度が遅い場所を特定する方法

動いている間に障害物があると思うだけで速度が落ちます

そのような不要な再割り当てのループ、バッファオーバーフロー、検索、メモリリークなどの操作は、より多くの実行能力を消費し、コードのパフォーマンスに悪影響を及ぼします。プロファイリングの前に-pgをコンパイルに追加してください。

g++ your_prg.cpp -pgまたはcc my_program.cpp -g -pgコンパイラごと

まだ試していませんが、google-perftoolsについて良いことを聞いています。それは間違いなく試してみる価値があります。

valgrind --tool=callgrind ./(Your binary)

gmon.outまたはcallgrind.out.xというファイルが生成されます。その後、kcachegrindまたはデバッガーツールを使用して、このファイルを読み取ることができます。それはあなたに物事のグラフィカルな分析を与え、どのラインがどれくらいの費用がかかるかのような結果をもたらします。

私はそう思う

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.