使用Environment.TickCount
计算时间跨度是否可行?
int start = Environment.TickCount;
// Do stuff
int duration = Environment.TickCount - start;
Console.WriteLine("That took " + duration " ms");
因为TickCount
已签名并且将在25天后翻转(需要50天才能达到所有32位,但如果您想对数学有任何意义,则必须废弃已签名的位),似乎喜欢这样做太冒险了。
我正在使用DateTime.Now
。这是最好的方法吗?
DateTime start = DateTime.Now;
// Do stuff
TimeSpan duration = DateTime.Now - start;
Console.WriteLine("That took " + duration.TotalMilliseconds + " ms");
答案 0 :(得分:86)
Environment.TickCount 基于GetTickCount() WinAPI功能。它以毫秒为单位 但它的实际精度约为15.6毫秒。所以你不能测量更短的时间间隔(或者你会得到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之前,请注意两个问题:
您可以通过简单的测试评估系统的精度:
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();
}
答案 1 :(得分:67)
使用秒表课程。在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;
答案 2 :(得分: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处理这个。
这是我在嵌入式系统中计算时间之前遇到的常见情况。我永远不会比较beforerollover<例如,直接后滚动。我总是会执行减法来找到考虑翻转的持续时间,然后根据持续时间进行任何其他计算。
答案 3 :(得分:10)
答案 4 :(得分:9)
如果您正在寻找Environment.TickCount
的功能但没有创建新Stopwatch
对象的开销,则可以使用静态Stopwatch.GetTimestamp()
方法(以及Stopwatch.Frequency
)计算长时间跨度。因为GetTimestamp()
返回long
,所以它不会在非常长的时间内溢出(超过10万年,在我用来编写它的机器上)。它也比Environment.TickCount
准确得多,其最大分辨率为10到16毫秒。
答案 5 :(得分:8)
使用
System.Diagnostics.Stopwatch
它有一个名为
的属性EllapsedMilliseconds
答案 6 :(得分:6)
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();
}
答案 7 :(得分:5)
这是一个更新和更新的摘要,可能是最有用的答案&amp;此主题中的评论+额外的基准和变体:
首先要做的事情:正如其他人在评论中指出的那样,事情在过去的几年里发生了变化,并且现代化了#34; Windows(Win XP ++)和.NET以及现代硬件没有或几乎没有理由不使用Stopwatch()。 有关详细信息,请参阅MSDN。语录:
&#34; QPC精度是否受到电源管理或Turbo Boost技术引起的处理器频率变化的影响? 否。如果处理器具有不变的TSC ,则QPC不受这些更改的影响。如果处理器没有不变的TSC,QPC将恢复到不受处理器频率变化或Turbo Boost技术影响的平台硬件定时器。
QPC能否可靠地在多处理器系统,多核系统和具有超线程的系统上工作? 是
如何确定并验证QPC在我的机器上是否正常工作? 您不需要执行此类检查。
哪些处理器具有非常数TSC? [..进一步..] &#34;
但是如果你不需要秒表()的精确度,或者至少想知道秒表(静态与基于实例)和其他可能变体的性能,请继续阅读:
我从cskwg接管了上面的基准测试,并扩展了代码以获得更多变体。我用VS 2017测量了几年前的i7 4700 MQ和C#7(更准确地说,用.NET 4.5.2编译,尽管是二进制文字,但它是C#6(用于此:字符串文字和&#39) ;使用静态&#39;。特别是与上述基准相比,秒表()表现似乎有所改善。
这是一个循环中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位&#34;丑陋&#34; 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();
}
答案 8 :(得分:0)
您应该使用Stopwatch课程。
答案 9 :(得分:0)
我使用Environment.TickCount,因为:
话虽如此,我还建议使用秒表,如果你可以使用的话。或者你可以花大约1分钟写一个类似秒表的类来包装Environment.TickCount。
顺便说一句,我在秒表文档中没有看到提及底层计时器机制的环绕问题,所以我不会惊讶地发现秒表遇到了同样的问题。但同样,我也不会花时间担心它。答案 10 :(得分:0)
我打算把它包装成一个秒表课,但Grzenio已经说了正确的话,所以我会给他一个提升。这种封装因素决定了哪种方式更好,这可以及时改变。我记得在某些系统上花费多少时间感到震惊,因此拥有一个可以实现最佳技术的地方非常重要。
答案 11 :(得分:0)
对于一次性定时,写入
更简单Stopwatch stopWatch = Stopwatch.StartNew();
...dostuff...
Debug.WriteLine(String.Format("It took {0} milliseconds",
stopWatch.EllapsedMilliseconds)));
考虑到ElapsedTicks字段很长,我猜想TickCount中的宇宙不太可能的回绕甚至不太关注StopWatch。在我的机器上,StopWatch是高分辨率,每秒2.4e9滴答。即使按照这个速度,也需要超过121年的时间才能超过蜱虫领域。当然,我不知道封面下发生了什么,所以拿出一粒盐。但是,我注意到StopWatch的文档甚至没有提到回绕问题,而TickCount的文档也没有。
答案 12 :(得分:0)
如前所述,过渡可能会在24.9天后发生,或者,如果使用uint cast,则可能会在49.8天后发生。 因为我不想pInvoke GetTickCount64,所以我写了这个溢出补偿。示例代码使用“字节”保持数字方便。请查看它,它仍然可能包含错误:
using System;
namespace ConsoleApp1 {
class Program {
//
static byte Lower = byte.MaxValue / 3;
static byte Upper = 2 * byte.MaxValue / 3;
//
///<summary>Compute delta between two TickCount values reliably, because TickCount might wrap after 49.8 days.</summary>
static short Delta( byte next, byte ticks ) {
if ( next < Lower ) {
if ( ticks > Upper ) {
return (short) ( ticks - ( byte.MaxValue + 1 + next ) );
}
}
if ( next > Upper ) {
if ( ticks < Lower ) {
return (short) ( ( ticks + byte.MaxValue + 1 ) - next );
}
}
return (short) ( ticks - next );
}
//
static void Main( string[] args ) {
// Init
Random rnd = new Random();
int max = 0;
byte last = 0;
byte wait = 3;
byte next = (byte) ( last + wait );
byte step = 0;
// Loop tick
for ( byte tick = 0; true; ) {
//
short delta = Delta( next, tick );
if ( delta >= 0 ) {
Console.WriteLine( "RUN: last: {0} next: {1} tick: {2} delta: {3}", last, next, tick, delta );
last = tick;
next = (byte) ( last + wait );
}
// Will overflow to 0 automatically
step = (byte) rnd.Next( 0, 11 );
tick += step;
max++; if ( max > 99999 ) break;
}
}
}
}
答案 13 :(得分:0)
我发现对此新功能进行了一些快速测量(优化,发布64位,1000mio循环):
Environment.TickCount: 2265
Environment.TickCount64: 2531
DateTime.UtcNow.Ticks: 69016
未优化代码的测量结果相似。 测试代码:
static void Main( string[] args ) {
long start, end, length = 1000 * 1000 * 1000;
start = Environment.TickCount64;
for ( int i = 0; i < length; i++ ) {
var a = Environment.TickCount;
}
end = Environment.TickCount64;
Console.WriteLine( "Environment.TickCount: {0}", end - start );
start = Environment.TickCount64;
for ( int i = 0; i < length; i++ ) {
var a = Environment.TickCount64;
}
end = Environment.TickCount64;
Console.WriteLine( "Environment.TickCount64: {0}", end - start );
start = Environment.TickCount64;
for ( int i = 0; i < length; i++ ) {
var a = DateTime.UtcNow.Ticks;
}
end = Environment.TickCount64;
Console.WriteLine( "DateTime.UtcNow.Ticks: {0}", end - start );
}