为什么大型程序(如游戏)不使用不同线程的负载?

时间:2009-02-21 00:26:22

标签: multithreading concurrency multicore

我不知道商业游戏是如何在内部运作的,但我遇到的开源游戏似乎并没有大规模地进入线程。大多数其他桌面应用程序也是如此,通常使用两个或三个线程(例如程序逻辑和GUI更新)。

为什么游戏没有很多线程?例如物理,声音,图形,AI等的单独线程?

10 个答案:

答案 0 :(得分:23)

我不知道您玩过的游戏,但大多数游戏都在单独的线程上运行声音。网络代码,至少套接字监听器在一个单独的线程上运行。

然而,游戏引擎的其余部分通常在单个线程中运行。这是有原因的。例如,游戏中的大多数处理运行单个依赖链。图形依赖于物理引擎的状态,人工智能也是如此。设计多线程意味着您必须在各个子系统之间存在帧延迟以实现并发。如果这些子系统是每帧线性计算的,那么您可以更快地获得响应时间和更快速的游戏。从并行化中获益最多的游戏部分当然是渲染子系统,它被卸载到高度并行化的图形加速器卡上。

答案 1 :(得分:15)

主要原因是,听起来优雅,在程序中使用多个线程就像3D游戏一样复杂,实际上非常非常困难。此外,在最近推出低成本多核系统之前,使用多线程并没有提供太多的性能激励。

答案 2 :(得分:15)

您需要思考,线程的实际好处是什么?请记住,在单个核心机器上,线程实际上不允许并发执行,只是它的印象。在幕后,CPU在不同线程之间进行上下文切换,每次都做一点工作。因此,如果我有几个不需要等待的任务,那么同时运行它们(在单个核心上)将比线性运行它们更快。事实上,由于频繁上下文切换的额外开销,它会更慢。

如果是这种情况那么,为什么要在单个核心机器上使用线程呢?首先,因为有时任务可能需要长时间等待某些外部资源(如磁盘或其他硬件设备)才能使用。虽然任务处于等待阶段,但线程允许其他任务继续,从而使CPU的时间更有效率。

其次,任务可能有某种完成的最后期限,特别是如果他们正在响应某个事件。典型的例子是应用程序的用户界面。计算机应尽快响应用户操作事件,即使它正忙于执行其他长时间运行的任务,否则用户将变得激动并可能认为应用程序已崩溃。线程允许这种情况发生。

至于游戏,我不是游戏程序员,但我对情况的理解是这样的:3D游戏创造了游戏世界的程序化模型;玩家,敌人,物品,地形等。此游戏世界将根据自上次更新后经过的时间量以不连续的步骤进行更新。因此,如果从最后一次游戏循环开始经过1ms,则通过使用其速度和经过时间来确定对象的位置来更新对象的位置(显然物理学比这更复杂,但是你得到了理念)。其他因素(如AI和输入键)也可能有助于更新。当一切都完成后,更新的游戏世界将呈现为一个新框架,并且该过程将再次开始。这个过程通常每秒发生多次。

当我们以这种方式思考游戏循环时,我们可以看到引擎实际上实现了与线程非常相似的目标。它有许多长期运行的任务(更新世界的物理,处理用户输入等),它给人的印象是它们同时发生,将它们分解成小块工作并交错这些部分,而不是依赖于CPU或操作系统来管理在每个上花费的时间,它本身就是这样做的。这意味着它可以使所有不同的任务保持正确同步,并避免真正线程带来的复杂性:锁定,抢占,重入代码等。这种方法也没有性能影响,因为我们说过单核机器无论如何都只能线性地执行代码。

拥有多核系统时情况会发生变化。现在,任务可以真正同时运行,并且使用线程来处理游戏世界更新的不同部分可能确实有好处,只要我们能够设法同步结果以呈现一致的帧。因此,我们期望随着多核系统的出现,游戏引擎开发人员将致力于此。事实证明,他们是。 Valve是Half Life的制造商,最近在他们的Source Engine中引入了多处理器支持,我想其他许多引擎开发者也在效仿。

嗯,结果比我想象的要长一点。我不是线程或游戏专家,但我希望我没有做出任何特别明显的错误。如果我有,我相信人们会纠正我:)

答案 3 :(得分:14)

如今许多游戏都在使用“任务”或“工作”系统进行并行处理。也就是说,游戏产生固定数量的工作线程,用于多个任务。工作分为小块并排队,然后在工作线程可用时发送给它们进行处理。

这在游戏机上变得尤为常见。 PS3基于Cell架构,因此您需要使用并行处理来获得系统外的最佳性能。 Xbox 360可以模拟为PS3设计的任务/作业设置,因为它具有多个核心。您可能会发现大多数游戏中很多系统设计都是在360,PS3和PC代码库之间共享的,因此PC最有可能采用相同的策略。

虽然很难编写线程安全代码,但正如许多其他答案所表明的那样,我认为你所看到的事情有其他一些原因:

  • 首先,许多开源游戏已有几年历史。特别是对于这一代控制台,如上所述,并行编程变得流行甚至是必要的。
  • 其次,很少有开源项目似乎关注获得最高性能。正如John Carmack向Utah GLX项目所指出的那样,高度优化的代码通常比未经优化的代码更难维护,因此后者在开源环境中通常是首选。
  • 第三,我不会将游戏创建的少量线程意味着它没有很好地使用并行作业。

答案 4 :(得分:1)

我即将发布与威廉相同的内容,但我想稍微扩展一下。为未来编写最佳代码非常困难。如果选择编写可以扩展到硬件的东西,而不是编写可以在硬件上工作的东西,大多数人会选择做后者。由于单核范式已经与我们共存了很长时间,因此大多数编写的代码(特别是对于那些有极大压力要求将其推出的游戏)并不是未来的证明。

x86对游戏程序员非常友好,因为我们不必考虑不那么宽容的硬件平台的后果。

答案 5 :(得分:1)

除了针对多核编程的技术挑战之外,商业游戏必须在没有多核的低端系统上运行良好才能赚钱。

现在多核处理器已经出现了一段时间,主要的游戏控制台有多个内核,双核显示在PC游戏的最低系统要求列表上只是时间问题。

这是来自英特尔的an interview with Orion Granatir的链接,他正在谈论让游戏开发者利用多线程。

答案 6 :(得分:1)

这里的每个人都正确地声称多线程很难,这一点非常令人难过。我们迫切需要简化并发系统。

我个人认为我们需要一种范式转换和新工具。

答案 7 :(得分:0)

使用大量线程时,竞争条件和数据锁定存在许多问题。由于游戏的不同部分相互依赖,因此完成使用大量线程所需的所有额外工程并没有多大意义。

答案 8 :(得分:0)

使用线程非常困难,并且大多数GUI API都基于事件驱动编码。线程要求使用锁定机制,这会给代码增加延迟,并且通常该延迟是不可预测的,具体取决于当前持有锁的人。

让一个(或者很少)线程以事件驱动的方式处理事物而不是数百个线程都会导致奇怪的和不可重复的错误,这对我来说似乎是明智的。

答案 9 :(得分:0)

线程已经死了,宝贝。

实际上,在游戏开发中,线程不会超出卸载非常专门的任务(如网络和加载)。作业系统似乎是前进的唯一途径,因为即使在PC上,8个CPU系统也变得越来越普遍。而且你几乎可以保证即将推出的超级多核系统如英特尔的Larrabee将基于作业系统。

这在Playstation3和XBOX360项目中实现有点痛苦,现在看来苹果已经加入了他们在Snow Leopard中的“革命性”Grand Central Dispatch系统。

线程有它们的位置,但是“把所有东西都放在一个线程中它会全部运行得更快”的天真承诺在实践中根本不起作用。