質問

Environment.TickCountタイムスパンの計算に使用しても大丈夫ですか?

int start = Environment.TickCount;
// Do stuff
int duration = Environment.TickCount - start;
Console.WriteLine("That took " + duration " ms");

TickCountが署名され、25日後にロールオーバーするため(32ビットすべてをヒットするのに50日かかりますが、数学を理解したい場合は、署名されたビットを破棄する必要があります)、リスクが高すぎて役に立たない。

代わりにDateTime.Nowを使用しています。これが最善の方法ですか?

DateTime start = DateTime.Now;
// Do stuff
TimeSpan duration = DateTime.Now - start;
Console.WriteLine("That took " + duration.TotalMilliseconds + " ms");
役に立ちましたか?

解決

ストップウォッチクラスを使用します。 msdnには適切な例があります: http://msdn.microsoft .com / en-us / library / system.diagnostics.stopwatch.aspx

    Stopwatch stopWatch = Stopwatch.StartNew();
    Thread.Sleep(10000);
    stopWatch.Stop();
    // Get the elapsed time as a TimeSpan value.
    TimeSpan ts = stopWatch.Elapsed;

他のヒント

Environment.TickCount は、 GetTickCount() WinAPIに基づいています関数。ミリ秒単位です ただし、実際の精度は約15.6 msです。したがって、短い時間間隔を測定することはできません(または0を取得します)

注:戻り値はInt32であるため、このカウンターは〜49.7日ごとにロールオーバーします。このような長い間隔の測定には使用しないでください。

DateTime.Ticks GetSystemTimeAsFileTime ()WinAPIに基づいています関数。それは100ナノ秒(数十マイクロ秒)です。 DateTime.Ticksの実際の精度はシステムに依存します。 XPでは、システムクロックの増分は、Environment.TickCountと同じ約15.6 msです。 Windows 7では、精度は1ミリ秒(Environemnt.TickCountはまだ15.6ミリ秒ですが)ですが、省電力スキームを使用すると(通常ラップトップで)、15.6ミリ秒まで低下する可能性があります。

ストップウォッチは、 QueryPerformanceCounter() WinAPI関数(ただし、システムで高解像度のパフォーマンスカウンターがサポートされていない場合は、DateTime.Ticksが使用されます)

StopWatchを使用する前に、2つの問題に注意してください:

  • マルチプロセッサシステムでは信頼できない場合があります(MS kb895980 kb896256
  • CPU周波数が変動すると信頼性が低下する可能性があります(こちらの記事を参照)

簡単なテストでシステムの精度を評価できます:

static void Main(string[] args)
{
    int xcnt = 0;
    long xdelta, xstart;
    xstart = DateTime.UtcNow.Ticks;
    do {
        xdelta = DateTime.UtcNow.Ticks - xstart;
        xcnt++;
    } while (xdelta == 0);

    Console.WriteLine("DateTime:\t{0} ms, in {1} cycles", xdelta / (10000.0), xcnt);

    int ycnt = 0, ystart;
    long ydelta;
    ystart = Environment.TickCount;
    do {
        ydelta = Environment.TickCount - ystart;
        ycnt++;
    } while (ydelta == 0);

    Console.WriteLine("Environment:\t{0} ms, in {1} cycles ", ydelta, ycnt);


    Stopwatch sw = new Stopwatch();
    int zcnt = 0;
    long zstart, zdelta;

    sw.Start();
    zstart = sw.ElapsedTicks; // This minimizes the difference (opposed to just using 0)
    do {
        zdelta = sw.ElapsedTicks - zstart;
        zcnt++;
    } while (zdelta == 0);
    sw.Stop();

    Console.WriteLine("StopWatch:\t{0} ms, in {1} cycles", (zdelta * 1000.0) / Stopwatch.Frequency, zcnt);
    Console.ReadKey();
}

ロールオーバーが心配なのはなぜですか?測定する期間が24.9日未満であり、相対期間を計算する限り、問題ありません。 (開始点と終了点で小なりまたは大なりの比較を直接実行するのではなく)その実行時間の一部にのみ関心がある限り、システムの実行時間は関係ありません。つまりこれ:

 int before_rollover = Int32.MaxValue - 5;
 int after_rollover = Int32.MinValue + 7;
 int duration = after_rollover - before_rollover;
 Console.WriteLine("before_rollover: " + before_rollover.ToString());
 Console.WriteLine("after_rollover: " + after_rollover.ToString());
 Console.WriteLine("duration: " + duration.ToString());

正しく印刷:

 before_rollover: 2147483642
 after_rollover: -2147483641
 duration: 13

符号ビットについて心配する必要はありません。 C#と同様に、C#はCPUにこれを処理させます。

これは、組み込みシステムで時間をカウントする際に以前に遭遇した一般的な状況です。ロールオーバーする前に私は決して比較しません<!> lt;たとえば、直接ロールオーバーします。私は常に減算を実行して、ロールオーバーを考慮に入れた期間を見つけ、その期間に基づいて他の計算を行います。

Environment.TickCountの機能を探しているが、新しいStopwatchオブジェクトを作成するオーバーヘッドがない場合、(Stopwatch.GetTimestamp()とともに)静的Stopwatch.Frequencyメソッドを使用して、長い期間を計算できます。 GetTimestamp()longを返すため、非常に長い時間(これを記述するために使用しているマシンでは100,000年以上)オーバーフローしません。また、最大解像度が10〜16ミリ秒である<=>よりもはるかに正確です。

使用

System.Diagnostics.Stopwatch

というプロパティがあります

EllapsedMilliseconds

Environment.TickCountは、他のソリューションよりもはるかに高速であるようです:

Environment.TickCount 71
DateTime.UtcNow.Ticks 213
sw.ElapsedMilliseconds 1273

次のコードによって測定値が生成されました。

static void Main( string[] args ) {
    const int max = 10000000;
    //
    //
    for ( int j = 0; j < 3; j++ ) {
        var sw = new Stopwatch();
        sw.Start();
        for ( int i = 0; i < max; i++ ) {
            var a = Environment.TickCount;
        }
        sw.Stop();
        Console.WriteLine( $"Environment.TickCount {sw.ElapsedMilliseconds}" );
        //
        //
        sw = new Stopwatch();
        sw.Start();
        for ( int i = 0; i < max; i++ ) {
            var a = DateTime.UtcNow.Ticks;
        }
        sw.Stop();
        Console.WriteLine( $"DateTime.UtcNow.Ticks {sw.ElapsedMilliseconds}" );
        //
        //
        sw = new Stopwatch();
        sw.Start();
        for ( int i = 0; i < max; i++ ) {
            var a = sw.ElapsedMilliseconds;
        }
        sw.Stop();
        Console.WriteLine( $"sw.ElapsedMilliseconds {sw.ElapsedMilliseconds}" );
    }
    Console.WriteLine( "Done" );
    Console.ReadKey();
}

これは、更新された<!> amp;最も有用な回答である可能性があるものの更新された要約です!<!> amp;このスレッドのコメント+追加のベンチマークとバリアント:

まず最初に:他の人がコメントで指摘しているように、ここ数年で物事は変わっており、<!> quot; modern <!> quot; Windows(Win XP ++)および.NET、および最新のハードウェアでは、Stopwatch()を使用しない理由はまったくないか、ほとんどありません。 MSDN 」をご覧ください。引用:

  

<!> quot; QPCの精度は、電源管理またはターボブーストテクノロジーによるプロセッサ周波数の変更の影響を受けますか?
  いいえ。 プロセッサに不変のTSCがある場合、QPCはこれらの種類の変更の影響を受けません。プロセッサに不変のTSCがない場合、QPCは、プロセッサ周波数の変更やTurbo Boostテクノロジの影響を受けないプラットフォームハードウェアタイマーに戻ります。

     

QPCは、マルチプロセッサシステム、マルチコアシステム、およびハイパースレッディングを備えたシステムで確実に動作しますか?
  はい

     

QPCがマシンで動作することを確認および検証するにはどうすればよいですか?
  このようなチェックを実行する必要はありません。

     

どのプロセッサに非不変TSCがありますか?   [..さらに読む..]   <!>引用;

しかし、Stopwatch()の精度が必要ない場合、または少なくともストップウォッチ(静的とインスタンスベース)およびその他の可能なバリアントのパフォーマンスについて正確に知りたい場合は、読み続けてください:

上記のベンチマークをcskwgから引き継ぎ、さらに多くの亜種のコードを拡張しました。私はVS 2017で数年前のi7 4700 MQとC#7で測定しました(より正確には、.NET 4.5.2でコンパイルされ、バイナリリテラルにもかかわらず、C#6です(これは文字列リテラルと')。特にStopwatch()のパフォーマンスは、上記のベンチマークと比較して改善されているようです。

これはループ内での1,000万回の繰り返しの結果の例です。いつものように、絶対値は重要ではありませんが、相対値でさえ他のハードウェアでは異なる場合があります。

32ビット、最適化なしのリリースモード:

  

測定:GetTickCount64()[ms]:275
  測定:Environment.TickCount [ms]:45
  測定値:DateTime.UtcNow.Ticks [ms]: 167
  測定:ストップウォッチ:.ElapsedTicks [ms]:277
  測定:ストップウォッチ:.ElapsedMilliseconds [ms]:548
  測定:静的Stopwatch.GetTimestamp [ms]:193
  測定:ストップウォッチ+ DateTimeへの変換[ms]:551
  DateTime.Now.Ticks [ms]と比較してください: 9010

32ビット、リリースモード、最適化:

  

測定:GetTickCount64()[ms]:198
  測定:Environment.TickCount [ms]:39
  測定値:DateTime.UtcNow.Ticks [ms]:66 (!)
  測定:ストップウォッチ:.ElapsedTicks [ms]:175
  測定:ストップウォッチ:.ElapsedMilliseconds [ms]: 491
  測定:静的Stopwatch.GetTimestamp [ms]:175
  測定:ストップウォッチ+ DateTimeへの変換[ms]: 510
  DateTime.Now.Ticks [ms]と比較してください: 8460

64ビット、最適化なしのリリースモード:

  

測定:GetTickCount64()[ms]:205
  測定:Environment.TickCount [ms]:39
  測定値:DateTime.UtcNow.Ticks [ms]: 127
  測定:ストップウォッチ:.ElapsedTicks [ms]:209
  測定:ストップウォッチ:.ElapsedMilliseconds [ms]:285
  測定:静的Stopwatch.GetTimestamp [ms]:187
  測定:ストップウォッチ+ DateTimeへの変換[ms]:319
  DateTime.Now.Ticks [ms]と比較してください:3040

64ビット、リリースモード、最適化:

  

測定:GetTickCount64()[ms]:148
  測定:Environment.TickCount [ms]:31 (それでも価値はありますか?)
  測定値:DateTime.UtcNow.Ticks [ms]:76 (!)
  測定:ストップウォッチ:.ElapsedTicks [ms]:178
  測定:ストップウォッチ:.ElapsedMilliseconds [ms]:226
  測定:静的Stopwatch.GetTimestamp [ms]:175
  測定:ストップウォッチ+ DateTimeへの変換[ms]:246
  DateTime.Now.Ticks [ms]と比較してください:3020

非常に興味深いのは、ストップウォッチ時間を印刷するためにDateTime値を作成することは、ほとんど費用がかからないように見えることです。実用的な方法よりもアカデミックな方法に興味があるのは、静的なストップウォッチが(予想どおり)少し速いということです。いくつかの最適化ポイントは非常に興味深いものです。 たとえば、32ビットのみのStopwatch.ElapsedMillisecondsが、他のバリアント(静的なものなど)と比較して非常に遅い理由を説明できません。これとDateTime.Nowは、64ビットで速度を2倍以上にします。

見ることができます:数百万の実行についてのみ、ストップウォッチの時間が重要になり始めます。これが本当に当てはまる場合(ただし、マイクロ最適化が早すぎることに注意してください)、GetTickCount64()を使用すると興味深い場合がありますが、特に DateTime.UtcNow を使用すると、64ビット(長い)タイマーがストップウォッチよりも精度は低くなりますが、高速であるため、32ビットの<!> quot; ugly <!> quotをいじる必要はありません。 Environment.TickCount。

予想どおり、DateTime.Nowはすべての中で最も遅いです。

実行すると、コードは現在のストップウォッチの精度なども取得します。

完全なベンチマークコードは次のとおりです。

using System.Diagnostics;
using System.Runtime.InteropServices;
using System.Threading;
using static System.Environment;

[...]

    [DllImport("kernel32.dll") ]
    public static extern UInt64 GetTickCount64(); // Retrieves a 64bit value containing ticks since system start

    static void Main(string[] args)
    {
        const int max = 10_000_000;
        const int n = 3;
        Stopwatch sw;

        // Following Process&Thread lines according to tips by Thomas Maierhofer: https://codeproject.com/KB/testing/stopwatch-measure-precise.aspx
        // But this somewhat contradicts to assertions by MS in: https://msdn.microsoft.com/en-us/library/windows/desktop/dn553408%28v=vs.85%29.aspx?f=255&MSPPError=-2147217396#Does_QPC_reliably_work_on_multi-processor_systems__multi-core_system__and_________systems_with_hyper-threading
        Process.GetCurrentProcess().ProcessorAffinity = new IntPtr(1); // Use only the first core
        Process.GetCurrentProcess().PriorityClass = ProcessPriorityClass.High;
        Thread.CurrentThread.Priority = ThreadPriority.Highest;
        Thread.Sleep(2); // warmup

        Console.WriteLine($"Repeating measurement {n} times in loop of {max:N0}:{NewLine}");
        for (int j = 0; j < n; j++)
        {
            sw = new Stopwatch();
            sw.Start();
            for (int i = 0; i < max; i++)
            {
                var tickCount = GetTickCount64();
            }
            sw.Stop();
            Console.WriteLine($"Measured: GetTickCount64() [ms]: {sw.ElapsedMilliseconds}");
            //
            //
            sw = new Stopwatch();
            sw.Start();
            for (int i = 0; i < max; i++)
            {
                var tickCount = Environment.TickCount; // only int capacity, enough for a bit more than 24 days
            }
            sw.Stop();
            Console.WriteLine($"Measured: Environment.TickCount [ms]: {sw.ElapsedMilliseconds}");
            //
            //
            sw = new Stopwatch();
            sw.Start();
            for (int i = 0; i < max; i++)
            {
                var a = DateTime.UtcNow.Ticks;
            }
            sw.Stop();
            Console.WriteLine($"Measured: DateTime.UtcNow.Ticks [ms]: {sw.ElapsedMilliseconds}");
            //
            //
            sw = new Stopwatch();
            sw.Start();
            for (int i = 0; i < max; i++)
            {
                var a = sw.ElapsedMilliseconds;
            }
            sw.Stop();
            Console.WriteLine($"Measured: Stopwatch: .ElapsedMilliseconds [ms]: {sw.ElapsedMilliseconds}");
            //
            //
            sw = new Stopwatch();
            sw.Start();
            for (int i = 0; i < max; i++)
            {
                var a = Stopwatch.GetTimestamp();
            }
            sw.Stop();
            Console.WriteLine($"Measured: static Stopwatch.GetTimestamp [ms]: {sw.ElapsedMilliseconds}");
            //
            //
            DateTime dt=DateTime.MinValue; // just init
            sw = new Stopwatch();
            sw.Start();
            for (int i = 0; i < max; i++)
            {
                var a = new DateTime(sw.Elapsed.Ticks); // using variable dt here seems to make nearly no difference
            }
            sw.Stop();
            //Console.WriteLine($"Measured: Stopwatch+conversion to DateTime [s] with millisecs: {dt:s.fff}");
            Console.WriteLine($"Measured: Stopwatch+conversion to DateTime [ms]:  {sw.ElapsedMilliseconds}");

            Console.WriteLine();
        }
        //
        //
        sw = new Stopwatch();
        var tickCounterStart = Environment.TickCount;
        sw.Start();
        for (int i = 0; i < max/10; i++)
        {
            var a = DateTime.Now.Ticks;
        }
        sw.Stop();
        var tickCounter = Environment.TickCount - tickCounterStart;
        Console.WriteLine($"Compare that with DateTime.Now.Ticks [ms]: {sw.ElapsedMilliseconds*10}");

        Console.WriteLine($"{NewLine}General Stopwatch information:");
        if (Stopwatch.IsHighResolution)
            Console.WriteLine("- Using high-resolution performance counter for Stopwatch class.");
        else
            Console.WriteLine("- Using high-resolution performance counter for Stopwatch class.");

        double freq = (double)Stopwatch.Frequency;
        double ticksPerMicroSec = freq / (1000d*1000d) ; // microsecond resolution: 1 million ticks per sec
        Console.WriteLine($"- Stopwatch accuracy- ticks per microsecond (1000 ms): {ticksPerMicroSec:N1}");
        Console.WriteLine(" (Max. tick resolution normally is 100 nanoseconds, this is 10 ticks/microsecond.)");

        DateTime maxTimeForTickCountInteger= new DateTime(Int32.MaxValue*10_000L);  // tickCount means millisec -> there are 10.000 milliseconds in 100 nanoseconds, which is the tick resolution in .NET, e.g. used for TimeSpan
        Console.WriteLine($"- Approximated capacity (maxtime) of TickCount [dd:hh:mm:ss] {maxTimeForTickCountInteger:dd:HH:mm:ss}");
        // this conversion from seems not really accurate, it will be between 24-25 days.
        Console.WriteLine($"{NewLine}Done.");

        while (Console.KeyAvailable)
            Console.ReadKey(false);
        Console.ReadKey();
    }

ストップウォッチを使用する必要があります代わりにクラス。

Environment.TickCountを使用する理由:

  1. StopwatchクラスはCompact Frameworkにはありません。
  2. ストップウォッチはTickCountと同じ基本的なタイミングメカニズムを使用するため、結果の精度は多少なりとも変わりません。
  3. TickCountのラップアラウンドの問題は、宇宙的にヒットする可能性は低い(コンピューターを27日間実行したままにして、ラップにまたがる時間を測定する必要がある-瞬間)、そしてあなたがそれをヒットしたとしても、結果は巨大な負の時間範囲になります(したがって、それは一種の目立つでしょう)。

そうは言っても、ストップウォッチを使用できる場合は、ストップウォッチの使用をお勧めします。または、約1分かかり、Environment.TickCountをラップするストップウォッチのようなクラスを記述できます。

ところで、ストップウォッチのドキュメントには、基礎となるタイマーメカニズムのラップアラウンドの問題について言及しているものは何もありません。そのため、ストップウォッチが同じ問題に苦しんでいることはまったく驚きません。しかし、繰り返しますが、心配する時間はありません。

ストップウォッチのクラスにラップするつもりでしたが、Grzenioはすでに正しいことを言っていたので、彼にアップティックを与えます。そのようなカプセル化は、どちらの方法がより良いかに関する決定を除外し、これは時間の経過とともに変化する可能性があります。一部のシステムで時間を費やすことができることにショックを受けたことを覚えているので、最高のテクニックを実装できる場所を1つ持つことは非常に重要です。

ワンショットタイミングの場合、記述はさらに簡単です

Stopwatch stopWatch = Stopwatch.StartNew();
...dostuff...
Debug.WriteLine(String.Format("It took {0} milliseconds",
                              stopWatch.EllapsedMilliseconds)));

ElapsedTicksフィールドが長いことを考えると、TickCountの宇宙的に起こりそうもないラップアラウンドは、StopWatchにとってはそれほど心配ではないと思います。私のマシンでは、StopWatchは毎秒2.4e9ティックの高解像度です。そのレートであっても、ティックフィールドをオーバーフローさせるには121年以上かかります。もちろん、私はカバーの下で何が起こっているのかわからないので、塩の粒でそれを取る。ただし、StopWatchのドキュメントにはラップアラウンドの問題すら記載されておらず、TickCountのドキュメントには記載されていません。

ライセンス: CC-BY-SA帰属
所属していません StackOverflow
scroll top