如何在Windows上获得微秒分辨率时间戳?
我比较喜欢QueryPerformanceCounter
和QueryPerformanceFrequency
(这些只能给你一个自引导以来的时间,如果它们在不同的线程上调用,它们不一定准确 – 也就是说, QueryPerformanceCounter
可能在不同的CPU上返回不同的结果。处理器调整其节电频率,这显然不总是反映在他们的QueryPerformanceFrequency
结果)。
在Windows中实现了一个持续更新,高分辨率的时间提供程序 ,但似乎并不稳固。 微秒的问题看起来不错,但不能下载。
另一个资源是在Windows XP下获取准确的时间戳 ,但它需要一些步骤,运行一个帮助程序,还有一些初始化的东西,我不知道它是否可以在多个CPU上工作。
我也看了维基百科的文章时间戳计数器 ,这很有趣,但没有那么有用。
如果答案只是用BSD或者Linux来做的话,那么这很容易,而且很好,但是我想确认一下,并且得到一些解释,为什么在Windows和Linux和BSD中这么容易。 这是相同的硬件…
我相信这仍然是有用的: 系统内部:提供多媒体计时器支持指导 。
它很好地解释了各种可用的定时器和它们的限制。 这可能是你的敌人不是解决方案,而是延迟。
QueryPerformanceCounter不会总是以CPU速度运行。 实际上,它可能会尝试避免RDTSC ,特别是在多处理器(/多核)系统上:它将使用Windows Vista上的HPET ,如果可用,或使用ACPI / PM定时器 ,则使用HPET 。 在我的系统(Windows 7 x64,AMD双核)上,定时器运行在14.31818 MHz。
早期的系统也是如此:
默认情况下,Windows server 2003 Service Pack 2(SP2)对所有多处理器APIC或ACPI HAL使用PM定时器,除非检查过程确定BIOS是否支持APIC或ACPI HAL失败。
问题是,当检查失败时。 这只是意味着您的计算机/ BIOS在某种程度上被破坏了。 然后,您可以修复您的BIOS(推荐),或者至少切换到使用ACPI定时器(/ usepmtimer) 。
从C#很容易 – 没有P / Invoke – 用Stopwatch.IsHighResolution
检查高分辨率定时器支持,然后查看Stopwatch.Frequency
。 它将在内部进行必要的QueryPerformanceCounter调用。
另外要考虑的是,如果定时器被破坏,整个系统将会破坏,总的来说,表现会奇怪,报告负面消逝时间,放慢速度等 – 不仅仅是您的应用程序。
这意味着您实际上可以依靠QueryPerformanceCounter。
…与流行的观点相反, QueryPerformanceFrequency()
在系统运行时不能改变“ 。
编辑:正如QueryPerformanceCounter()
的文档所述,“哪个处理器被调用并不重要” – 事实上,只有在APIC / ACPI检测失败且系统采用TSC 。 这是一个不应该发生的度假胜地。 如果它发生在较旧的系统上,则可能是制造商提供的BIOS更新/驱动程序修复程序。 如果没有, /usepmtimer
启动开关仍然在那里。 如果这也失败了,因为除了Pentium TSC之外,系统还没有一个合适的定时器,实际上你可能会考虑搞乱线程关联 – 即使这样,其他人在页面的“社区内容”区域提供的示例也是误导,因为它有一个不可忽视的开销,因为在每个开始/停止调用设置线程亲和力 – 这会引入相当长的延迟,并可能减少首先使用高分辨率计时器的好处。
游戏时间和多核处理器是如何正确使用它们的建议。 请考虑它现在已经五年了,当时只有更少的系统完全符合ACPI标准/支持 – 这就是为什么要抨击它的原因,文章详细介绍了TSC以及如何通过保留仿射来解决其局限性线。
我认为现在找到一个支持零ACPI并且没有可用PM计时器的通用PC是相当困难的任务。 最常见的情况可能是BIOS设置,当ACPI支持不正确设置(有时可悲的出厂默认值)。
轶事告诉八年前,情况在罕见的情况下是不同的。 (让读者开心一点,开发人员围绕着设计“缺点”,抨击芯片设计师,公平地说,反过来也可能是相同的。:-)
QueryPerformanceCounter / QueryPerformanceFrequency,处理器速度解析
只要注意多线程。 处理器上的每个内核都可以有自己的计数器。
更多信息在Windows XP下获取准确时间戳 。
如果你最终不得不采取这种方法:
当我试图手动将数据写入串口(对于红外线发送器)时,我发现将进程和线程优先级设置为最大值(实时)会大大提高其可靠性(因为没有错误),这是必须的如果我还记得,它的分辨率大约为40 kHz,所以它应该保持足够精确的毫秒分辨率。
Windows不是一个实时的操作系统 。
多任务操作系统上的进程将需要花费时间到另一个线程/进程。 这给了一些时间的开销。
每个函数调用都会有开销,因此在返回请求时会稍微延迟一点。
此外,调用系统调用将需要您的进程从用户空间模式切换到具有相对较高延迟的内核空间模式。 您可以通过在内核模式下运行整个进程(如设备驱动程序代码)来克服这一点。
一些操作系统,比如Linux或者BSD ,性能更好,但是它们仍然无法将精确的时间分辨率保持在亚微秒级(例如,Linux上nanosleep()的准确度大约为1ms,不少于1ms)将内核补丁到一些特定的调度程序,为您的应用程序带来好处。
所以我认为,最好是让您的应用程序适应这些问题,比如经常重新调整计时例程,这就是您的链接所提供的。 AFAIK ,Windows的最高计时器分辨率仍然是GetPerformanceCounter / Frequency(),无论其精度如何。 通过在单独的线程中运行定时器池化例程,并将该线程亲和性设置为一个核心处理器,并将线程优先级设置为可获得的最高值,可以获得更高的精度。
QueryPerformanceCounter是正确的解决方案。 与你和你的一些人所写的相反,这个调用即使在多处理器系统下也能给出正确的答案(除非有问题的系统被破坏),甚至可以处理CPU频率的变化。 在大多数现代系统中,它来源于RDTSC ,但为您处理所有这些多CPU和频率变化的细节。 (虽然它比RDTSC慢得多)。
请参阅QueryPerformanceCounter
在多处理器计算机上,调用哪个处理器并不重要。 但是,由于基本输入/输出系统(BIOS)或硬件抽象层(HAL)中的错误,您可以在不同的处理器上获得不同的结果。
我不认为你会找到比QueryPerformanceCounter更好的解决方案。 标准的技术是设置你的代码来捕捉和放弃反向时间跳转和可能由线程切换CPU产生的大量异常值。 如果你正在测量非常小的时间间隔(如果不是这样,那么你就不需要那么精确),那么这种情况就不是那么普遍。 只是让它是一个容忍的错误,而不是一个严重的错误。
在极少数情况下,您绝对需要确保它永不发生,然后通过设置处理器关联掩码来锁定线程是唯一的选择。
到目前为止,答案中有很多很好的信息。
如果你正在寻找的是从1970年1月1日起以毫秒级或更高分辨率在Windows XP或更高版本上获得经过时间的简单方法,那么在苹果的OSS版本的CurrentTime.cpp中有一个非常简单的跨平台示例JavaScriptCore为MacOS 10.7.5 (我似乎无法找到它在他们的10.8 +版本)。 我所指的代码是在CurrentTime()
函数中。
它使用使用QueryPerformanceCounter()
的标准技术来计算高于毫秒分辨率的已用时间差异,然后将其周期性地同步到系统时钟以计算时间戳并计算时钟漂移。 为了获得更高分辨率的时间戳,它需要您运行Windows XP或更高版本,以便对QueryPeformanceFrequency()
调用保证成功。
它没有考虑到上下文切换的细节(例如“为Windows实现持续更新,高分辨率时间提供程序”和“Windows时间戳项目” ),但它不断地重新同步。 我不会推出一个火箭,但是在大约50行的代码中,它很容易实现,而且足够用于很多目的。
此外,如果您知道自己保证正在运行Windows 8 / Windows server 2012,则应该使用GetSystemTimePreciseAsFileTime()
,因为它以最高可能的精度(1微秒或更好)返回系统日期和时间。
我使用了 代码项目中 的DateTimePrecise类 。
我遇到的唯一问题是,如果我至少每10秒钟就调用一次,会产生疯狂的结果 – 我认为内部有一些整型溢出 – 所以我有一个执行DateTimePrecise.Now
每隔几秒钟
如果你想让时间准确无误,你还应该在机器上运行NTP 。
祝你好运…
PerformanceCounter
与PerformanceCounter
一起使用时发现困难,因为给定的PerformanceCounterFrequency
偏离实际频率。
它偏移一个偏移量,它也显示热漂移。 较新的硬件似乎有较少的漂移,但漂移和偏移是相当可观的。 由于1 ppm为1μs/ s,所以几ppm的漂移已经很大程度上损害了微秒精度! 因此,将PerformanceCounter
与PerformanceCounterFrequency
一起使用时,强烈建议谨慎进行特定于硬件的校准。 这也可能是不经常调用某些函数时出现“疯狂结果”的原因。
我在这个问题上做了一些更详细的调查。 可以在Windows的Microsecond Resolution Time Services中找到说明。