diff --git a/rd-net/Lifetimes/Util/LocalStopwatch.cs b/rd-net/Lifetimes/Util/LocalStopwatch.cs
index ee3ea015a..6599ccb8d 100644
--- a/rd-net/Lifetimes/Util/LocalStopwatch.cs
+++ b/rd-net/Lifetimes/Util/LocalStopwatch.cs
@@ -56,4 +56,55 @@ private void AssertTimeStamp()
if (Mode.IsAssertion) Assertion.Assert(myStartTimeStamp != 0, $"{nameof(LocalStopwatch)} must be created using `{nameof(StartNew)}` method");
}
}
-}
\ No newline at end of file
+
+ ///
+ /// Non-allocating, low-resolution stopwatch backed by on
+ /// .NET 5+ and by on older targets. Resolution is whatever
+ /// the OS tick granularity is (typically ~10–16 ms on Windows), which makes this much cheaper
+ /// than when sub-millisecond accuracy is not needed.
+ ///
+ ///
+ ///
+ /// Always create instances via ; a default-constructed value will trip an
+ /// assertion in .
+ ///
+ ///
+ /// Warning — wrap-around on pre-.NET 5 targets (net472 / netstandard2.0):
+ /// the underlying is a 32-bit counter. The unsigned
+ /// subtraction used here recovers the correct elapsed value across one wrap, giving an
+ /// unambiguous range of ~49.71 days (2^32 ms). If the stopwatch is read after a longer
+ /// interval the result silently aliases (true elapsed modulo ~49.71 days) — do not rely on
+ /// this type for measurements that may exceed that bound on pre-net5 targets. The .NET 5+
+ /// branch uses a 64-bit counter and is not subject to this limit in practice.
+ ///
+ ///
+ public readonly struct FastStopwatch
+ {
+#if NET5_0_OR_GREATER
+ private readonly long myStartTimeStamp;
+ private FastStopwatch(long startTimestamp) => myStartTimeStamp = startTimestamp;
+ private static long GetTicks() => Environment.TickCount64;
+#else
+ private readonly uint myStartTimeStamp;
+ private FastStopwatch(uint startTimestamp) => myStartTimeStamp = startTimestamp;
+ private static uint GetTicks() => (uint)Environment.TickCount;
+#endif
+
+ public static FastStopwatch StartNew() => new(GetTicks());
+ public long ElapsedMilliseconds
+ {
+ get
+ {
+ AssertTimeStamp();
+ return unchecked(GetTicks() - myStartTimeStamp);
+ }
+ }
+
+ public TimeSpan Elapsed => TimeSpan.FromMilliseconds(ElapsedMilliseconds);
+
+ private void AssertTimeStamp()
+ {
+ if (Mode.IsAssertion) Assertion.Assert(myStartTimeStamp != 0, $"{nameof(FastStopwatch)} must be created using `{nameof(StartNew)}` method");
+ }
+ }
+}