当前位置:  开发笔记 > 编程语言 > 正文

Environment.TickCount与DateTime.Now

如何解决《Environment.TickCount与DateTime.Now》经验,为你挑选了8个好方法。

是否可以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");

mistika.. 88

Environment.TickCount基于GetTickCount() WinAPI函数.它以毫秒为单位但实际精度约为15.6 ms.所以你不能测量更短的时间间隔(或者你会得到0)

注意:返回的值是Int32,因此此计数器每隔~49.7天滚动一次.你不应该用它来测量这么长的间隔.

DateTime.Ticks基于GetSystemTimeAsFileTime()WinAPI函数.它在100s纳秒(十分之一的微观键).DateTime.Ticks的实际精度取决于系统.在XP上,系统时钟的增量约为15.6 ms,与Environment.TickCount相同.在Windows 7上,它的精度是1毫秒(而Environemnt.TickCount仍然是15.6毫秒),但如果使用省电方案(通常在笔记本电脑上),它也可以降至15.6毫秒.

秒表基于QueryPerformanceCounter() WinAPI函数(但如果您的系统不支持高分辨率性能计数器,则使用DateTime.Ticks)

在使用StopWatch之前,请注意两个问题:

它在多处理器系统上可能不可靠(参见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();
}

你所指出的"问题"现在基本上是无关紧要的.它们适用于Windows 2000,Windows 2003和Windows XP.如果您正在运行这些系统,那么您的计时器是否准确就会遇到更大的问题. (4认同)

QueryPerformanceCounter()现在是可靠的:请参阅https://blogs.msdn.microsoft.com/oldnewthing/20140822-01/?p=163和http://msdn.microsoft.com/en-us/library/windows/desktop /dn553408(v=vs.85).aspx:_"问:QPC能否可靠地用于多处理器系统,多核系统和具有超线程的系统?答:是的","问:QPC准确度是否受到影响由电源管理或Turbo Boost技术引起的处理器频率变化?答:没有"_ (3认同)


Grzenio.. 69

使用秒表课程.在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;


小智.. 24

你为什么担心翻车?只要您测量的持续时间低于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处理这个问题.

这是我在嵌入式系统中计算时间之前遇到的常见情况.例如,我永远不会直接比较afterrollover

1> mistika..:


Environment.TickCount基于GetTickCount() WinAPI函数.它以毫秒为单位但实际精度约为15.6 ms.所以你不能测量更短的时间间隔(或者你会得到0)

注意:返回的值是Int32,因此此计数器每隔~49.7天滚动一次.你不应该用它来测量这么长的间隔.

DateTime.Ticks基于GetSystemTimeAsFileTime()WinAPI函数.它在100s纳秒(十分之一的微观键).DateTime.Ticks的实际精度取决于系统.在XP上,系统时钟的增量约为15.6 ms,与Environment.TickCount相同.在Windows 7上,它的精度是1毫秒(而Environemnt.TickCount仍然是15.6毫秒),但如果使用省电方案(通常在笔记本电脑上),它也可以降至15.6毫秒.

秒表基于QueryPerformanceCounter() WinAPI函数(但如果您的系统不支持高分辨率性能计数器,则使用DateTime.Ticks)

在使用StopWatch之前,请注意两个问题:

它在多处理器系统上可能不可靠(参见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();
}


你所指出的"问题"现在基本上是无关紧要的.它们适用于Windows 2000,Windows 2003和Windows XP.如果您正在运行这些系统,那么您的计时器是否准确就会遇到更大的问题.
QueryPerformanceCounter()现在是可靠的:请参阅https://blogs.msdn.microsoft.com/oldnewthing/20140822-01/?p=163和http://msdn.microsoft.com/en-us/library/windows/desktop /dn553408(v=vs.85).aspx:_"问:QPC能否可靠地用于多处理器系统,多核系统和具有超线程的系统?答:是的","问:QPC准确度是否受到影响由电源管理或Turbo Boost技术引起的处理器频率变化?答:没有"_

2> Grzenio..:

使用秒表课程.在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;



3> 小智..:

你为什么担心翻车?只要您测量的持续时间低于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处理这个问题.

这是我在嵌入式系统中计算时间之前遇到的常见情况.例如,我永远不会直接比较afterrollover


它会在50天后爆炸.在服务器上不好.
最佳答案IMO.这也不会"在50天后爆炸",除非你试图用它来追踪超过50天的时间段,这似乎不太可能.+1用于以易于测试的方式包含此工作的可靠证据.StopWatch是一个无用的类,由于多核CPU的问题,现在几乎每台PC都有.

4> Joel Coehoor..:

你可能想要System.Diagnostics.StopWatch.



5> Mark..:

如果您正在寻找功能Environment.TickCount但没有创建新Stopwatch对象的开销,则可以使用静态Stopwatch.GetTimestamp()方法(以及Stopwatch.Frequency)来计算长时间跨度.因为GetTimestamp()返回a long,它不会溢出非常长的时间(超过10万年,在我用来写这个的机器上).它也比Environment.TickCount其最大分辨率为10到16毫秒更精确.



6> Martin Kool..:

使用

System.Diagnostics.Stopwatch

它有一个叫做的属性

EllapsedMilliseconds



7> cskwg..:

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();
}



8> Philm..:

这里有一个更新和更新的摘要,可能是这个主题中最有用的答案和评论+额外的基准和变体:

第一件事:正如其他人在评论中指出的那样,事情已经改变了过去几年,并且使用"现代"Windows(Win XP ++)和.NET,现代硬件没有或几乎没有理由不使用秒表().有关详细信息,请参阅MSDN 语录:

"QPC精度是否受到电源管理或Turbo Boost技术引起的处理器频率变化的影响?
否.如果处理器具有不变的TSC,则QPC不受这些变化的影响.如果处理器没有不变的TSC, QPC将恢复为不受处理器频率变化或Turbo Boost技术影响的平台硬件定时器.

QPC能否可靠地用于多处理器系统,多核系统和具有超线程的系统?

如何确定并验证QPC在我的机器上是否正常工作?
您不需要执行此类检查.

哪些处理器具有非不变的TSC?[..进一步..]"

但是如果你不需要秒表()的精确度或者至少想要确切知道秒表(静态与基于实例)和其他可能变体的性能,请继续阅读:

我从cskwg接管了上面的基准测试,并扩展了代码以获得更多变体.我用几年前的i7 4700 MQ和C#7来测量VS 2017(更准确地说,用.NET 4.5.2编译,尽管是二进制文字,但它是C#6(用于此:字符串文字和'使用静态) ').特别是与上述基准相比,秒表()表现似乎有所改善.

这是一个循环中1000万次重复结果的例子,一如既往,绝对值并不重要,但即使相对值在其他硬件上也可能不同:

32位,没有优化的发布模式:

测量:GetTickCount64()[ms]:275
测量:Environment.TickCount [ms]:45
测量:DateTime.UtcNow.Ticks [ms]:167
测量:秒表:.ElapsedTicks [ms]:277
测量:秒表:.ElapsedMilliseconds [ ms]:548
测量:静态秒表.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
测量:静态秒表.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
测量:静态秒表.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
测量:静态秒表.GetTimestamp [ms]:175
测量:秒表+转换为DateTime [ms]:246
将其与DateTime.Now.Ticks [ms]进行比较: 3020

可能非常有趣的是,创建一个DateTime值来打印秒表时间似乎几乎没有任何成本.有趣的是更具学术性而非实用性的方式是静态秒表略快(如预期的那样).一些优化点非常有趣.例如,我无法解释为什么只有32位的Stopwatch.ElapsedMilliseconds与其他变种相比是如此之慢,例如静态变体.这和DateTime.Now比64位速度加倍.

你可以看到:只有数百万次执行,秒表的时间开始变得重要.如果真的是这种情况(但是要过早地提防微优化),使用GetTickCount64()可能会很有趣,但特别是使用DateTime.UtcNow时,你有一个64位(长)计时器,其精度低于秒表,但更快,这样你就不必乱用32位"丑陋"的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();
    }

推荐阅读
农大军乐团_697
这个屌丝很懒,什么也没留下!
DevBox开发工具箱 | 专业的在线开发工具网站    京公网安备 11010802040832号  |  京ICP备19059560号-6
Copyright © 1998 - 2020 DevBox.CN. All Rights Reserved devBox.cn 开发工具箱 版权所有