0

gprofcallgrindを何度も使用してきましたが、大規模な(車全体をロードするCADプログラムのように)プログラムを処理する場合、これらを効率的に使用できないという(明らかな)結論に達しました。たぶん、C / C ++ MACROの魔法を使って、どういうわけか単純な(しかし素晴らしい)ロギングメカニズムを構築できるのではないかと思っていました。たとえば、次のマクロを使用して関数を呼び出すことができます。

#define CALL_FUN(fun_name, ...) \
    fun_name (__VA_ARGS__);

関数呼び出しの前後にクロッキング/タイミングを追加して、CALL_FUNで呼び出されるすべての関数のタイミングを調整することができます。

#define CALL_FUN(fun_name, ...) \
   time_t(&t0);                 \
   fun_name (__VA_ARGS__);      \
   time_t(&t1);

変数t0、t1は、グローバルロギングオブジェクトで見つけることができます。そのロギングオブジェクトは、 CALL_FUNを介して呼び出された各関数の呼び出しグラフを保持することもできます。その後、そのオブジェクトを(特別にフォーマットされた)ファイルに書き込んで、他のプログラムから解析することができます。

それで、ここに私の(最初の)質問があります:このアプローチは扱いやすいと思いますか?はいの場合、それをどのように強化できますか?そうでない場合は、時間を測定してコールグラフを記録するためのより良い方法を提案できますか?

同僚は、この問題に対処するための別のアプローチを提案しました。これは、各関数(ログに記録する必要がある)に特定のコメントを付けることです。次に、makeプロセス中に、特別なプリプロセッサを実行し、各ソースファイルを解析し、ログに記録する関数ごとにログロジックを追加し、新しく追加された(解析)コードを使用して新しいソースファイルを作成し、代わりにそのコードをビルドする必要があります。CALL_FUN ...マクロ(私の提案)をあちこちで読むのは最善のアプローチではないと思います。彼のアプローチはこの問題を解決するでしょう。では、このアプローチについてどう思いますか?

PS:私はC / C ++マクロの落とし穴に精通していないので、これが別のアプローチを使用して開発できる場合は、そう言ってください。

ありがとうございました。

4

6 に答える 6

2

私は少し遅れていますが、これが私がこれのためにやっていることです:

Windowsには、/ Ghコンパイラスイッチがあり、コンパイラが各関数の先頭に非表示の_penter関数を挿入するようにします。各関数の最後に_pexit呼び出しを取得するためのスイッチもあります。

これを利用して、各関数呼び出しでコールバックを取得できます。詳細とサンプルソースコードの記事は次のとおりです。

http://www.johnpanzer.com/aci_cuj/index.html

カスタムログシステムでこのアプローチを使用して、最後の数千の関数呼び出しをリングバッファに保存しています。これは、クラッシュのデバッグに役立つことがわかりました(MiniDumpsと組み合わせて)。

これに関するいくつかの注意:

  • パフォーマンスへの影響は、コールバックコードによって大きく異なります。あなたはそれをできるだけ単純に保つ必要があります。
  • 関数アドレスとモジュールベースアドレスをログファイルに保存するだけです。その後、Debug Interface Access SDKを使用して、アドレスから(PDBファイルを介して)関数名を取得できます。

これはすべて私にとって驚くほどうまく機能します。

于 2012-10-04T20:35:28.730 に答える
2

さて、ロギングオブジェクトを埋め込むためにC++の魔法を使うことができます。何かのようなもの

class CDebug 
{
CDebug() { ... log somehow ... }
~CDebug() { ... log somehow ... }

};

あなたの関数でそれからあなたは単に書く

void foo()
{
   CDebug dbg;
    ...

   you could add some debug info


   dbg.heythishappened()

   ...
}  // not dtor is called or if function is interrupted called from elsewhere.
于 2011-04-14T13:57:52.847 に答える
1

多くの優れた産業用ライブラリには、念のため、関数の宣言と定義が void マクロにラップされています。コードがすでにそのようなものである場合は、単純な非同期トレース ロガーを使用してパフォーマンスの問題をデバッグしてください。そうでない場合 -- そのようなマクロのポスト挿入は、許容できないほど時間がかかる可能性があります。

valgrind の下で 1Mx1M マトリックス ソルバーを実行することの苦痛を理解できるので、いわゆる「モンテカルロ プロファイリング法」から始めることをお勧めします。プロセスを開始し、並行してpstackを 1 秒ごとに繰り返し実行します。その結果、N 個のスタック ダンプが作成されます (N は非常に重要です)。次に、数学的アプローチは、各スタックの相対頻度を数え、最も頻度の高いものについて結論を出すことです。実際には、すぐにボトルネックを確認するか、そうでない場合は、二分法、gprof に切り替え、最後に valgrind のツールセットに切り替えます。

于 2011-04-14T19:22:20.993 に答える
1

これを行っている理由は、パフォーマンスの問題 (ボトルネック) を見つけて、それらを修正してパフォーマンスを向上させたいからだと思います。

速度を測定したり、カバレッジ情報を取得したりするのではなく。

これを行う方法は、関数呼び出しの履歴をログに記録し、各呼び出しにかかる時間を測定することだと考えているようです。

別のアプローチがあります。これは、主にプログラムが大きな呼び出しツリーをたどるという考えに基づいています。時間が無駄になっているのは、コール ツリーが必要以上にふさふさしているためであり、無駄になっている間、無駄になっているコードがスタック上に表示されます。スタックのほぼすべてのレベルで、ターミナル命令の可能性がありますが、関数呼び出しの可能性が高くなります。デバッガーの下でプログラムを数回一時停止するだけで、最終的に表示されます。複数のスタックサンプルで実行しているのを見ると、改善できれば、プログラムが高速化されます。CPU、I/O、またはウォールクロック時間を消費するその他のものに時間が費やされているかどうかに関係なく機能します。表示されないのは、知る必要のない大量の情報です。それができない唯一の方法ボトルネックが非常に小さい場合は、コードがほぼ最適であることを示しています。

ここにもっと説明があります。

于 2011-04-14T19:02:37.827 に答える
0

gprofよりも優れた方法を実行するのは難しいと思いますが、たとえば、特別なクラスLOGを作成して、ログに記録する各関数の先頭でインスタンス化することができます。

class LOG {
    LOG(const char* ...) {
        // log time_t of the beginning of the call
    }
    ~LOG(const char* ...) {
        // calculate the total time spent,
        //by difference between current time and that saved in the constructor
    }
};

void somefunction() {
    LOG log(__FUNCTION__, __FILE__, ...);
    .. do other things
}

これで、このアプローチを前述の前処理と統合できます。ログに記録する各関数の先頭に次のようなものを追加するだけです。

// ### LOG

次に、デバッグビルドで文字列を自動的に置き換えます(難しいことではありません)。

于 2011-04-14T13:57:33.203 に答える
0

プロファイラーを使用する必要があるかもしれません。AQTimeは、Visual Studio に比較的適しています。(VS2010 Ultimate をお持ちの場合は、既にプロファイラーを持っています。)

于 2011-04-14T14:23:15.353 に答える