Windows 10下多核处理器上的QueryPerformanceCounter行为不稳定

Windows 10下多核处理器上的QueryPerformanceCounter行为不稳定,windows,performance,timer,intel,multicore,Windows,Performance,Timer,Intel,Multicore,在Windows下,我的应用程序使用QueryPerformanceCounter(和QueryPerformanceFrequency)执行“高分辨率”时间戳 由于Windows 10(目前仅在英特尔i7处理器上测试),我们观察到QueryPerformanceCounter返回的值存在不稳定的行为。 有时,调用返回的值会向前跳转很远,然后返回到其以前的值。 这感觉就像线程从一个核心移动到另一个核心,并在一段时间内返回不同的计数器值(没有证据,只是直觉) 在XP或7下从未观察到过这种情况(没有

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

由于Windows 10(目前仅在英特尔i7处理器上测试),我们观察到
QueryPerformanceCounter
返回的值存在不稳定的行为。 有时,调用返回的值会向前跳转很远,然后返回到其以前的值。 这感觉就像线程从一个核心移动到另一个核心,并在一段时间内返回不同的计数器值(没有证据,只是直觉)

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

一个“简单”的解决方法是使用BCDEdit启用UsePlatformClock引导选项(这使得一切都正常运行)

我知道GetSystemTimePrecisesFileTime可能具有更高的性能,但由于我们仍然支持7,除非我们为不同的操作系统编写完全不同的代码,否则这并不完全是一个选项,我们真的不想这样做


在Windows 10下是否观察到/解释过此类行为

我需要更多关于您的代码的知识,但让我强调一下MSDN中的几点:

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

尤其是:

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

您的案例可能利用了其中一个bug。简言之:

  • 您应该始终从一个线程查询时间戳(设置相同的CPU亲缘关系以确保它不会更改),并从任何其他线程读取该值(只是一个互锁读取,不需要奇怪的同步)
  • 钳制计算出的增量(至少要确保它不是负值)
注:

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

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

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

另请注意:如果通过软件进行同步,您可以从英特尔文档中阅读:

…对于软件来说,以确保所有逻辑处理器在给定时间点对TSC具有相同的值的方式来实现这一点可能很困难


编辑:如果您的应用程序是多线程的,并且您不能(或者您不想)设置CPU关联性(特别是如果您需要精确的时间戳,而代价是在线程之间取消同步值),那么在Win8(或更高版本)上运行时,您可以使用
GetSystemTimePreciseSFileTime()
并回退到Win7的
timeGetTime()
(使用
timeBeginPeriod(1)
将粒度设置为1毫秒,并假设1毫秒的分辨率就足够了)。一本非常有趣的书:


编辑2:由OP直接建议!如果适用(因为这是一个系统设置,而不是应用程序的本地设置),这可能是一个简单的解决方法。您可以使用bcdedit强制QPC使用HPET而不是TSC(请参阅)。延迟和分辨率应该更差,但从本质上讲,它不会受到上述问题的影响。

谢谢!基本上,我们没有进行大规模的重新思考,因为我们的代码是大规模多线程的,每个人都使用QPC来获得精确的时间戳(据我们所知,在W8之前的Windows上获得毫秒或亚毫秒时间戳的唯一方法…),好吧,作为肮脏的解决方法,你可能会成为你的一个(长寿命)负责调用QPC和(某种程度上)搜索并替换对QueryPerformanceCounter()的所有其他调用以简单读取该值的线程是的,但这违背了“精确”时间戳的目的,因为我们现在依赖另一个线程以“稍微‘低’的频率”提取值,人为地重新创建我们试图用DateTime来逃避的问题。现在,该问题的值以“高”间隔更新。True(即使准实时工作线程可能会缓解该问题)。如果适用,请注意
KeQueryPerformanceCounter()
具有更好的吞吐量(如果您选择自己的“时间戳服务”)。然而……既然你必须(至少)编写自己的函数来替换QPC,那么……为什么不为Win8+使用
GetSystemTimePreciseSFileTime()
,为Win7使用
timeGetTime()
(在调用
timeBeginPeriod(1)
)呢?这就是我要考虑的。但仍然不确定我设置UsePlatformClock的技巧是什么:)你有关于哪些i7处理器出现这种不良行为的信息,以及哪些windows 10版本的信息吗?i7处理器已经有近两年的历史了,所以我真的很好奇它是否是mode