Windows 10下多核处理器上的QueryPerformanceCounter行为不规律

时间:2017-05-17 09:20:43

标签: windows performance timer intel multicore

在Windows下,我的应用程序使用QueryPerformanceCounter(和QueryPerformanceFrequency)来执行“高分辨率”时间戳。

自Windows 10(目前仅在Intel i7处理器上测试过)以来,我们观察到QueryPerformanceCounter返回的值中的不稳定行为。 有时,调用返回的值将远远超前,然后返回其先前的值。 感觉好像线程已经从一个核心移动到另一个核心并且在一段时间内返回了不同的计数器值(没有证据,只有直觉)。

这在XP或7下从未观察到过(没有关于Vista,8或8.1的数据)。

一个“简单”的解决方法是使用BCDEdit启用UsePlatformClock引导opiton(这使得一切都表现不顺利)。

我知道潜在的优势GetSystemTimePreciseAsFileTime,但是我们仍然支持7这不是一个选项,除非我们为不同的操作系统编写完全不同的代码,我们真的不想这样做。

在Windows 10下是否已观察到此行为?

1 个答案:

答案 0 :(得分:2)

我需要更多关于您的代码的知识,但让我重点介绍一些来自MSDN的内容:

  

计算增量时,[来自QueryPerformanceCounter]的值应钳制,以确保定时值中的任何错误不会导致崩溃或不稳定的时间相关计算。

尤其是:

  

通过使用Windows API SetThreadAffinityMask将单个线程设置为保留在单个处理器上...虽然QueryPerformanceCounter和QueryPerformanceFrequency通常针对多个处理器进行调整,但BIOS中的错误或驱动程序可能会导致这些例程返回不同的值< / strong>当线程从一个处理器移动到另一个处理器时。因此,最好将线程保留在单个处理器上。

您的案例可能利用其中一个错误。简而言之:

  • 您应该始终从一个线程查询时间戳(设置相同的CPU亲和力以确保它不会更改)并从任何其他线程读取该值(只是一个互锁的读取,不需要花哨的同步)。
  • 钳制计算的delta(至少确定它不是负数)......

注意:

如果可能,

QueryPerformanceCounter()使用TSC(请参阅MSDN)。同步TSC的算法(如果可用,在你的情况下它应该是)从Windows 7到Windows 8的变化很大但是请注意:

  

随着多核/超线程CPU,具有多个CPU和休眠操作系统的系统的出现,TSC无法提供准确的结果 - 除非非常谨慎地纠正可能的缺陷:勾选并且所有核心(处理器)在其计时寄存器中是否具有相同的值。没有承诺单个主板上多个CPU的时间戳计数器将同步。因此,程序只能通过将自身限制为在一个特定的CPU上运行来获得可靠的结果

然后,即使理论上QPC是单调的,你也必须始终从同一个线程中调用它来确保这一点。

另一个注意事项:如果通过软件进行同步,您可以从英特尔文档中读取:

  

......软件可能难以确保所有逻辑处理器在给定时间点对TSC具有相同的值...

编辑:如果你的应用程序是多线程的,你不能(或者你不想)设置CPU亲和力(特别是如果你需要精确的时间戳,需要花费时间)线程之间的同步值)然后您可以在Win8(或更高版本)上运行时使用GetSystemTimePreciseAsFileTime()并在Win7上回退到timeGetTime()(使用timeBeginPeriod(1)将粒度设置为1 ms并假设为1 ms分辨率就够了)。一个非常有趣的读物:The Windows Timestamp Project

编辑2 :OP直接建议!如果适用(因为它是系统设置,而不是您的应用程序本地设置),这可能是一个简单的解决方法。您可以使用bcdedit强制QPC使用HPET而不是TSC(请参阅MSDN)。延迟和解决方案应该更糟,但它本质上是安全来自上述问题。