我遇到了一个问题,即我的游戏循环每秒钟结束一次(可变间隔)。单帧然后需要60ms,而所有其他帧需要不到1ms。
在简化了很多之后,我结束了以下程序,它重现了这个bug。它只测量帧时间并报告它。
#include <iostream>
#include "windows.h"
int main()
{
unsigned long long frequency, tic, toc;
QueryPerformanceFrequency((LARGE_INTEGER*)&frequency);
QueryPerformanceCounter((LARGE_INTEGER*)&tic);
double deltaTime = 0.0;
while( true )
{
//if(deltaTime > 0.01)
std::cerr << deltaTime << std::endl;
QueryPerformanceCounter((LARGE_INTEGER*)&toc);
deltaTime = (toc - tic) / double(frequency);
tic = toc;
if(deltaTime < 0.01) deltaTime = 0.01;
}
}
同样,许多帧中的一帧比其他帧慢得多。添加if
让错误消失(cerr永远不会被调用)。我原来的问题不包含任何cerr / cout。但是,我认为这是同一错误的再现。
cerr在每次迭代中都被刷新,所以这不是创建单个慢帧的情况。我从分析器(Very Sleepy)知道流内部使用锁/临界区,但这不应该改变任何东西,因为程序是单线程的。
是什么导致单次迭代停止那么多?
编辑:我做了更多测试:
std::this_thread::sleep_for( std::chrono::milliseconds(7) );
并因此降低进程CPU利用率不会改变任何内容。printf("%f\n", deltaTime);
问题消失了(可能是因为与流相比,它不使用互斥和内存分配)答案 0 :(得分:2)
Windows的设计并不保证任何执行时间的上限,因为它使用某种逻辑动态地将运行时资源分配给所有程序 - 例如,调度程序将资源分配给具有高优先级的进程,并且饿死某些情况下的优先流程。程序在统计上更有可能 - 最终 - 如果它们运行紧密的循环并消耗大量的CPU资源,会受到这些事情的影响。因为 - 最终 - 调度程序将临时提升正在饥饿的程序的优先级和/或降低其他程序的优先级(在您的情况下,通过运行紧密循环)。
将输出设置为std::cerr
条件不会改变这种情况发生的事实 - 它只会改变它在指定时间间隔内发生的可能性,因为它会改变程序在循环中使用系统资源的方式,因此改变了它与系统调度程序,策略等的交互方式。
这类事情会影响在所有非实时操作系统中运行的程序,但具体影响取决于每个操作系统的实现方式(例如,调度策略,控制程序对资源访问的其他策略等)。这种失速总是存在非零概率(即使它很小)。
如果您希望绝对保证不会出现这种情况,那么您将需要一个实时操作系统。这些系统设计用于在时序意义上更加可预测地执行操作,但这需要权衡,因为它还要求您的程序设计时必须知道它们必须在指定的时间间隔内完成指定函数的执行。实时操作系统使用不同的策略,但是如果程序没有考虑到这些因素,那么它们强制执行约束会导致程序出现故障。
答案 1 :(得分:1)
我不确定它,但可能是系统正在中断你的主线程以让其他人运行,并且因为它需要一些时间(我记得在我的Windows XP pc上量子是10ms),它会拖延一个框架。
这是非常明显的,因为它是一个单线程应用程序,如果你使用多个线程,它们通常在处理器的几个核心上发送(如果可用),并且停顿仍然在这里但不太重要(如果你实现的话)你的应用程序逻辑正确。)
编辑:here您可以获得有关Windows和Linux调度程序的更多信息。基本上,Windows使用量子(在Windows Server上从几毫秒到120毫秒不等)。
编辑2:您可以看到更详细的explanation on the windows scheduler here。