Windows 10下的多核处理器上的QueryPerformanceCounter行为不正常

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

从Windows 10开始(目前为止只在Intel i7处理器上testing过),我们观察到QueryPerformanceCounter返回值的不规则行为。 有时候,这个调用返回的值会跳到更远的地方,然后回到原来的值。 感觉好像线程已经从一个核心移动到另一个,并在一段时间内返回一个不同的计数器值(没有证据,只是一个直觉)。

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

一个“简单”的解决方法是使用BCDEdit启用UsePlatformClock启动选项(这使得所有的行为都不会有问题)。

我知道可能出众的GetSystemTimePreciseAsFileTime但由于我们仍然支持7,所以这不是一个完全的select,除非我们为不同的操作系统编写完全不同的代码,我们真的不想这样做。

在Windows 10下是否已经观察/解释过这种行为?

我需要更多关于你的代码的知识,但是让我从MSDN中突出一些东西:

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

特别是这个:

通过使用Windows API SetThreadAffinityMask将该单线程保留在单个处理器上…虽然QueryPerformanceCounter和QueryPerformanceFrequency通常针对多个处理器进行调整,但BIOS或驱动程序中的错误可能导致这些例程在线程从一个处理器移动时返回不同的值到另一个。 所以,最好把线程放在一个处理器上。

你的情况可能会利用这些错误之一 。 简而言之:

  • 你应该总是从一个线程查询时间戳(设置相同的CPU亲和力,以确保它不会改变),并从任何其他线程读取该值(只是一个互锁的读取,不需要花哨的同步)。
  • 钳制计算的三角洲(至少可以肯定它不是负面的)…

笔记:

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

随着多核/超线程CPU,多CPU系统和休眠操作系统的出现,不能依赖TSC来提供准确的结果 – 除非要非常小心地纠正可能的缺陷:刻度速率和是否所有内核(处理器)在其计时寄存器中具有相同的值。 单个主板上的多个CPU的时间戳记计数器不会被同步。 因此, 程序只能通过限制自己运行在一个特定的CPU上才能获得可靠的结果

那么,即使在理论上QPC是单调的,那么你必须总是从同一个线程调用它来确保这一点。

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

…软件可能难以做到这一点,而不是确保所有逻辑处理器在给定的时间点具有与TSC相同的值…


编辑 :如果你的应用程序是多线程的,你不能(或者你不想)设置CPU的亲和力(尤其是如果你需要精确的时间戳成本,线程之间有不同步的值),那么你可以使用GetSystemTimePreciseAsFileTime()在Win8(或更高版本)上运行,并回timeGetTime() Win7的timeGetTime() (使用timeBeginPeriod(1)将粒度设置为1毫秒并假设1毫秒的分辨率就足够了)。 一个非常有趣的阅读: Windows时间戳项目 。

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