1

绝对值是否保护以下代码不被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帮助修改代码。

4

8 回答 8

8

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

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

我对 VB.NET 不是很熟悉,但只要您使用未经检查的数学,这将起作用并且您不必担心换行。

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

于 2009-11-29T18:41:37.007 回答
3

因为我正在研究这个并偶然发现了这个页面,所以我将分享一个我制作的小基准。

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

我的电脑上的结果:

Test1 1000000: 7ms - Environment.TickCount
Test2 1000000: 1392ms - DateTime.Now.Ticks
Test3 1000000: 933ms - 秒表.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 + ")");
}
于 2015-08-17T12:11:47.373 回答
2

只需使用秒表

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。另外它更容易理解。

于 2009-11-29T18:47:05.297 回答
1

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

如果您为此目的使用 Environment.TickCount,这可能会给您带来无穷无尽的痛苦。正如您所指出的,这个值可以并且将大约每 25 天包装一次。没有办法防止这种情况发生,如果您尝试这样做,您最终会在代码中遇到错误。

相反,为什么不执行以下操作:

  • 使用实际的 Timer 进行事件的内部和执行
  • 将 StartTime 存储为 DateTime(或 VB 中的 Date)值。这个值的范围要长得多。您的应用程序不太可能运行足够长的时间以使该值回绕(操作系统将需要在此之前很久重新启动:))。
于 2009-04-16T14:52:15.890 回答
0

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

DateTime.Now.Ticks

这个 Ticks 没有包装。

于 2009-04-16T14:57:13.227 回答
0

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

您的代码不应检查间隔是否相等,因为这可能会出错。使用 Int64 来存储刻度,将最后处理的刻度存储在一个变量中,如果这个变量大于新刻度值,那么你有一个换行并且需要增加你的总刻度基数(Int64) - 假设你每 12 天检查一次蜱虫的频率超过 1 次...... ;)

于 2009-04-16T15:01:36.757 回答
0

对于大多数用途,秒表是最合适的选择。Environment.TickCount 仅在以下情况下才有意义:

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

  2. 但你不需要测量比 1 毫秒更精确

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

通常情况并非如此,至少第 1 点无关紧要。秒表(通常测量精度至少高出约 1000 倍(微秒范围或更佳))通常是最合适的选择。

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

原因如下:在我的旧 i7 上循环调用 Stopwatch 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_

语录:

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

“QPC 在多处理器系统、多核系统和超线程系统上是否可靠工作?是的

如何确定和验证 QPC 在我的机器上工作?您无需执行此类检查。[...]“

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

于 2017-05-03T18:16:51.677 回答
0

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

Abs() 不能解决问题。如果 Interval < 12.43 天,则代码将正确计时,而 StartTime 较小​​。但有时,每当 StartTime > (24.86 days - Interval) 时,测试将比应有的更早通过,即 (TickCount And Int32.MaxValue) 翻转为 0 的那一刻。此外,如果 Interval > 12.43 天 AND (TickCount并且 Int32.MaxValue) 接近 12.43 天,测试永远不会通过。

Environment.TickCount And Int32.MaxValue没有必要,也没有帮助。它生成一个正符号整数 32,表示以毫秒为单位的时间,每 24.86 天环绕到 0。替代方案更难理解,但它们可以让您毫无困难地进行长达 49.71 天的时间间隔。原始Environment.TickCount数据在 24.86 天后和每 49.71 天后环绕到 Int32.MinValue。强制转换unchecked((Uint32)Environment.TickCount)生成一个 [正] 无符号整数 32,表示以毫秒为单位的时间,每 49.71 天回绕到 0。(因为Environment.TickCount调用了 Windows 函数 GetTickCount(),它返回一个 DWORD (UInt32),并将其未经检查地强制转换为 Int32。)

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

您可以将经过时间计算为带符号的 int32(-24.86 到 +24.86 天),如果您比较独立时间或可能混合未来和过去,则更有用;或 unsigned int32(0 到 +49.71 天)。您可以将 T0 捕获为有符号 int32 或无符号 int32;它对结果没有影响。如果 T0 或经过的时间未签名,则只需要 [unchecked] 强制转换;带有签名的 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 >= TimeLimit 时至少测试一次。(如果您不确定测试的频率是否足够高,您可以在 Elapsed 上添加一个备份测试。如果 Elapsed 减少,它已经结束,所以时间必须到了。)

=====

对于超过 49.71 天的时间间隔,您可以计算 uiElapsed 回绕的次数,也可以计算Environment.TickCount回绕的次数。您可以将计数器组合成一个 64 位值,进行模拟GetTickCount64()(仅在 Windows Vista 和更新版本中可用)。64 位值具有全范围(2.92 亿年)和全分辨率(1 毫秒)。或者,您可以制作一个 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给出自启动以来的时间,以毫秒为单位,分辨率为 10-16 毫秒。

未经检查的Int32差异给出了时间差,-24.81 到 +24.81 天,带有环绕。未经检查的 32 位整数减法溢出并回绕到正确的值。的标志Environment.TickCount从不重要。示例,由一个环绕:iBase = Environment.TickCountgets Int32.MaxValue; 一个滴答声后,Environment.TickCount换行到Int32.MinValue; 并unchecked(Environment.TickCount - iBase)产生 1。

未经检查的UInt32差异给出了时间差,0 到 +49.71 天,带有环绕。(经过的时间永远不会是负数,因此这是更好的选择。)无符号 32 位整数的未经检查的减法溢出并回绕到正确的值。示例,由一个环绕:iBase = Environment.TickCountgets UInt32.MaxValue; 一个滴答声后,Environment.TickCount换行到UInt32.MinValue; 并unchecked(Environment.TickCount - iBase)产生 1。

于 2016-10-24T04:16:07.213 回答