他のどのプログラムがgprofと同じことをしますか?
他のどのプログラムがgprofと同じことをしますか?
回答:
Valgrindには、KCacheGrindと呼ばれる非常に優れたビジュアライザーを備えた命令カウントプロファイラーがあります。Mike Dunlaveyが推奨しているように、Valgrindは、プロシージャがスタック上でライブになっている命令の割合をカウントしますが、相互再帰が存在すると混乱するように見えます。しかし、ビジュアライザーは非常に素晴らしく、何年も先を行っていgprof
ます。
gprof (論文を読む)は歴史的な理由で存在しています。パフォーマンスの問題を見つけるのに役立つと思われる場合は、そのように宣伝されていません。これは紙が言うことです:
プロファイルは、さまざまな実装のコストを比較および評価するために使用できます。
それは、評価されるさまざまな実装を識別するために使用できるとは言いませんが、特別な状況下では可能であることを意味します:
特に、プログラムの小さな部分が実行時間を支配していることが判明した場合。
ローカライズされていない問題についてはどうですか?それらは関係ありませんか?要求されたことのないgprofに期待しないでください。これは単なる測定ツールであり、CPUにバインドされた操作のみです。
代わりにこれを試してください。
これは、44倍のスピードアップの例です。
これは730倍の高速化です。
こちらが8分間のビデオデモです。
統計の説明は次のとおりです。
これが批評に対する答えです。
プログラムについての簡単な観察があります。特定の実行では、すべての命令が全体の時間の一部(特にcall
命令)に関与します。つまり、存在しない場合、時間は費やされないという意味です。その間、命令はスタック**にあります。それが理解されると、あなたはそれを見ることができます-
gprofは、次のようなパフォーマンスに関する特定の神話を体現しています。
このプログラムカウンターのサンプリングは便利です。
これは、スカラー値の大きな配列のバブルソートなど、不要なホットスポットのボトルネックがある場合にのみ役立ちます。たとえば、string-compareを使用して並べ替えに変更しても、それはまだボトルネックですが、ホットスポットがstring-compareにあるため、プログラムカウンターのサンプリングはそれを認識しません。一方、拡張プログラムカウンター(呼び出しスタック)をサンプリングすると、文字列比較が呼び出されるポイントであるソートループが明確に表示されます。実際、gprofは、PCのみのサンプリングの制限を解消する試みでした。
このタイミング関数は、時間のかかるコード行をキャプチャするよりも重要です。
その神話の理由は、gprofがスタックサンプルをキャプチャできなかったためです。代わりに、関数の時間を計り、それらの呼び出しをカウントし、コールグラフをキャプチャしようとします。ただし、コストのかかる関数が特定された後も、その関数の内部を調べて、時間の原因となっている行を見つける必要があります。調べる必要のないスタックサンプルがある場合、それらの行はサンプル上にあります。(一般的な関数には100〜1000の命令がある場合があります。関数呼び出しは1命令なので、コストのかかる呼び出しを見つけるものは2〜3桁正確です。)
コールグラフが重要です。
プログラムについて知っておくべきことは、プログラムが時間を費やす場所ではなく、その理由です。。関数で時間を費やしているとき、スタック上のすべてのコード行は、それが存在する理由の推論の連鎖に1つのリンクを提供します。スタックの一部しか表示できない場合は、理由の一部しか表示できないため、その時間が実際に必要かどうかはわかりません。コールグラフは何を教えてくれますか?各アークは、一部の関数Aが一部の時間の間、一部の関数Bを呼び出すプロセス中であったことを示しています。AにBを呼び出すコード行が1行しかない場合でも、その行は理由のほんの一部を提供します。運が良ければ、その行には理由がありません。通常、複数の同時線を見て、そこにあるかどうかの理由を見つける必要があります。Aが複数の場所でBに電話をかけると、さらに少ないことがわかります。
その再帰はトリッキーで紛らわしい問題です。
これは、gprofや他のプロファイラーがコールグラフを生成し、ノードに時間を割り当てる必要性を認識しているためです。スタックのサンプルがある場合、サンプルに表示されるコードの各行の時間コストは非常に単純な数です-それが存在するサンプルの割合。再帰がある場合、指定された行が1つのサンプルに複数回現れることがあります。
どんなに。サンプルがNミリ秒ごとに取得され、ラインがそれらのF%に表示されると仮定します(単独かどうか)。その行が(例えば、それを削除するか、その周りに分岐などによる)は時間を取らないようにすることができる場合、これらのサンプルはなり消滅、及び時間は、F%低減されるであろう。
時間測定の精度(したがって、多数のサンプル)が重要です。
少し考えてみてください。コードの行が5つのうち3つのサンプルにある場合、電球のようにそれを発射できれば、使用される時間は約60%短くなります。これで、異なる5つのサンプルを取得した場合、それを2回または4つしか表示しなかった可能性があることがわかります。したがって、60%の測定は、40%から80%の一般的な範囲に似ています。40%しかなかった場合、問題を修正する価値はないと思いますか?それで、あなたが本当に欲しいのが問題を見つけることであるとき、正確な時点は何ですか?500または5000個のサンプルで問題をより正確に測定できたはずですが、それ以上正確には検出できなかったでしょう。
ステートメントまたは関数の呼び出しのカウントが役立つこと。
関数が1000回呼び出されたことがわかっているとします。それから、時間の何分の1かわかりますか?また、平均して実行にかかる時間をカウントで乗算し、合計時間で除算する必要もあります。平均呼び出し時間はナノ秒から秒まで変動する可能性があるため、カウントだけではあまりわかりません。スタックサンプルがある場合、ルーチンまたは任意のステートメントのコストは、それが存在するサンプルの割合にすぎません。その時間の割合は、ルーチンまたはステートメントに時間がかからないようにできる場合、原則として全体的に節約できるものであり、それがパフォーマンスに最も直接的な関係を持っているものです。
ブロックされたときにサンプルを取得する必要がない
この神話の理由は2つあります。1)プログラムが待機しているときはPCサンプリングは無意味である、2)タイミングの正確さへの専念。ただし、(1)の場合、プログラムは、ファイルI / Oなど、ユーザーが知っておく必要があること、およびどのスタックサンプルが明らかにするかなど、プログラムが要求するものを非常に待っている可能性があります。(明らかに、ユーザー入力を待機している間、サンプルを除外します。)(2)の場合、プログラムが他のプロセスとの競合のために単に待機している場合、おそらく実行中にかなりランダムに発生します。そのため、プログラムに時間がかかる可能性がありますが、重要な統計、ステートメントがスタック上にある時間の割合には大きな影響はありません。
「セルフタイム」が重要であること
セルフタイムは、行レベルではなく関数レベルで測定している場合にのみ意味があり、関数時間が純粋にローカルな計算と呼び出されたルーチンのどちらに入るのかを見分けるのに助けが必要だと思います。ラインレベルで要約する場合、ラインがスタックの最後にある場合はラインは自己時間を表し、それ以外の場合は包括的時間を表します。どちらの場合も、コストがかかるのは、それが存在するスタックサンプルのパーセンテージです。そのため、どちらの場合でも、それが検索されます。
これは、サンプルを高頻度で取得
する必要があるということです。これは、パフォーマンスの問題は即効性のあるものであり、ヒットするためには頻繁にサンプルを取得する必要があるという考えに基づいています。ただし、問題のコストが10秒(またはその他)の合計実行時間の20%である場合、問題が発生したかどうかに関係なく、その合計時間の各サンプルは20%の確率でヒットします。このような単一のピース
.....XXXXXXXX...........................
.^.^.^.^.^.^.^.^.^.^.^.^.^.^.^.^.^.^.^.^
(20サンプル、4ヒット)
またはこのような多くの小さなピース
X...X...X.X..X.........X.....X....X.....
.^.^.^.^.^.^.^.^.^.^.^.^.^.^.^.^.^.^.^.^
(20サンプル、3ヒット)の
どちらの方法でも、ヒット数は、サンプルの数に関係なく、平均して5分の1になります。なんと少ない。(平均= 20 * 0.2 =4。標準偏差= +/- sqrt(20 * 0.2 * 0.8)= 1.8。)
あなたは見つけようとしているというボトルネックを
一つだけがあったかのように。次の実行タイムラインを考えてみます。vxvWvzvWvxvWvYvWvxvWv.vWvxvWvYvW
これは、で表される実際に役立つ作業で構成されてい.
ます。vWxYz
それぞれ、時間の1 / 2、1 / 4、1 / 8、1 / 16、1 / 32を使用するパフォーマンスの問題があります。サンプリングはv
簡単に見つかります。これは削除され、残り
xWzWxWYWxW.WxWYW
ます。プログラムの実行W
時間は半分になり、半分の時間がかかり、簡単に見つけることができます。それは削除され、そのままに
xzxYx.xY
なります。削除するものが見つからなくなるまで、パフォーマンスの問題をパーセンテージで最大にするたびに、このプロセスが続行されます。実行されるのは.
、元のプログラムが使用する時間の1/32で実行されるだけです。これは拡大効果です、分母が減少するため、問題を取り除くと、残りがパーセントで大きくなります。
もう1つの重要な点は、すべての問題を検出する必要があることです。5のどれも欠けていません。検出および修正されなかった問題は、最終的なスピードアップ率を大幅に低下させます。すべてではなく一部を見つけるだけでは「十分に良い」とは言えません。
追加:gprofが人気がある理由の1つを指摘したいと思います。それは、おそらく無料で、教えるのが簡単で、長い間行われているため、教えられているからです。Googleをすばやく検索すると、それを教える(またはそのように見える)いくつかの学術機関が見つかります。
バークレーbuクレムソンコロラドデュークアールハムfsuインディアナmit msu ncsa.illinois ncsu nyu ouプリンストンpsuスタンフォードucd umd umich utah utexas utk wustl
**作業の実行を要求する他の方法を除いて、メッセージの投稿など、理由を示す痕跡を残しません。
perf
Linuxでカーネルおよびユーザーアプリケーションをプロファイリングするための比較的新しいツールは何もここに表示されなかったので、この情報を追加することにしました。
まず第一に、これはLinuxプロファイリングに関するチュートリアルですperf
perf
Linuxカーネルが2.6.32より大きい場合、またはoprofile
古い場合に使用できます。どちらのプログラムも、プログラムを計測するgprof
ために(requiresのように)必要はありません。ただし、呼び出しグラフを正しく取得perf
するには、でプログラムを作成する必要があります-fno-omit-frame-pointer
。例:g++ -fno-omit-frame-pointer -O2 main.cpp
。
あなたはあなたのアプリケーションの「ライブ」分析を見ることができますperf top
:
sudo perf top -p `pidof a.out` -K
または、実行中のアプリケーションのパフォーマンスデータを記録し、その後分析することもできます。
1)パフォーマンスデータを記録するには:
perf record -p `pidof a.out`
または10秒間記録するには:
perf record -p `pidof a.out` sleep 10
またはコールグラフで記録する()
perf record -g -p `pidof a.out`
2)記録されたデータを分析する
perf report --stdio
perf report --stdio --sort=dso -g none
perf report --stdio -g none
perf report --stdio -g
または、アプリケーションのパフォーマンスデータを記録し、この方法でアプリケーションを起動して終了するのを待つだけで、それらを分析できます。
perf record ./a.out
これはテストプログラムのプロファイリングの例です
テストプログラムはファイルmain.cppにあります(メッセージの下部にmain.cppを追加します)。
私はそれをこのようにコンパイルします:
g++ -m64 -fno-omit-frame-pointer -g main.cpp -L. -ltcmalloc_minimal -o my_test
libc mallocはこのオプションなしでコンパイルされているようlibmalloc_minimial.so
ですが、でコンパイルされて-fno-omit-frame-pointer
いるので使用します。次に、テストプログラムを実行します
./my_test 100000000
次に、実行中のプロセスのパフォーマンスデータを記録します。
perf record -g -p `pidof my_test` -o ./my_test.perf.data sleep 30
次に、モジュールごとの負荷を分析します。
パフォーマンスレポート--stdio -g none --sort comm、dso -i ./my_test.perf.data
# Overhead Command Shared Object
# ........ ....... ............................
#
70.06% my_test my_test
28.33% my_test libtcmalloc_minimal.so.0.1.0
1.61% my_test [kernel.kallsyms]
次に、関数ごとの負荷が分析されます。
パフォーマンスレポート--stdio -g none -i ./my_test.perf.data | c ++ filt
# Overhead Command Shared Object Symbol
# ........ ....... ............................ ...........................
#
29.30% my_test my_test [.] f2(long)
29.14% my_test my_test [.] f1(long)
15.17% my_test libtcmalloc_minimal.so.0.1.0 [.] operator new(unsigned long)
13.16% my_test libtcmalloc_minimal.so.0.1.0 [.] operator delete(void*)
9.44% my_test my_test [.] process_request(long)
1.01% my_test my_test [.] operator delete(void*)@plt
0.97% my_test my_test [.] operator new(unsigned long)@plt
0.20% my_test my_test [.] main
0.19% my_test [kernel.kallsyms] [k] apic_timer_interrupt
0.16% my_test [kernel.kallsyms] [k] _spin_lock
0.13% my_test [kernel.kallsyms] [k] native_write_msr_safe
and so on ...
次に、呼び出しチェーンが分析されます。
パフォーマンスレポート--stdio -gグラフ-i ./my_test.perf.data | c ++ filt
# Overhead Command Shared Object Symbol
# ........ ....... ............................ ...........................
#
29.30% my_test my_test [.] f2(long)
|
--- f2(long)
|
--29.01%-- process_request(long)
main
__libc_start_main
29.14% my_test my_test [.] f1(long)
|
--- f1(long)
|
|--15.05%-- process_request(long)
| main
| __libc_start_main
|
--13.79%-- f2(long)
process_request(long)
main
__libc_start_main
15.17% my_test libtcmalloc_minimal.so.0.1.0 [.] operator new(unsigned long)
|
--- operator new(unsigned long)
|
|--11.44%-- f1(long)
| |
| |--5.75%-- process_request(long)
| | main
| | __libc_start_main
| |
| --5.69%-- f2(long)
| process_request(long)
| main
| __libc_start_main
|
--3.01%-- process_request(long)
main
__libc_start_main
13.16% my_test libtcmalloc_minimal.so.0.1.0 [.] operator delete(void*)
|
--- operator delete(void*)
|
|--9.13%-- f1(long)
| |
| |--4.63%-- f2(long)
| | process_request(long)
| | main
| | __libc_start_main
| |
| --4.51%-- process_request(long)
| main
| __libc_start_main
|
|--3.05%-- process_request(long)
| main
| __libc_start_main
|
--0.80%-- f2(long)
process_request(long)
main
__libc_start_main
9.44% my_test my_test [.] process_request(long)
|
--- process_request(long)
|
--9.39%-- main
__libc_start_main
1.01% my_test my_test [.] operator delete(void*)@plt
|
--- operator delete(void*)@plt
0.97% my_test my_test [.] operator new(unsigned long)@plt
|
--- operator new(unsigned long)@plt
0.20% my_test my_test [.] main
0.19% my_test [kernel.kallsyms] [k] apic_timer_interrupt
0.16% my_test [kernel.kallsyms] [k] _spin_lock
and so on ...
したがって、この時点で、プログラムが時間を費やす場所がわかります。
そして、これはテスト用のmain.cppです。
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
time_t f1(time_t time_value)
{
for (int j =0; j < 10; ++j) {
++time_value;
if (j%5 == 0) {
double *p = new double;
delete p;
}
}
return time_value;
}
time_t f2(time_t time_value)
{
for (int j =0; j < 40; ++j) {
++time_value;
}
time_value=f1(time_value);
return time_value;
}
time_t process_request(time_t time_value)
{
for (int j =0; j < 10; ++j) {
int *p = new int;
delete p;
for (int m =0; m < 10; ++m) {
++time_value;
}
}
for (int i =0; i < 10; ++i) {
time_value=f1(time_value);
time_value=f2(time_value);
}
return time_value;
}
int main(int argc, char* argv2[])
{
int number_loops = argc > 1 ? atoi(argv2[1]) : 1;
time_t time_value = time(0);
printf("number loops %d\n", number_loops);
printf("time_value: %d\n", time_value );
for (int i =0; i < number_loops; ++i) {
time_value = process_request(time_value);
}
printf("time_value: %ld\n", time_value );
return 0;
}
f1
が delete
時間の(大体)40%process_request
がを呼び出していましたdelete
。残りの大部分はで費やされましたnew
。測定値は粗いですが、ホットスポットは特定されています。
As in my answer, you run it under a debugger and hit ^C at a random time and capture the stack trace
。1)顧客のサーバーで実行されているプログラムのパフォーマンスの問題を分析する必要がある場合、この手法は役に立たないと思います。2)さまざまな要求を処理する多数のスレッドを持つプログラムの情報を取得するために、この手法をどのように適用するかわかりません。つまり、全体像がかなり複雑な場合です。
the problem is outside your code
ね。あなたの主張を裏付けるためにいくつかの情報が必要になるかもしれないので。この状況では、ある時点でアプリケーションをプロファイルする必要があるかもしれません。顧客にgdbを起動して^ Cを押し、コールスタックを取得するよう依頼することはできません。これが私のポイントでした。これはspielwiese.fontein.de/2012/01/22/…の例です。私にはこの問題があり、プロファイリングは大いに役立ちました。
OProfileを試してください。コードをプロファイリングするためのはるかに優れたツールです。Intel VTuneもお勧めします。
上記の2つのツールは、特定のコード行で費やされた時間を絞り込み、コードに注釈を付け、アセンブリと特定の命令がどれだけかかるかを示します。時間メトリックのほかに、特定のカウンタ、つまりキャッシュヒットなどをクエリすることもできます。
gprofとは異なり、システムで実行中の任意のプロセス/バイナリを、2つのいずれかを使用してプロファイルできます。
Googleパフォーマンスツールには、使いやすいプロファイラーが含まれています。CPUとヒーププロファイラが利用可能です。
http://lttng.org/高性能トレーサーが必要な場合