代码之家  ›  专栏  ›  技术社区  ›  Nikhil

Windows上的微秒分辨率时间戳

  •  16
  • Nikhil  · 技术社区  · 15 年前

    如何在Windows上获取微秒分辨率时间戳?

    我是为了比 QueryPerformanceCounter QueryPerformanceFrequency (这些只会给您自引导以来经过的时间,如果在不同的线程上调用它们,则不一定准确——也就是说, 查询性能计数器 可能会在不同的CPU上返回不同的结果。还有一些处理器可以通过调整频率来节电,这显然并不总是反映在它们的 查询性能效率 结果)

    Implement a Continuously Updating, High-Resolution Time Provider for Windows 但它似乎并不牢固。 When microseconds matter 看起来不错,但不能再下载了。

    另一个资源是 Obtaining Accurate Timestamps under Windows XP 但是它需要很多步骤,运行一个助手程序加上一些初始化的东西,我不确定它是否在多个CPU上工作。

    我还看了维基百科的文章 Time Stamp Counter 这很有趣,但没那么有用。

    如果答案只是用BSD或Linux来做,那就简单多了,没关系,但是我想确认一下,并得到一些解释,为什么在Windows中这样难,在Linux和 BSD . 这是同样好的硬件…

    8 回复  |  直到 10 年前
        1
  •  19
  •   Arno    10 年前

    我相信这仍然有用: System Internals: Guidelines For Providing Multimedia Timer Support .

    它很好地解释了可用的各种计时器及其局限性。也许你的大敌不是解决问题,而是潜伏期。

    QueryPerformanceCounter 不会总是以CPU速度运行。事实上,它可能试图避免 RDTSC ,特别是在多处理器(/multi-core)系统上:它将使用 HPET 在Windows Vista及更高版本上(如果可用)或 ACPI/PM timer . 在我的系统(Windows 7 x64,双核AMD)上,计时器以14.31818 MHz的频率运行。

    The same is true for earlier systems:

    默认情况下,Windows Server 2003 Service Pack 2(SP2)对所有多处理器APIC或ACPI HALS使用PM计时器,除非检查进程确定BIOS是否支持APIC或ACPI HALS失败。“

    问题是,当检查失败时。这只意味着您的计算机/BIOS在某种程度上被破坏。然后,您可以修复BIOS(推荐),或者至少切换到使用 ACPI timer (/usepmtimer) 暂时。

    从C-不需要 P/Invoke -检查是否支持高分辨率计时器 Stopwatch.IsHighResolution 然后偷看 Stopwatch.Frequency . 它将在内部进行必要的QueryPerformanceCounter调用。

    另外,考虑一下,如果计时器坏了,整个系统将陷入混乱,总体上,行为异常,报告负的经过时间,减速等等——而不仅仅是您的应用程序。

    这意味着您实际上可以依赖于QueryPerformanceCounter。

    …与大众的看法相反, QueryPerformanceFrequency() "cannot change while the system is running" .

    编辑:作为文档 QueryPerformanceCounter() 声明,“不管调用哪个处理器”,实际上,只有在APIC/ACPI检测失败并且系统使用 TSC . 这是一个不应该发生的度假胜地。如果发生在较旧的系统上,则可能有来自制造商的BIOS更新/驱动程序修复。如果没有,则 /usepmtimer 启动开关还在。如果这也失败了,因为除了Pentium TSC之外,系统没有合适的计时器,您实际上可能会考虑处理线程关联性——即使如此,页面“社区内容”区域中其他人提供的示例也是误导性的,因为在每次启动/停止调用时设置线程关联性会产生不可忽略的开销。-这会带来相当大的延迟,并且可能会首先降低使用高分辨率计时器的好处。

    Game Timing and Multicore Processors 是关于如何正确使用它们的建议。请考虑现在是 几年前,当时很少有系统完全兼容/支持ACPI——这就是为什么在抨击它的同时,本文详细介绍了TSC以及如何通过保持仿射线程来克服它的局限性。

    我相信现在要找到一台支持零ACPI,没有可用PM计时器的普通PC是一件相当困难的工作。最常见的情况可能是BIOS设置,当ACPI支持设置不正确时(有时不幸的是,工厂默认设置)。

    Anecdotes tell 八年前,这种情况在罕见的情况下是不同的。(有趣的是,开发人员围绕设计“缺点”和抨击芯片设计人员而工作。公平地说,这可能是相同的方式,反之亦然。-)

        2
  •  11
  •   Peter Mortensen icecrime    12 年前

    queryperformancecounter/queryperformancefrequence,处理器速度分辨率

    只要小心多线程。处理器上的每个核心都可以有自己的计数器。

    更多信息在 Obtaining Accurate Timestamps under Windows XP .

    如果最终不得不采用这种方法:

    当我试图手动将数据写入一个串行端口(用于红外发射器)时,我发现将进程和线程优先级设置为最大(实时)大大提高了它的可靠性(如无错误),如果我还记得的话,这是一个必须有40 kHz左右分辨率的东西,因此它应该保持足够精确的毫秒。决心。

        3
  •  6
  •   Peter Mortensen icecrime    12 年前
    1. Windows不是 real-time OS .

    2. 多任务操作系统上的进程需要将其时间让给另一个线程/进程。这会给定时带来一些开销。

    3. 每个函数调用都有开销,因此在返回请求时会有一点延迟。

    4. 此外,调用系统调用将需要您的进程从用户空间模式切换到内核空间模式,这有相对较高的延迟。您可以通过在内核模式下运行整个进程(如设备驱动程序代码)来克服这个问题。

    5. 有些奥斯,像 Linux BSD ,是更好的,但他们仍然不能保持精确的时间分辨率到亚微秒(例如, nanosleep() 在Linux上大约是1毫秒,不少于1毫秒),除非您将内核修补到一些特定的调度程序,这样可以给您的应用程序带来好处。

    因此,我认为,最好调整应用程序以遵循这些问题,例如经常重新调整计时例程,这就是链接提供的。 AFAIK ,Windows的最高计时器分辨率仍然是GetPerformanceCounter/Frequency(),而不管其准确性如何。通过在一个单独的线程中运行计时器池例程,并将该线程关联设置为一个核心处理器,并将线程优先级设置为可以获得的最高值,可以获得更好的准确性。

        4
  •  6
  •   Peter Mortensen icecrime    12 年前

    QueryPerformanceCounter是对此的正确解决方案。与你和一些回答你的人写的相反,这个调用给出了正确的答案,即使是在多处理器系统中(除非所讨论的系统被破坏),它甚至可以处理不断变化的CPU频率。在大多数现代系统中,它源自 RDTSC 但要为您处理所有这些多CPU和频率变化的细节。(但比RDTSC慢得多)。

    QueryPerformanceCounter

    在多处理器计算机上,调用哪个处理器并不重要。但是,由于基本输入/输出系统(BIOS)或硬件抽象层(HAL)中的错误,您可以在不同的处理器上获得不同的结果。

        5
  •  5
  •   Peter Mortensen icecrime    12 年前

    我认为您不会找到比QueryPerformanceCounter更好的解决方案。标准技术是设置代码来捕获和丢弃线程切换可能导致的向后时间跳跃和大量异常值。 CPUs . 如果你测量的是很小的间隔(如果不是,那么你就不需要这样的精度),那么这在任何情况下都不常见。只是让它成为一个可容忍的错误,而不是一个关键的错误。

    在极少数情况下,您绝对需要确保它不会发生,然后通过设置处理器关联掩码锁定线程是唯一的选择。

        6
  •  3
  •   Chris Kline    10 年前

    到目前为止,答案中有很多好的信息。

    如果您要寻找的是一种简单的方法,可以从1970年1月1日开始,在Windows XP或更高版本上以毫秒或更高分辨率运行,那么在 the CurrentTime.cpp of Apple's OSS release of JavaScriptCore for MacOS 10.7.5 (我似乎在他们的10.8+版本中找不到它)。我指的代码在 CurrentTime() 功能。

    它使用标准的使用技术 QueryPerformanceCounter() 以高于毫秒的分辨率计算经过的时间差,然后定期将其与系统时钟同步,以计算时间戳并计算时钟漂移。为了获得更高的分辨率时间戳,需要运行Windows XP或更高版本,以便调用 QueryPeformanceFrequency() 一定会成功。

    它不能解释上下文切换会使事情稍微偏离(例如 "Implement a Continuously Updating, High-Resolution Time Provider for Windows" "The Windows Timestamp Project" 但是它会不断地重新同步。我不会用它来发射火箭,但是在大约50行代码的情况下,它实现起来很简单,而且对于许多用途来说已经足够好了。

    此外,如果您知道保证您运行的是Windows 8/Windows Server 2012,则只需使用 GetSystemTimePreciseAsFileTime() ,因为它以尽可能高的精度(1微秒或更好)返回系统日期和时间。

        7
  •  1
  •   Peter Mortensen icecrime    12 年前

    我用过 the DateTimePrecise class The Code Project .

    唯一的问题是,如果我不至少每10秒调用一次,它会产生疯狂的结果——我认为内部存在某种整数溢出——所以我有一个计时器可以执行 DateTimePrecise.Now 每隔几秒钟。

    你也应该跑 NTP 在机器上,如果你想让时间精确的话。

    祝你好运。。。

        8
  •  1
  •   Peter Mortensen icecrime    12 年前

    我发现使用 PerformanceCounter 一起 PerformanceCounterFrequency ,因为给定的 性能计数器频率 偏离实际频率。

    它偏离一个偏移,它也显示热漂移。较新的硬件似乎有较少的漂移,但漂移和偏移是相当可观的。由于1 ppm为1 _祄祄/s,几ppm的漂移已经将微秒精度破坏到很大程度!因此,在使用时强烈建议仔细进行硬件特定校准。 性能计数器 具有 性能计数器频率 . 这也可能是在不频繁调用某些函数时观察到“疯狂结果”的原因。

    我对这件事做了一些更详细的调查。描述可在 Microsecond Resolution Time Services for Windows .