是否可以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 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频率变化不可靠(读此文章)
您可以通过简单的测试评估系统的精度:
1> mistika..: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();
}
使用秒表课程.在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.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
你可能想要System.Diagnostics.StopWatch
.
如果您正在寻找功能Environment.TickCount
但没有创建新Stopwatch
对象的开销,则可以使用静态Stopwatch.GetTimestamp()
方法(以及Stopwatch.Frequency
)来计算长时间跨度.因为GetTimestamp()
返回a long
,它不会溢出非常长的时间(超过10万年,在我用来写这个的机器上).它也比Environment.TickCount
其最大分辨率为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(); }
这里有一个更新和更新的摘要,可能是这个主题中最有用的答案和评论+额外的基准和变体:
第一件事:正如其他人在评论中指出的那样,事情已经改变了过去几年,并且使用"现代"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(); }