如何为适当的CPU利用率编写代码

本文关键字:利用率 代码 CPU | 更新日期: 2023-09-27 18:08:52

请原谅我,这可能有点难以解释清楚。我试图了解如何编写一个程序,只使用它需要的CPU量。这解释起来有点混乱,所以我将使用一个真实的例子。

我制作了一款带有无限主游戏循环的《俄罗斯方块》游戏。我把它限制在每秒40帧。但是循环仍然每秒执行数千甚至数百万次。它只会在足够的时间将其限制在40帧/秒时渲染。

因为我有一个4核CPU,当我运行游戏时,一切都很好,游戏运行良好。但是游戏进程的CPU使用率保持在25%。这是预期的,因为它是一个无限循环,并持续运行。

然后我在线读取,在主循环中添加1毫秒的延迟。这立即将使用率降低到1%左右或更少。这很好,但现在我故意在每个循环中等待1毫秒。这是有效的,因为我的主循环需要更少的时间来执行,并且1毫秒的延迟不会影响游戏。

但是如果我制作更大型的游戏呢?具有更长时间和更多处理器密集循环的游戏。如果我需要1毫秒的时间来平稳运行游戏怎么办?然后,如果我移除延迟,处理器将再次跳到25%。如果我添加延迟,游戏就会很慢,可能会有一些延迟。

在这种情况下理想的解决方案是什么?真正的游戏/应用程序如何编码来防止这个问题?

如何为适当的CPU利用率编写代码

由于您在标记中列出了三种不同的语言,因此我将保持一般性,不提供代码示例。

一般来说,为了避免占用CPU, 永远不要在的每次迭代中都有一个不的循环:

  • 做有用的工作
  • 基于循环计数器的跳过
  • 或调用阻塞调用,阻塞I/O或阻塞线程wait()

sleep()是阻塞调用的一个例子,但正如您所观察到的,在许多情况下,它有点像一个大杂牌。

:

while(true) {
    if(some_condition) {
        foo();
    }
}

…是坏的。(我的一个朋友曾经用这样的代码使一个共享主机崩溃)

你需要找到一个调用你的显示API阻塞,直到垂直同步。我相信在DirectX, device.Present()是一个这样的调用,如果设备设置得当。

在单线程游戏中,逻辑可能是:

 while(game is active)
    read user input
    calculate next frame
    blocking call to display API

因此,CPU得到休息,等待每次垂直同步。

通常至少有两个线程,一个处理渲染循环,另一个处理游戏状态。在这种情况下,呈现循环需要像以前一样等待垂直同步。游戏状态循环需要阻塞,直到渲染循环准备好。

渲染线程循环:

  while(game is active)
      notify()
      prepare_frame(game_state)
      blocking call to display API

游戏状态线程循环:

  while(game is active)
      read user input
      update game_state
      wait(display_loop_thread)

一定要理解thread wait/notify/join才能理解这个

这个模型允许你有其他线程也影响游戏状态。例如,另一个线程可能控制一个AI敌人。

另一种选择是使计算事件驱动,并在vsync后触发它们:

 while(game is active)
     calculate next frame
     blocking call to display API
     gameLogic.onFrame()

如果onFrame()需要比一帧更长的时间来完成,那么游戏的帧率将受到影响。这是否重要取决于游戏;解决方案超出了这个答案的范围——如果它对你很重要,那么可能是时候买一本关于电子游戏架构的书了。

不是睡1毫秒,而是睡X毫秒,其中X是用公式max(NextDrawingTime-CurrentTime, 0)

计算的