13

コンテキストの切り替えとスレッドのダウンタイムを考慮して、いくつかのコードの実行時間を多くのスレッドで可能な限り正確に測定しようとしています。アプリケーションは C# (VS 2008) で実装されています。例:

public void ThreadFunc ()
{
    // Some code here

    // Critical block #1 begins here
    long lTimestamp1 = Stopwatch.GetTimestamp ();

    CallComplex3rdPartyFunc (); // A

    long lTimestamp2 = Stopwatch.GetTimestamp ();
    // Critical block #1 ends here

    // Some code here

    // Critical block #2 begins here
    long lTimestamp3 = Stopwatch.GetTimestamp ();

    CallOtherComplex3rdPartyFunc (); // B

    long lTimestamp4 = Stopwatch.GetTimestamp ();
    // Critical block #2 ends here

    // Save timestamps for future analysis.
}

public int Main ( string[] sArgs )
{
    // Some code here

    int nCount = SomeFunc ();

    for ( int i = 0; i < nCount; i++ )
    {
        Thread oThread = new Thread ( ThreadFunc );
        oThread.Start ();
    }

    // Some code here

    return ( 0 );
}

上記の 2 つの重要なコード ブロックの実行時間をできるだけ正確に測定したいと考えています。AおよびBとしてマークされた 2 つの呼び出しは、潜在的に長い関数呼び出しであり、実行に数秒かかる場合もありますが、場合によっては数ミリ秒で完了する場合もあります。

上記のコードを多数のスレッドで実行しています - ユーザーの入力に応じて、1 から 200 スレッドの間のどこかです。このコードを実行するコンピューターには 2 ~ 16 のコアがあり、ユーザーはより弱いマシンでより低いスレッド数を使用します。

問題は、ABの両方が潜在的に長い関数であるため、実行中に少なくとも 1 回のコンテキスト スイッチが発生する可能性が非常に高く、おそらく複数回発生することです。そのため、コードが lTimestamp1 を取得すると、別のスレッドが実行を開始します (現在のスレッドは待機します)。最終的に、現在のスレッドが制御を取り戻し、lTimestamp2 を取得します。

これは、lTimestamp1lTimestamp2の間の期間には、スレッドが実際に実行されていなかった時間 (他のスレッドが実行されている間、スレッドが再度スケジュールされるのを待っていた時間) が含まれていることを意味します。ただし、ティック数はとにかく増加するため、期間は実際には

コード ブロック時間 = A + B +他のスレッドで費やされた時間

それだけでありたいと思っている間

コードブロック時間 = A + B

これは、多数のスレッドで特に問題になります。すべてのスレッドが実行される可能性があるため、問題のスレッドが実行される別の機会が得られる前に他のすべてのスレッドが実行される間、上記のタイミングが高くなります。

私の質問は次のとおりです。スレッドが実行されていない時間を何らかの方法で計算し、それに応じて上記のタイミングを調整することは可能ですか? その第 3 項を完全に、または少なくとも可能な限り削除 (減算) したいと思います。コードは何百万回も実行されるため、最終的なタイミングは多くのサンプルから計算され、平均化されます。

プロファイラー製品などを探しているわけではありません。アプリケーションは、これらのマークされたパーツの時間をできるだけ正確に計る必要があります。関数ABはサード パーティの関数であり、どのような方法でも変更できません。また、ナノ秒の精度で時間を測定するときに変動する可能性と、これらのサードパーティ関数内でオーバーヘッドが発生する可能性があることも認識していますが、それでもこの測定を行う必要があります。

C++ または x86 アセンブリ コードも同様に機能します。

編集:これを実装することは不可能のようです。以下の Scott のアイデア (GetThreadTimes を使用) は優れていますが、残念ながら GetThreadTimes() には欠陥のある API があり、正しいデータを返すことはほとんどありません。すべての返信に感謝します!

4

2 に答える 2

12

これは、ネイティブ API 呼び出しGetThreadTimesで実行できます。これを使用するCodeProjectに関する記事を次に示します。

2 番目のオプションは、QueryThreadCycleTimeを使用することです。これは時間ではありませんが、現在のスレッドが実行しているサイクル数を示します。

cycles->seconds多くのプロセッサ (特にモバイル プロセッサ) は固定速度で実行されないため、直接変換することはできないため、経過時間を秒単位で取得するために乗算できる定数がないことに注意してください。しかし、速度を変化させないプロセッサを使用している場合、サイクルからウォール クロック時間を取得するのは単純な数学の問題になります。

于 2011-09-29T15:12:51.420 に答える
2

Stopwatch.Start ()およびStopwatch.Stop()メソッドを使用して、時間測定を一時停止/継続できます。Elapsed/ ElapsedMilliseconds値はリセットされないためおそらくこれを利用できます。

スレッドのコンテキストスイッチについて-マネージコードで処理する方法はないと思うので、スレッドが一時停止された時間を除外することはできません

編集:

ベンチマークに関する興味深い記事:コンテキストスイッチを作成するのにどのくらい時間がかかりますか?

于 2011-09-29T14:37:43.257 に答える