使用XNA时CPU占用率过高

8
我今天注意到,当我编译和运行一个新的XNA 4.0游戏时,其中一个CPU线程运行在100%,帧速率下降到54 FPS。
奇怪的是,有时它以60 FPS工作,但然后它只会下降到54 FPS。
我以前没有注意到这种行为,所以我不知道这是否正常。我卸载了我的防病毒软件并重新安装了XNA Game Studio、XNA Redistributable和.NET Framework 4。
如果我将IsFixedTimeStep设置为false,游戏以60 FPS运行,CPU使用率很低(1-2%)。但据我所知,这要求我使用ElapsedGameTime进行速度计算,但我不知道如何做,因为我对XNA相当新。但是有些人说将其设置为false可以减少动画抖动。
我已经检查了这个论坛帖子,但没有人找到一个好的解决方案。
有人遇到过这个问题吗?
编辑: 我进行了更多的研究,并实现了一个FPS计数器(直到现在,我用Fraps测量它),我的计数器显示游戏以60 FPS运行(IsFixedTimeStep = true),所以这解决了FPS问题,但高CPU使用率仍然存在。这是每个人都会发生的吗?

我也遇到过这个问题,试图解决它真的让我头痛不已,因为性能分析只告诉我时间花在了框架代码的某个地方。有趣的是,如果程序不在前台,它仍然可以正确地更新/绘制/运行,但 CPU 使用率会降至正常水平。 - Cheezmeister
你有多少个核心?通常游戏循环的“忙等待”while(1) { update(); draw(); }不会休眠(否则游戏会卡顿),因此您应该至少期望使用_一个逻辑核心_的100%。 (因此,如果您有双核CPU,则应为50%,具有超线程的双核心应该约为25%) - bobobobo
2个回答

19
根据XBox Live Indie Games论坛上的这个讨论,显然在一些处理器(和操作系统)上,当使用默认值Game.IsFixedTimeStep时,XNA会占用一个核心的100% CPU时间
一个常见的解决方案(对我也有效)是在你的Game构造函数中添加以下内容:
IsFixedTimeStep = false;

这是什么意思?

Game.IsFixedTimeStep 属性为 true 时,确保您的每一帧(Update(), Draw(), ...)在指定的固定时间间隔 Game.TargetElapsedTime 内被调用。默认值为每秒 60 次调用

Game.IsFixedTimeStep = false 时,下一帧的调用将在前一帧完成后发生。如下时间图所示:

enter image description here


这种变化会对我的代码产生什么影响?

所有基于固定时间计算的操作(移动定时等)需要进行修改以适应可变时间步长。幸运的是,这非常简单

假设您原来有:

Vector3 velocity;
Vector3 position;

对于某些对象,您正在使用以下方式更新其位置

position += velocity;

默认情况下,这意味着对象的速度是60 * velocity.Length()每秒单位。您每秒添加60次速度。

当您将该句话翻译成代码时,您会得到这个简单的修改

position += velocity * 60 * gameTime.ElapsedGameTime.TotalSeconds;

简单来说:你根据经过的时间来缩放添加的值。
在执行移动(或计时等)操作的位置进行这些修改,可以确保你的游戏表现与固定时间步长时相同。

这真的让一切变得更加困难。直到现在,我只需要将速度添加到当前位置。难道真的没有其他解决方法吗? - Klemen Košir
1
将我的更新与GameTime集成是我XNA应用程序中最好的事情之一,这绝对值得(非常小的)努力。 - Owen
亲爱的上帝,它使用了一个忙循环来进行固定步骤?而且默认使用它?微软,我以为你应该知道得更好! - Cheezmeister
关于你的“编辑2”,我不明白固定时间步长如何影响速度——经过的时间仍然是相同的,只是增量不同而已! - Cameron
你试图解决一个不存在的问题。游戏应用程序在一个核心上使用100%的CPU是正常的。 - bobobobo

-1
高 CPU 使用率(一个核心上达到 100%)对于游戏来说并不是问题,换句话说,这是预期的。编写游戏时使用 CPU 的方式要求你这样做。
打开代码环境并编写一个简单的程序。
void main(){
    while(1) puts( "aaah" ) ;
}

打开CPU监视器,看到一个核心的使用率达到了100%。

现在你的游戏正在执行以下操作:

void main(){
    while(1){
        update() ;
        draw() ;

        while( getTimeElapsedThisFrame() < 1.0/60.0 ) ; // busy wait, until 16ms have passed
    }
}

基本上,你调用update()draw(),这将占用你计算一帧的大部分16ms时间(当你的游戏有很多东西时,你将会占用大部分时间)。

现在,由于操作系统睡眠分辨率不精确(如果你调用sleep(1ms),它实际上可能会睡眠20ms,然后你的应用程序才会再次唤醒),所以游戏永远不会调用操作系统函数SleepSleep函数会导致你的游戏“过度休眠”,使游戏看起来卡顿缓慢。

好的CPU使用率不值得拥有一个无响应的游戏应用程序。因此,标准是“忙等待”,并消耗CPU时间。


我不同意。游戏可以是GPU或CPU绑定的。如果它是GPU绑定的,对于图形密集型游戏来说很可能CPU会花费一部分时间处于空闲状态。但即使您在这些游戏中增加了额外的逻辑,那也不会是您的输入逻辑,因为Update()每帧只调用一次。响应速度仍然保持不变。此外,将IsFixedTimeStep = false设置为XNA尽可能快地调用帧(更新、绘制),而不是在固定间隔内调用。 - neeKo
CPU绑定或GPU绑定仅指瓶颈是CPU还是GPU。这在这里并不重要。我上面展示的代码显示了当帧被绘制时XNA框架内部发生的情况,并且说明为什么你应该期望在游戏运行时至少有一个核心的100%处于使用状态。 - bobobobo
1
我仍然不同意。创建一个XNA游戏项目并运行它。它会在任何核心上占用100%吗?对我来说不是。在另一台PC上,直到我进行了更改,它才使用了100%。即使是您发布的这个无限循环也从未使用过100% - 看看 - neeKo
它正在使用一个核心的100%(1核心/8=12.5%你总可用CPU)。你所看到的(将100%的负载“重新分配”在你的8个核心之间)一定有一个名字,但我不知道是什么。 - bobobobo
1
你的推理是:游戏应用程序有一个循环,因此它们必须在一个核心上使用100%的CPU。但是,如果您实际运行XNA游戏项目,则不会在一个核心上获得100%的使用率-因此您的推理是错误的。如果您不想亲自检查,这里有一个例子。因此,在一个核心上限制了100%的使用实际上是一个真正的问题,只会在某些配置上偶尔发生。 - neeKo

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接