C# 秒表和DateTime.UtcNow产生出乎意料的大时间变化
我们有记录各种昂贵操作的性能信息的应用程序日志。我们在日志记录中同时使用了C# 秒表和DateTime.UtcNow产生出乎意料的大时间变化,c#,datetime,timing,stopwatch,C#,Datetime,Timing,Stopwatch,我们有记录各种昂贵操作的性能信息的应用程序日志。我们在日志记录中同时使用了秒表和日期时间.UtcNow,我们发现,即使给定日期时间,这些值的差异也远远超过预期值。UtcNow的~20ms精度。我的问题是什么会导致这种情况?它能被修复吗? 记录的资料如下: 开始时间(DateTime.UtcNow) 持续时间(TimeSpan.FromSeconds((after-before)/(double)Stopwatch.Frequency),其中after和before是操作开始和结束时的Stopw
秒表
和日期时间.UtcNow
,我们发现,即使给定日期时间,这些值的差异也远远超过预期值。UtcNow的~20ms精度。我的问题是什么会导致这种情况?它能被修复吗?
记录的资料如下:
- 开始时间(
)DateTime.UtcNow
- 持续时间(
,其中TimeSpan.FromSeconds((after-before)/(double)Stopwatch.Frequency)
和after
是操作开始和结束时的before
值Stopwatch.GetTimestamp()
- EndTime(
)DateTime.UtcNow
- 约2%的条目关闭时间超过20毫秒
- 其中,它们的平均关断时间为100毫秒(最大关断时间为1.4秒)
- 其中,在某些情况下,秒表计算的结束时间大于基于日期时间的结束时间,但在大多数情况下,秒表时间较小
- 运行在Windows Server 2012 R2之上的Windows Server 2012 R2 Hyper-V虚拟机
- 32GB ram用于虚拟机,8个虚拟CPU
秒表使用引擎盖下的查询性能计数器(QPC),而日期时间。UtcNow
使用计算机的实时时钟(RTC)
- QPC来自您的CPU的时间戳计数器(TSC),非常精确,但与UTC相关的准确性没有依据
- RTC位于计算机主板上,通常由廉价的晶体振荡器构成。它是精确的,但不精确
要测量经过的时间,您需要精度,因此应该使用秒表
要获取一天中的当前时间,您需要准确度,因此应该使用DateTime.UtcNow
这是一本很棒的书,里面有很多支持性的细节
您测量到的变化是由于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;
}
}
// 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();
然而,缺点是您假设在初始化类时RTC是完全同步的。实际上,您对此没有保证
如果你想要某种程度的保证,你可以考虑自己做NTP调用。我已经实现了一个完全正确的类。它实现了<代码> IcLoo.接口。它定期调用NTP服务器,并使用<代码>秒表< /C>跟踪调用之间的时间。你会这样使用:
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;
}
}
// 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函数的实现。通常,除非您正在做实时的事情,如图形或音频,否则这是一种过度的操作。下面是Eric Lippert关于为什么秒表优于调用DateTime的部分内容。现在,我们可以用来测量程序中经过了多少时间。@Chase我可以问你测量的操作长度吗?秒?分钟?小时?看一下,utcnow的分辨率大约为10毫秒,因此可以解释您观察到的一些差异。这些差异基于长时间运行的实际系统测量值?偏差是否与操作长度相关?一种理论可能是系统时钟正在自动调整,这会影响DateTime.UtcNow
,但不会影响秒表
measurements。虚拟机通常与主机同步的频率相当高。我可以相信,这些虚拟机会关闭几毫秒,但我看到会关闭数百毫秒。您所描述的内容是否真的会导致如此大的差异?当然,很可能是这样,甚至更大。这一切都取决于主板上RTC晶体的质量。有些问题是:dware比其他的要好。在我的机器上,我测量了10到60毫秒的修正,但是没有理由它们不能在其他硬件上更大。另外,你说你在虚拟机上运行这个,这完全可能会影响结果。我不完全确定RTC虚拟化的机制,但我非常确定主机OS必须将RTC值传递给客户,并且可能存在延迟。您可以考虑直接在服务器硬件上运行相同的测试,并查看它是否有区别。