3

我正在编写一个游戏引擎,我需要一种方法来获得一个精确和准确的“deltatime”值,从中得出当前的 FPS 进行调试并限制帧速率(这对我们的项目很重要)。

做了一些研究,我发现最好的方法之一是使用 WinAPI 的QueryPerformanceCounter函数。GetTicksCount必须用来防止向前反跳,但它本身不是很准确。

现在,问题QueryPerformanceCounter在于,它显然可能会返回看起来像时间扭曲的值(即,相对于过去的另一个调用,调用可能会在时间上返回一个更早的值)。仅当将使用给定处理器内核获得的值与使用另一个处理器内核获得的值进行比较时才会发生这种情况,这使我想到了促使我发表这篇文章的终极问题:

  1. 操作系统可以在线程已经运行时将线程“重新分配”到另一个内核,还是将线程分配给给定的内核,直到线程死亡?
  2. 如果无法重新分配线程(至少对我来说很有意义),那么为什么我可以做类似的事情SetThreadAffinityMask(GetCurrentThread(),mask)Ogre3D在其Ogre::Timer 类 (Windows implementation)中做到这一点,我假设这是为了避免时间倒退。但要做到这一点,我将不得不考虑操作系统任意将线程从一个内核移动到另一个内核的可能性,这对我来说似乎很奇怪(不知道为什么)。

我想这就是我现在想知道的。谢谢。

4

6 回答 6

2

除非线程具有处理器关联掩码,否则调度程序会将其从一个处理器移动到另一个处理器,以便为其提供执行时间。由于在处理器之间移动线程会降低性能,因此它会尽量不移动它,但给它一个执行的处理器比不移动它具有优先级。因此,通常线程会移动。

至于定时器API。 timeGetTime是为多媒体计时而设计的,所以它比GetTickCount.

QueryPerformanceCounter().不过,它仍然是您最精确的测量值。微软对此有话要说。

在多处理器计算机上,调用哪个处理器并不重要。但是,由于基本输入/输出系统 (BIOS) 或硬件抽象层 (HAL) 中的错误,您可能会在不同的处理器上获得不同的结果。要指定线程的处理器亲和性,请使用 SetThreadAffinityMask 函数。

因此,如果您在特定计算机上进行时序测试,您可能不必担心 QPC 会倒退,您应该进行一些测试,看看它在您的机器上是否重要。

于 2010-02-03T20:00:02.887 回答
1

即使您使用 SetAffinityMask 将线程锁定到一个处理器,如果您真的不走运并且硬件很糟糕,QPC 也可以向后运行。最好只处理 QPC 返回错误值的情况。在 Windows 7 中,QPC 在这方面得到了显着改进,但是由于您正在编写游戏,因此您可能针对的是 XP,它对您没有帮助。

另外,不要设置线程亲和性,你可能会死锁自己,引入奇怪的时间和性能错误,通常会让你自己伤心。

于 2010-02-03T20:26:30.693 回答
1

线程可以并且正在(除非它们具有关联集)在线程运行时重新分配。Windows 将负载分散到所有处理器上以最大限度地提高性能。

于 2010-02-03T19:48:44.883 回答
0

因此,我们通常必须在运行计时时将游戏锁定在单个线程中;我们没有找到有效的解决方法,因为在测量性能时您需要亚微秒级的分辨率。

让它变得更容易的一件事是,我们的引擎被分割成总是同时运行的广泛组件(例如游戏/逻辑“服务器”,输入/图形“客户端”,音频,渲染都是它们自己的线程),所以什么我们所做的是将这些线程中的每一个锁定到它自己的核心上并独立地对它们进行计时。

类似地,因为我们知道例如渲染循环总是在核心 0 上,所以我们使用它来计时帧速率。

于 2010-02-04T01:04:15.097 回答
0

1) 线程可以将线程分配给具有空闲处理时间的任何内核。这就是为什么您经常会看到软件在四核机器上使用 50%,而当您检查图表时,它使用了所有四核机器的一半。

2) 见 1 ;)

于 2010-02-03T19:49:26.240 回答
0

使用 SetThreadAffinity() 通常不是一个好主意,除非线程只进行计时。如果您将线程锁定到单核,那么您首先会消除拥有多核系统的所有好处。您的应用程序无法再扩展。即使您启动了应用程序的多个实例,它们仍将被锁定到一个核心。

于 2010-02-03T19:58:35.730 回答