10

xミリ秒かかるまでにループを実行する必要がある最大カウントを取得したい。

たとえば。

int GetIterationsForExecutionTime(int ms)
{
    int count = 0;
    /* pseudocode 
    do
        some code here
        count++;
    until executionTime > ms
    */

    return count;
}

どうすればこのようなことを達成できますか?

4

4 に答える 4

30

xミリ秒かかるまでにループを実行する必要がある最大カウントを取得したい。

まず、単純にそれをしないでください。特定のミリ秒数待機する必要がある場合は、ループでビジー待機しないでください。むしろ、タイマーを開始して戻ります。タイマーが作動したら、中断したところから再開するメソッドを呼び出します。このTask.Delay方法は、使用するのに適している場合があります。タイマーの詳細を処理します。

あなたの質問が実際にコードにかかる時間を計る方法である場合は、単に良いタイマー以上のものを必要とします. 正確なタイミングを得るには、多くの芸術と科学があります。

まず、これらのタイミングには常に使用Stopwatchし、決して使用しないでください。ストップウォッチは、経過時間DateTime.Nowを知らせる高精度のタイマーとして設計されています。ドクター・フーをまだ見る時間かどうかを知らせるための低精度タイマーです。オリンピックのレースの時間を計るのに壁掛け時計は使いません。手に入れることができる最高精度のストップウォッチを使用します。したがって、提供されているものを使用してください。DateTime.Now

次に、 C# コードは Just In Time でコンパイルされることを覚えておく必要があります。したがって、最初にループを通過するときは、ループが呼び出すコードを分析するジッターのコストのために、その後のすべての回よりも数百倍または数千倍のコストがかかる可能性があります。ループの「ウォーム」コストを測定する場合は、タイミングを開始するにループを 1 回実行する必要があります。jit 時間を含む平均コストを測定する場合は、平均が正しく機能するように、妥当な回数の試行を構成する回数を決定する必要があります。

第三に、ランニング中はリードウェイトを着用していないことを確認する必要があります。デバッグ中は決してパフォーマンス測定を行わないでください。これを行う人の数は驚くべきものです。デバッガーを使用している場合、ランタイムはデバッガーとやり取りして、必要なデバッグ エクスペリエンスが得られていることを確認する場合があり、その会話には時間がかかります。ジッタは、通常よりも悪いコードを生成するため、デバッグ エクスペリエンスはより一貫したものになります。ガベージ コレクターはあまり積極的に収集していません。等々。パフォーマンス測定は常にデバッガーの外部で実行し、最適化をオンにしてください。

第 4 に、仮想メモリ システムはジッタと同様のコストを課すことを覚えておいてください。マネージド プログラムを既に実行している場合、または最近実行したことがある場合、必要な CLR のページはおそらく "ホット" (既に RAM 内) にあり、高速です。そうでない場合、ページはディスク上でコールドである可能性があり、ページ フォールトを発生させる必要があります。これにより、タイミングが大幅に変わる可能性があります。

第 5 に、ジッターによって予期しない最適化が行われる可能性があることに注意してください。時間を計る場合:

// Let's time addition!
for (int i = 0; i < 1000000; ++i) { int j = i + 1; }

ジッタは完全にループ全体を削除する権利の範囲内です。ループは、プログラム内の他の場所で使用されている値を計算せず、それを完全に削除して、時間をゼロにすることができます。そうしますか?多分。そうでないかもしれない。それはジッター次第です。計算された値が実際に何らかの形で使用される現実的なコードのパフォーマンスを測定する必要があります。ジッターは、それらを最適化して取り除くことができないことを認識します。

第 6 に、大量のガベージを作成するテストのタイミングは、ガベージ コレクタによってずれることがあります。2 つのテストがあるとします。1 つは大量のゴミを生成するテストで、もう 1 つは少量のゴミを生成するテストです。最初のテストが収集なしで実行できたが、2 番目のテストが収集をトリガーした場合、最初のテストによって生成されたガベージのコレクションのコストは、2 番目のテストの実行にかかった時間に "課金" できます。テストで大量のゴミが生成される場合は、(1) そもそもテストが現実的かどうかを検討してください。実際のプログラムがどのように動作するかを適切に推測することはできないため、非現実的なプログラムのパフォーマンス測定を行っても意味がありません。(2) ガベージ コレクションのコストを、ガベージを生成したテストに請求する必要がありますか? もしそうなら、

第 7 に、スレッドを自由に切り替えることができるマルチスレッドのマルチプロセッサ環境でコードを実行していて、スレッド クォンタム (オペレーティング システムが別のスレッドを実行する機会を得るまでにオペレーティング システムが別のスレッドを与える時間) が約16ミリ秒。16 ミリ秒は、約5000 万のプロセッサ サイクルです。測定しようとしている数百万のプロセッサ サイクルのうちの 1 つ内でスレッドの切り替えが発生した場合、ミリ秒未満の操作の正確なタイミングを考え出すことは非常に困難です。それを考慮してください。

于 2012-04-11T18:36:31.867 に答える
15
var sw = Stopwatch.StartNew();
...
long  elapsedMilliseconds = sw.ElapsedMilliseconds;
于 2012-04-11T17:49:11.680 に答える
6

Stopwatchクラスを使用することもできます。

int GetIterationsForExecutionTime(int ms)
{
    int count = 0;
    Stopwatch stopwatch = new Stopwatch();
    stopwatch.Start();        
    do
    {
        // some code here
        count++;
    } while (stopwatch.ElapsedMilliseconds < ms);

    stopwatch.Stop();
    return count;
}
于 2012-04-11T17:48:21.663 に答える
-1

Eric Lippert の良い点。私はしばらくベンチマークと単体テストを行っていましたが、JIT コンパイルの原因となるコードの最初のパスはすべて破棄することをお勧めします。したがって、ループとストップウォッチを使用するベンチマーク コードでは、これをループの最後に配置することを忘れないでください。

                // JIT optimization.
                if (i == 0)
                {
                    // Discard every result you've collected.
                    // And restart the timer.
                    stopwatch.Restart();
                }
于 2017-01-09T00:06:10.220 に答える