fix: Fixes critical bug in TickCount calculation. (#2151)
### Summary
On some operating systems (like hosted Linux VMs), the `TimeStamp.GetTimeStamp()` CPU tick count will wrap around. This is generally not an issue, except for legacy reasons the TickCount is returned in milliseconds instead of ticks. This means when the values wrap around, they are already divided by the CPU Frequency (usually 1million) and then converted to milliseconds. That means the delta between the tick count before and after wrapping is off by a magnitude of (Frequency / 1000).
Example:
TimeStamp A = 9223372036654775807
Some time has passed:
TimeStamp B = -9223372036654775809
The raw delta is 400_000_000 (400ms) when you do `unchecked(A - B)`.
If we do the calculation AFTER converting it to milliseconds, then:
TickCount A = 9223372036654
TickCount B = -9223372036654
The raw delta is -18446744073308 instead of 400_000_000.
To fix this the calculation was changed so `long` -> `ulong`, then divided, then converted back to `long`, effectively bypassing wrap-around issue.
The new TickCount values in our example become:
TickCount A = 9223372037054
TickCount B = 9223372036654
The delta is 400 (in milliseconds). 🎉
This commit is contained in:
parent
0a4579f298
commit
fdf8c5cf23
1 changed files with 13 additions and 1 deletions
|
|
@ -346,8 +346,20 @@ public static class Core
|
|||
}
|
||||
}
|
||||
|
||||
private static readonly bool UseFastTimestampMath = Stopwatch.Frequency % 1000 == 0;
|
||||
private static readonly ulong FrequencyInMilliseconds = (ulong)Stopwatch.Frequency / 1000;
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static long GetTimestamp() => 1000L * Stopwatch.GetTimestamp() / Stopwatch.Frequency;
|
||||
public static long GetTimestamp()
|
||||
{
|
||||
if (UseFastTimestampMath)
|
||||
{
|
||||
return (long)((ulong)Stopwatch.GetTimestamp() / FrequencyInMilliseconds);
|
||||
}
|
||||
|
||||
// Fast calculation will be lossy, fallback to slower but accurate calculation
|
||||
return (long)((UInt128)Stopwatch.GetTimestamp() * 1000 / (ulong)Stopwatch.Frequency);
|
||||
}
|
||||
|
||||
public static void Setup(Assembly applicationAssembly, Process process)
|
||||
{
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue