7

私は非常に大規模な C++ プロジェクトに取り組んでいます。リアルタイムで重要な関数が多数あり、バックグラウンド関数も低速です。これらのバックグラウンド関数は、タイム クリティカルな関数から呼び出されるべきではありません。これらのバックグラウンド関数が重要な関数から呼び出されていることを検出する方法はありますか? コンパイル時間は良いでしょうが、とにかくこれらのバックグラウンド関数の前に検出したい. 詳細については、遅い関数と重要な関数の両方が同じクラスの一部であり、同じヘッダーを共有しています。

いくつかの詳細情報、重要な機能は、非常に高速なスレッド (>=10KHz) で実行され、低速のスレッドは別の低速のスレッド (<=1KHz) で実行されます。クラス メンバー変数は、両方とも同じクラス メンバー変数を使用するため、スロー関数のクリティカル セクションを使用して保護されます。これが、重要な関数で遅い関数を呼び出すと、システム全体のパフォーマンスが低下する理由です。そのため、手動でチェックするのではなく、これらすべての種類の関数を自動的に見つけるのが好きです。

ありがとう....

4

4 に答える 4

4

リンカーを活用する必要があります。「リアルタイム」機能と低速機能を 2 つのモジュールに分け、正しい順序でリンクします。

たとえば、ファイルを 2 つのディレクトリに分割します。各ディレクトリから lib を作成し (オブジェクト ファイルを一緒に ranlib します)、次を使用して最終的なアプリケーションをリンクします。

c++ -o myapp main.o lib1/slowfns.a lib2/realtime.a

コンパイラによっては、realtime.a で slowfns.a から何かを呼び出そうとすると、リンクに失敗します (一部のコンパイラでは、これを強制するためのオプションが必要になる場合があります)。

さらに、これにより、コンパイル時の宣言も簡単に管理できます。保護を強化するために「リアルタイム」関数ライブラリをコンパイルするときは、slowfns ライブラリのヘッダーがインクルード パスにないことを確認してください。

于 2013-05-22T10:49:48.263 に答える
3

Nicholas Wilson によって提案されたもの以外のコンパイル時の検出を取得することは、不可能ではないにしても非常に困難ですが、「バックグラウンド」が実際には複数のスレッドではなく関数を参照していると仮定します (質問でスレッドについて言及されていないので、奇妙な言い回しだと思います)グローバルフラグとロッカーオブジェクトを簡単に使用してassert、例外をスローするか、スローすることができます。または、デバッグメッセージを出力してください。もちろん、これはランタイムのみですが、犯罪者を非常に迅速に隔離できるはずです。また、デバッグ ビルドのオーバーヘッドは非常に低く (L1 キャッシュからの実行がほぼ保証されます)、リリース ビルドのオーバーヘッドはありません。

CaptureStackBackTraceを使用すると、問題のある関数のアドレスをキャプチャできるはずです。これは、ツールのようなものaddr2line(または MS に相当するもの) がコード内の行に直接変換できます。おそらく、この翻訳を直接実行できるツールヘルプ機能さえあるでしょう (私にはわかりませんが)。

したがって、次のようなもの (テストされていません!) でうまくいく可能性があります。

namespace global { int slow_flag = 0; }
struct slow_func_locker
{
    slow_func_locker() { ++global::slow_flag; }
    ~slow_func_locker(){ --global::slow_flag; }
};
#indef NDEBUG
  #define REALTIME  if(global::slow_flag) \
  { \
    void* backtrace; \
    CaptureStackBackTrace(0, 1, &backtrace, 0); \
    printf("RT function %s called from %08x\n", __FUNCTION__, backtrace); \
  }
  #define SLOW_FUNC slow_func_locker slow_func_locker_;
#else
  #define REALTIME
  #define SLOW_FUNC
#endif

foo_class::some_realtime_function(...)
{
    REALTIME;
    //...
};

foo_class::some_slow_function(...)
{
    SLOW_FUNC;
    //...
    some_realtime_function(blah); // this will trigger
};

唯一の本当の欠点 (コンパイル時ではないことは別として) は、すべての遅いリアルタイム関数をいずれかのマーカーでマークする必要があることですが、コンパイラーはどれが何であるかを魔法のように知ることができないため、とにかく選択肢はあまりありません。

グローバルな「フラグ」は実際にはカウンターであり、フラグではないことに注意してください。この理由は、遅い関数が、フラグを返してクリアする別の遅い関数をすぐに呼び出す可能性があるためです。現在、高速関数を誤って想定しています (この場合、xgbi によって提案されたクリティカル セクションを使用するアプローチはデッドロックする可能性があります!)。カウンターはこれを防ぎます。intスレッドが存在する場合は、 に置き換えることstd::atomic_intもできます。

編集:実際に2つのスレッド
が実行 されていることが明らかになったため、そのうちの1つ(「高速」スレッド)が「低速」関数を呼び出さないことだけが重要であるため、別の単純で機能するソリューションがあります(使用する例Win32 API ですが、どちらの方法でも POSIX で実行できます):

「高速」スレッドが起動すると (「低速」スレッドはこれを行う必要はありません)、スレッド ID をグローバル変数として、またはすべての高速/低速関数を含むオブジェクトのメンバーとして、どこかに保存します。アクセス可能な場所:

global::fast_thread_id = GetCurrentThreadId();

「歓迎されない」関数呼び出しを回避するマクロは、次のようになります。

#define CHECK_FAST_THREAD assert(GetCurrentThreadID() != global::fast_thread_id)

このマクロは、「高速」スレッドから呼び出されてはならない「低速」関数に追加されます。高速スレッドが呼び出してはならない関数を呼び出すと、アサートがトリガーされ、どの関数が呼び出されたかがわかります。

于 2013-05-22T15:52:43.550 に答える