今日私はlinux ' perf ユーティリティを試しましたが、その結果の解釈に問題があります。 valgrindのcallgrindに慣れていますが、これはもちろん、サンプリングベースのperfメソッドとはまったく異なるアプローチです。
私がしたこと:
perf record -g -p $(pidof someapp)
perf report -g -n
今、私は次のようなものを見ます:
+ 16.92%kdevelop libsqlite3.so.0.8.6 [。] 0x3fe57↑ + 10.61%kdevelop libQtGui.so.4.7.3 [。] 0x81e344▮ + 7.09% kdevelop libc-2.14.so [。] 0x85804▒ + 4.96%kdevelop libQtGui.so.4.7.3 [。] 0x265b69▒ + 3.50%kdevelop libQtCore.so.4.7.3 [。 ] 0x18608d▒ + 2.68%kdevelop libc-2.14.so [。] memcpy▒ + 1.15%kdevelop [kernel.kallsyms] [k] copy_user_generic_string▒ + 0.90%kdevelop libQtGui.so.4.7.3 [。] QTransform :: translate(double、 double)▒ + 0.88%kdevelop libc-2.14.so [。] __libc_malloc▒ + 0.85%kdevelop libc-2.14.so [。] memcpy ...
わかりました、これらの関数は遅いかもしれませんが、それらがどこから呼び出されているのかを知るにはどうすればいいですか?これらのホットスポットはすべて外部ライブラリにあるため、コードを最適化する方法はありません。
基本的に、累積コストで注釈されたある種のコールグラフを探しています。この関数では、呼び出したライブラリ関数よりも包括的なサンプリングコストが高くなります。
これはperfで可能ですか?もしそうなら-どうやって?
注:「E」はコールグラフのラップを解除し、さらに多くの情報を提供することがわかりました。しかし、コールグラフは十分な深さではない場合や、どこでどのくらいの情報が使われたかについての情報を提供せずにランダムに終了することがよくあります。例:
-10.26%kate libkatepartinterfaces.so.4.6.0 [。] Kate :: TextLoader :: readLine(int&... Kate :: TextLoader :: readLine(int&、int&) Kate :: TextBuffer :: load(QString const&、bool&、bool&) KateBuffer :: openFile(QString const&) KateDocument :: openFile() 0x7fe37a81121c
64ビットで実行しているのは問題でしょうか?参照: http://lists.fedoraproject.org/pipermail/devel/2010-November/144952.html (私はFedoraを使用していませんが、すべての64ビットシステムに適用されるようです)。
ホットスポットを試してみてください: https://www.kdab.com/hotspot-gui-linux-perf-profiler/
Githubで利用可能です: https://github.com/KDAB/hotspot
たとえば、フレームグラフを生成できます。
わかりました、これらの関数は遅いかもしれませんが、それらがどこから呼び出されているのかを知るにはどうすればいいですか?これらのホットスポットはすべて外部ライブラリにあるため、コードを最適化する方法はありません。
アプリケーションsomeapp
はgccオプション-fno-omit-frame-pointer
(および場合によってはその依存ライブラリ)を使用してビルドされていますか?このようなもの:
g++ -m64 -fno-omit-frame-pointer -g main.cpp
perf annotate
を使用すると、非常に詳細なソースレベルレポートを取得できます。 perf annotateを使用したソースレベル分析 を参照してください。これは次のようになります(Webサイトから盗み取られます):
------------------------------------------------
Percent | Source code & Disassembly of noploop
------------------------------------------------
:
:
:
: Disassembly of section .text:
:
: 08048484 <main>:
: #include <string.h>
: #include <unistd.h>
: #include <sys/time.h>
:
: int main(int argc, char **argv)
: {
0.00 : 8048484: 55 Push %ebp
0.00 : 8048485: 89 e5 mov %esp,%ebp
[...]
0.00 : 8048530: eb 0b jmp 804853d <main+0xb9>
: count++;
14.22 : 8048532: 8b 44 24 2c mov 0x2c(%esp),%eax
0.00 : 8048536: 83 c0 01 add $0x1,%eax
14.78 : 8048539: 89 44 24 2c mov %eax,0x2c(%esp)
: memcpy(&tv_end, &tv_now, sizeof(tv_now));
: tv_end.tv_sec += strtol(argv[1], NULL, 10);
: while (tv_now.tv_sec < tv_end.tv_sec ||
: tv_now.tv_usec < tv_end.tv_usec) {
: count = 0;
: while (count < 100000000UL)
14.78 : 804853d: 8b 44 24 2c mov 0x2c(%esp),%eax
56.23 : 8048541: 3d ff e0 f5 05 cmp $0x5f5e0ff,%eax
0.00 : 8048546: 76 ea jbe 8048532 <main+0xae>
[...]
コードをコンパイルするときに、-fno-omit-frame-pointer
および-ggdb
フラグを渡すことを忘れないでください。
プログラムに含まれる関数がほとんどなく、システム関数やI/Oをほとんど呼び出さない限り、プログラムカウンターをサンプリングするプロファイラーは、あなたが発見しているように多くを語りません。実際、よく知られているプロファイラーgprofは、自己時間のみのプロファイリングの無用さに対処するために作成されました(成功したわけではありません)。
実際に機能するのは、呼び出しスタック(呼び出しの発信元を見つける)、-壁時計時間(I/O時間を含む)をサンプリングするものです。そして行ごとまたは命令ごと(それにより、それらが住んでいる関数だけでなく、調査すべき関数呼び出しを特定します)。
さらに、検索する統計はスタックの時間の割合であり、呼び出し回数ではなく、平均的な関数時間ではありません。 特に「自己時間」ではありません。呼び出し命令(または非呼び出し命令)が38%の時間スタックにある場合、それを取り除くことができれば、どれだけ節約できますか? 38%!とても簡単ですよね?
そのようなプロファイラーの例は Zoom です。
このテーマには 理解する必要のある他の問題 があります。
追加:@cafはperf
情報を探してくれました。コマンドライン引数-g
スタックサンプルを収集します。その後、 call-tree レポートを取得できます。次に、実時間でサンプリングしていることを確認すると(CPU時間だけでなく待機時間も取得できます)、almost必要なものが得られます。