我们有应用程序日志记录各种昂贵操作的性能信息。我们在日志记录中同时使用Stopwatch
和DateTime.UtcNow
,我们发现这些值可能比预期的要大很多,即使给定DateTime.UtcNow的精度为~20ms 。我的问题是什么可能导致这种情况并且可以修复?
记录的信息是:
DateTime.UtcNow
)TimeSpan.FromSeconds((after - before) / (double)Stopwatch.Frequency)
,其中after
和before
是操作开始和结束时Stopwatch.GetTimestamp()
的值DateTime.UtcNow
)你会期望EndTime接近StartTime + Duration,但在某些情况下它会偏离。我们对10000个这样的测量进行了采样,寻找EndTime和(StartTime + Duration)相差超过20ms的情况。我们发现了以下内容:
计算机信息
答案 0 :(得分:6)
这是可以预料的。 StopWatch
使用QueryPerformanceCounter
(QPC),DateTime.UtcNow
使用计算机的实时时钟(RTC)。
为了测量经过的时间,您需要精确度,因此应使用Stopwatch
。
要获取当前时间,您需要准确性,因此应使用DateTime.UtcNow
。
包含大量支持详情的精彩阅读can be found here on MSDN。
您测量的变化是由于RTC中的时钟漂移造成的。你的RTC(全部都是)会在实时提前或落后几毫秒。这很常见,并且通过NTP同步定期更正。对于微小的调整,操作系统将在几秒钟内以较小的间隔(几毫秒)分散校正。
就解决方法而言,你可以考虑这样一个类:
public static class PreciseClock
{
private static readonly DateTime StartTime = DateTime.UtcNow;
private static readonly Stopwatch Stopwatch = Stopwatch.StartNew();
public static DateTime GetCurrentUtcTime()
{
return StartTime + Stopwatch.Elapsed;
}
}
然而,缺点是你假设在初始化类时RTC完全同步。实际上,你无法保证这一点。
如果您想要一些级别的保证,您可以考虑自己进行NTP呼叫 。我已经在NodaTime.NetworkClock中实现了一个完全相同的类。它实现了Noda Time的IClock
接口。它会定期拨打NTP服务器,并使用Stopwatch
跟踪呼叫之间的时间。你可以这样使用它:
// grab the clock's singleton instance
var clock = NetworkClock.Instance;
// optionally set the ntp server during your app's startup
clock.NtpServer = "pool.ntp.org"; // or whatever server you want to sync with
// Get the current utc time whenever you like
DateTime utcNow = clock.Now.ToDateTimeUtc();
此外,还有大多数现代硬件都可以使用的“多媒体计时器”或“高精度事件计时器”(HPET)。它比QPC提供甚至更高的精度。但是,没有直接类在.NET Framework中公开它。如果你搜索,你会发现一些包含提供它的Win32函数的实现。通常情况下,除非您正在进行图形或音频之类的实时操作,否则这样做太过分了。