使用Environment.TickCount()的经过时间 - 避免换行

时间:2009-04-16 14:45:30

标签: .net vb.net rollover gettickcount

绝对值是否保护以下代码免受Environment.TickCount换行?

If Math.Abs((Environment.TickCount And Int32.MaxValue) - StartTime) >= Interval Then
    StartTime = Environment.TickCount And Int32.MaxValue ' set up next interval
    ...
    ...
    ...
End If

有没有更好的方法来防范Environment.TickCount环绕?

(这是.NET 1.1。)

编辑 - 根据Microsoft Environment.TickCount帮助修改代码。

8 个答案:

答案 0 :(得分:8)

如果要测量的时间跨度不超过24.8天(任何更长的时间不能用有符号整数表示),避免包装问题很容易。在C#中:

int start = Environment.TickCount;
DoLongRunningOperation();
int elapsedTime = Environment.TickCount - start;

我对VB.NET不太熟悉,但只要您使用未经检查的数学运算,这将有效,您不必担心换行。

例如,如果Environment.TickCount从2147483600换行到-2147483596,则无关紧要。您仍然可以计算它们之间的差异,即100毫秒。

答案 1 :(得分:2)

只需使用stopwatch

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

它将返回一个int64而不是一个int32。此外,它更容易理解。

答案 2 :(得分:2)

由于我正在研究这个并偶然发现这个页面,我将分享一个小基准。

运行代码时,忽略每次测试的前1000个结果(用于预热)。同时忽略“忽略:”后打印的数字。它只是为了确保不会影响结果的编译器优化。

我的电脑上的结果:

Test1 1000000: 7ms    - Environment.TickCount
Test2 1000000: 1392ms - DateTime.Now.Ticks
Test3 1000000: 933ms  - Stopwatch.ElapsedMilliseconds

DateTime.Now.Ticks(测试2)和Stopwatch.ElapsedMilliseconds(测试3)都比Environment.TickCount(测试1)慢得多,但不足以引起注意,除非你执行大量的计算。例如,我正在调查这个因为我需要一种廉价的方式来获得紧张的游戏循环时间。

我认为我的解决方案必须是(未经测试的):

var elapsed = Environment.TickCount - start;
if (elapsed < 0)
   elapsed = Int32.MaxValue - start + Environment.TickCount;

基准代码:

void Main()
{
    Test1(1000);
    Test1(1000000);
    Test2(1000);
    Test2(1000000);
    Test3(1000);
    Test3(1000000);
}

void Test1(int c) {
    var sw = new Stopwatch();
    sw.Start();
    long sum = 0;
    for (var i = 0; i < c; i++)
    {
        sum += Environment.TickCount;
    }
    sw.Stop();
    Console.WriteLine("Test1 " + c + ": " + sw.ElapsedMilliseconds + "ms   (ignore: " + sum + ")");}

void Test2(int c) {
    var sw = new Stopwatch();
    sw.Start();
    long sum = 0;
    for (var i = 0; i < c; i++)
    {
        sum += DateTime.Now.Ticks;
    }
    sw.Stop();
    Console.WriteLine("Test2 " + c + ": " + sw.ElapsedMilliseconds + "ms   (ignore: " + sum + ")");
}
void Test3(int c) {
    var sw = new Stopwatch();
    sw.Start();
    long sum = 0;
    var stopwatch = new Stopwatch();
    stopwatch.Start();
    for (var i = 0; i < c; i++)
    {
        sum += stopwatch.ElapsedMilliseconds;
    }
    sw.Stop();
    Console.WriteLine("Test3 " + c + ": " + sw.ElapsedMilliseconds + "ms   (ignore: " + sum + ")");
}

答案 3 :(得分:1)

我不确定你要完成什么。看起来您正在尝试检测是否发生了特定间隔,如果是,则执行某些特定逻辑。

如果您为此目的使用Environment.TickCount,这可能会导致您无痛苦。正如你所指出的那样,这个价值大约可以每25天换一次。没有办法阻止这种情况发生,如果你试图在代码中遇到错误。

相反,为什么不这样做:

  • 使用实际的Timer来执行事件的内部和执行
  • 将StartTime存储为DateTime(或只是VB中的日期)值。该值具有更长的范围。你的应用程序运行时间不大,不足以让这个值环绕(操作系统需要很长时间才能重启:))。

答案 4 :(得分:0)

根据您的准确性,您为什么不使用:

DateTime.Now.Ticks

这个Ticks没有包装。

答案 5 :(得分:0)

您的代码很可能无效。如果应用程序要在Vista或更新的Windows版本上运行,可以使用GetTickCount64,但我认为情况并非如此,因为您使用的是.NET 1.1。

您的代码不应检查间隔是否相等,因为这可能会出错。使用Int64存储刻度,将最后处理的刻度存储在变量中,如果此变量大于新的刻度值,则表示您需要增加总刻度基数(Int64) - 假设您每隔12天检查一次,每次超过1次......;)

答案 6 :(得分:0)

我假设StartTime是一个正整数;我假设您之前做过StartTime =(Environment.TickCount和Int32.MaxValue)。

Abs() 无法解决问题。如果Interval&lt; 12.43天,当StartTime很小时,代码将正确计时。但有些时候,每当StartTime&gt; (24.86天 - 间隔),测试将比它应该更早通过,(TickCount和Int32.MaxValue)翻到0的时刻。另外,如果Interval&gt; 12.43天AND(TickCount和Int32.MaxValue)接近12.43天,测试永远不会通过。

执行Environment.TickCount And Int32.MaxValue不是必需的,也没有帮助。它产生一个正的有符号整数32,表示以ms为单位的时间,每隔24.86天回绕到0。替代方案难以理解,但它们让您的时间间隔达到49.71天,没有任何困难。原始Environment.TickCount在24.86天之后和之后的49.71天内回绕到Int32.MinValue。转换unchecked((Uint32)Environment.TickCount)使得[正]无符号整数32表示以ms为单位的时间,每49.71天回绕到0。 (因为Environment.TickCount调用Windows函数GetTickCount(),它返回一个DWORD(UInt32),并取消选中 - 将其强制转换为Int32。)

Environment.TickCount的回绕不会影响经过时间的测量。未经检查的减法始终给出正确的结果。

您可以将已用时间计算为已签名的int32(-24.86到+24.86天),如果您要比较独立时间或可能混合未来和过去,则更有用;或unsigned int32(0到+49.71天)。您可以将T0捕获为signed int32或unsigned int32;它对结果没有影响。如果T0或经过的时间未签名,则只需要[未选中]投射;有签名的T0和签名的经过时间,你不需要演员表。

捕获T0签名:(C#)

...
int iT0 = Environment.TickCount;  // T0 is NOW.
...
iElapsed = unchecked(Environment.TickCount - iT0)  // -24.81 to +24.81 days
uiElapsed = unchecked((uint)Environment.TickCount - (uint)iT0)  // 0 to +49.71 days
if (uiElapsed >= uiTimeLimit)  // IF time expired,
{
    iT0 = Environment.TickCount;  // For the next interval, new T0 is NOW.
    ...
}

捕获T0无符号(我的偏好; Environment.TickCount永远不应该签名):

...
uint uiT0 = unchecked((uint)Environment.TickCount);  // T0 is NOW.
...
iElapsed = unchecked(Environment.TickCount - (int)uiT0)  // -24.81 to +24.81 days
uiElapsed = unchecked((uint)Environment.TickCount - uiT0)  // 0 to +49.71 days
if (uiElapsed >= uiTimeLimit)  // IF time expired,
{
    uiT0 = unchecked((uint)Environment.TickCount);  // For the next interval, new T0 is NOW.
    ...
}

如果您的TimeLimit接近环绕限制(24.81或49.71天),则有可能在没有测试通过的情况下到期。在Elapsed&gt; = TimeLimit时,您必须至少测试一次。 (如果您不确定经常进行测试,可以在Elapsed上添加备份测试。如果Elapsed已经减少,它已经包装,所以时间必须结束。)

=====

对于超过49.71天的时间间隔,您可以计算uiElapsed包裹的次数,或者您可以计算Environment.TickCount包裹的次数。您可以将计数器组成64位值,模拟GetTickCount64()(仅适用于Windows Vista及更高版本)。 64位值具有全范围(2.92亿年)和全分辨率(1ms)。或者,您可以制作具有缩小范围和/或分辨率的32位值。检查包装的代码必须至少每49.71天执行一次,以确保不会检测到包装。

uint uiTickCountPrev = 0;
uint uiTickWrap = 0;
Int64 TickCount64;
...
uiTickCount = unchecked((uint)Environment.TickCount)  // 0 to +49.71 days
if (uiTickCount < uiTickCountPrev)  // IF uiElapsed decreased,
    uiWrapcount++;  count that uiElapsed wrapped.
uiElapsedPrev = uiElapsed;  // Save the previous value.
TickCount64 = (Int64)uiTickWrap << 32 + (Int64)uiTickCount;

注意:

Environment.TickCount从启动开始,以ms为单位,分辨率为10-16ms。

未经检查的 Int32 差异给出了时间差,-24.81到+24.81天,带有环绕。未经检查的32位整数减法溢出并包围到正确的值。 Environment.TickCount的标志永远不重要。示例,由一个包围:iBase = Environment.TickCount获取Int32.MaxValue;一个刻度后,Environment.TickCount包裹到Int32.MinValue;和unchecked(Environment.TickCount - iBase)产生1。

未经检查的 UInt32 差异给出时间差,0到+49.71天,带有环绕。 (经过的时间永远不会是负数,所以这是更好的选择。)未经检查的无符号32位整数减法溢出并包裹到正确的值。示例,由一个包围:iBase = Environment.TickCount获取UInt32.MaxValue;一个刻度后,Environment.TickCount包裹到UInt32.MinValue;和unchecked(Environment.TickCount - iBase)产生1。

答案 7 :(得分:0)

秒表是大多数用途的最佳选择。 Environment.TickCount仅适用于罕见的目的:

  1. 时间测量所需的时间本身至关重要,您需要获得最佳性能,因为您必须测量数百万或数十亿次。 Tickcount在这里有优势,因为它可以是约。比秒表()快20-25倍(在我的i7机器上测量)。

  2. 但你不需要测量的精确度超过1毫秒

  3. 您确定自己不得超过24天,因为这是最大值。 (翻转)TickCount的32位int的时间。

  4. 通常情况下,至少第1点并不重要。秒表(通常至少测量精度至少约1000倍(微秒范围和更好))通常是最合适的选择。

    除了.ElapsedMilliseconds,您还可以使用静态方法
    Stopwatch.GetTimestamp();

    原因如下:为了在我的旧款i7上循环播放秒表1000万次,需要1.3秒。 GetTickCount64略快一点,同样的时间不超过1秒。 所以选择一个并且不必为32位的东西而烦恼。

    当然,给定的性能只是一个(我的)硬件的近似值,可能会有所不同。

    但微软声称要尽一切努力使秒表(性能指标)在所有系统上都做得最好。似乎不太可能有人轻易击败那个:

    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_

    语录:

    &#34; QPC精度是否受到电源管理或Turbo Boost技术引起的处理器频率变化的影响? 否。如果处理器具有不变的TSC,则QPC不受这些更改的影响。 [...]&#34;

    &#34; QPC能否可靠地在多处理器系统,多核系统和具有超线程的系统上运行? 是

    如何确定并验证QPC在我的机器上运行? 您不需要执行此类检查。 [...] &#34;

    然而,阅读提供了更多详细信息,尤其是硬件和Windows版本的性能差异。但这可能是非常重要的,如果您需要实时性能,而Windows不是最适合的,并且肯定不是最合适的。