【问题标题】:QueryPerformance counter in multicore systems with variable clock speeds具有可变时钟速度的多核系统中的 QueryPerformance 计数器
【发布时间】:2012-12-31 01:35:38
【问题描述】:

根据 MSDN 文章 Game Timing and Multicore Processors,QueryPerformanceFrequency() 和 QueryPerformanceCounter() 函数据说是最好的。但如果不支持它,我可以使用 timeGetTime() 或 GetTickCount()。

  1. QueryPerformanceFrequency() 是否与 CPU 时钟相同,还是使用自己的时钟或具有自己的频率且不会随时间变化的东西?
  2. 如果频率随时间随机变化会怎样(尤其是在笔记本电脑中)
  3. 如何使用 SetThreadAffinityMask 函数? (我看到的一些代码使用该函数将其更改为“1”,然后使用计数器并将掩码再次更改为旧值。这是为什么?是否正确?)
  4. 在案例/问题 1 中仅使用一次 QueryPerformanceFrequency() 函数并通过除以频率来计算增量时间值是否正确?还是用case 2解决了?

【问题讨论】:

  • 哪里不支持?
  • 请说明您正在开发的软件类型:例如桌面应用、3D 游戏……
  • @AndyT:以防万一:P。我有几个应用程序。我的另一个项目是儿童监视器的游戏和 Windows 服务。在儿童监控系统中,我正在计算时间,以防孩子可以更改系统时间:P.
  • QPF 无论如何都会提供一个常数。 但是: 它的来源是硬件,因此constant QPF 只是一个估计值。请参阅this SO 问题以进一步了解。
  • 有一个很好的答案,有人删除了吗???

标签: windows performance winapi timer multicore


【解决方案1】:
  1. QPC 底层实现差异很大。在某些情况下是这样,但通常不是。
  2. 这将影响 RDTSC,但不会影响 QPC。
  3. 即防止线程从一个 CPU 核心移动到另一个。它可能有助于避免报告负时间流逝的高分辨率计时方法(它发生了......)。不过一般不推荐。
  4. QPC 的频率是恒定的。至少在给定的系统上,至少在重新启动之前。

但你不一定会问正确的问题......

windows上常用的四种定时功能是: GetTickCount、timeGetTime、QueryPerformanceCounter (QPC) 和 RDTSC

我的建议:

游戏逻辑计时应使用 timeGetTime 完成。它简单、可靠,并且有足够的分辨率来达到这个目的。 (编辑:默认分辨率会有所不同 - 您可以调用 timeBeginPeriod 将其强制为 1 毫秒)

不应使用GetTickCount。它的分辨率对于游戏逻辑或性能监控来说都太差了(64 赫兹——一个令人讨厌的频率,因为它会产生具有典型显示器刷新率的节拍频率)。它是最快的计时函数调用 IIRC,但我找不到可以弥补其低分辨率的场景。 (编辑:有传言说 timeBeginPeriod 可以提高 GetTickCount 的分辨率 - 谣言是 FALSE)

RDTSC 和 QPC 对于简单的游戏逻辑时序来说都太不可靠/古怪,但更适合用于性能测量。如果您想要独立于 CPU 频率变化的单位,RDTSC 的问题会使其使用起来很痛苦,并且您通常需要 asm 才能使用它。 QPC 通常可以正常工作......但是当它出错时,它可能会出错,并且出错的方式多种多样(有时它真的很慢,有时它经常出现小的负增量,有时它不经常出现大的负增量(不是环绕),有时它只是完全精神病,等等)。 RDTSC 几乎总是更快,而且通常分辨率更好。总的来说,我更喜欢 RDTSC 用于内部使用,因为它速度更快,因此在测量时产生的失真更少。在客户机器上,这是一个更接近的电话 - 由于微软推动 QPC 更容易证明它的合理性,并且它更经常地工作而没有复杂性,但是它可以在客户机器上搞砸的各种各样的方式,你永远不会看到 -在我看来,房子是一个主要缺点。

【讨论】:

  • 恕我直言,您介意告诉我您的参考或经验吗?
  • 当然,主要来自编写使用这些功能的游戏和性能监控代码。诚然,我对模糊 QPC 错误的大部分知识来自与游戏开发者 (Shizmoo) 的交谈,他曾短暂尝试使用 QPC 来为游戏逻辑计时,并发现了为什么这是一个坏主意的艰难方式,大约在 2002 年 IIRC - 我只亲眼见过QPC 有三四种可能会失败,因为我还没有在那么多机器上测试过。
  • Oki,谢谢 :),顺便说一句,2002 年左右是什么? (是这个苹果 ipad,dvice.com/archives/2012/07/7_photos_of_an.php 吗?)你能指出这些错误/如何重现错误/发生什么信息吗?
  • “大约在 2002 年”,大约在 2002 年。如果您正在寻找游戏名称,我不知道,这可能是 ActiveX 的事情,因为我认为他们的大部分工作都是。
【解决方案2】:
如果您需要高精度计时器

QPF/QPC 是最好的(返回值以纳秒为单位,但这并不意味着精度为 1 纳秒)。否则,只需使用GetTickCount()(以毫秒为单位)。两个版本都应能正确处理可变 CPU 频率(例如,在带有省电选项的笔记本电脑上)。

我不知道关联掩码如何帮助检索系统时间。

获得高精度时间的正确方法是同时调用 QPF 和 QPC 并将时间计算为:

double seconds = QPC / QPF;

编辑:

GetTickCount() 精度较差,大约为 5 毫秒,但仍适用于大多数应用程序。要测量非常小的时间段,只有一个选项:QPC/QPF。

【讨论】:

  • "最可靠的...:QPC / QPF" 怎么样?如果 QPF 完全可以改变,那么如果它在调用 QPC 之后立即改变呢?我不是要挑剔任何人,我只是想了解你的理由。我们是否有任何证据表明 QPF 可以改变?如果可以,何时改变?
  • 它是一个多线程多核系统。因此,正如 MSDN 所说,我必须确保它在单个处理器上运行。是他们说我们应该为此使用该功能。
  • @500-InternalServerError: 抱歉,解释得很糟糕,更正了我的回答。上下文:计算小时间段通常需要高精度的时间,这是Windows平台上最可靠的方式。
  • 我发现 timeGetTime() 是比 GetTickCount() 更好的解决方案。不是吗?它有点灵活,比 GetTickCount() 更快。说它以防万一其他人四处寻找答案。 :)
  • @Deamonpog: timeGetTime() 是一个多媒体计时器,与 GetTickCount() 精度一样差,但需要与 winmm.lib 链接。性能差异(如果有的话)通常对其典型应用无关紧要。
【解决方案3】:

我个人更喜欢时间戳计数器,它是 x86 架构中的 64 位计数器,每个内部时钟周期都会递增。它使用 rdtsc 指令读取,并返回 edx:eax 寄存器 (x86-32) 和 rdx:rax (x86-64) 中的计数器值。

指令存在问题,但那是很多年前的事了。如今,“绿色功能”导致负载相关的执行频率发生变化,使得计算经过的时间变得更加困难,但经过的时钟周期不是问题。

unsigned long long startCycle, endCycle, elapsedCycles, overhead;

// @ start of program

overhead=instruction_rdtsc ();
overhead=instruction_rdtsc ()-overhead;

// preparing to measure

startCycle=instruction_rdtsc ();

// (sequence to measure)

endCycle=instruction_rdtsc ();

elapsedCycles=endCycle-startCycle-overhead;

应该确定指令本身的开销。我发现英特尔处理器的开销比 AMD 处理器小。应该多次测量开销 - 例如循环 - 以找到可能的最低值。测量的序列越长,开销就越小。该指令可以在应用程序中插入永久性性能计量,以便能够在正常(非性能测试)执行下测量其实际性能。

由于流水线和乱序执行问题,不应测量非常短的序列。有些人建议在 rdtsc 之前插入 cpuid 指令,但这仅意味着实际时钟计数变得大于实际。我认为 30 左右的循环计数具有指示性,而 100 左右或更大的循环计数通常是可靠的。中间有一个灰色地带。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2013-06-24
    • 2015-08-31
    • 1970-01-01
    • 2021-01-22
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多