当前位置:0101部落 >> 技术文档 >> 游戏图形 >> 浏览文章 【字体:
针对游戏的线程基本信息
作者:Jeff Andrews 日期:2008年02月21日 阅读次数: 来源:互联网
 

在游戏开发中利用英特尔® 双核处理器,并了解英特尔® 编译器如何优化代码。

即使有了超线程技术(HT 技术)提供的优势,出于性能原因,通常也会因这种技术的潜在复杂性而避免使用线程游戏引擎。但是,随着具备双核功能的英特尔® 奔腾® D 处理器以及具备双核和 HT 技术功能的英特尔® 奔腾® 4 处理器至尊版的出现,对于所有四种逻辑处理器,目前将游戏处理功率加倍(甚至更多)的可能性即将到来。但是,为了使速度大幅度增加,需要对游戏进行线程处理,以提高性能。

因为涉及到潜在的复杂性,所以通常在进行线程处理时应非常谨慎。特别是对于那些不熟悉服务器编程的人员来说,更应谨慎一些,因为目前仅在为提高性能而进行线程处理时需要使用桌面区域。游戏,特别是具有复杂数据结构和在功能上相互依赖的游戏,几乎不可能进行线程处理。但是,游戏和其他桌面应用程序很难进行线程处理的原因在于,其中的大多数产品在设计时没有想到线程处理。

本文旨在提示和说服游戏设计人员和开发人员针对线程处理进行开发。我们希望您目前在开发时就开始对游戏进行线程处理,以充分利用目前随英特尔® 产品一同提供的两种和四种处理器。提前考虑在一个包中包含具备多个可用处理器的处理器产品更需要对游戏进行线程处理,并提供更好的机会借助可用处理功率执行更多任务。

线程处理概要
我们将“线程处理”定义为在不同处理器上同时运行不同的代码段。下图显示了单线程处理与双线程处理之间的差异。单线程代码在运行过程中连续执行。请注意,双线程代码有两个代码段在运行期间并发执行。通过同时运行这两个可并行的代码段,现在执行整个运行过程所用的时间变少了(请注意虚线区域)。

点此在新窗口浏览图片

图 1 显示了图中的不均衡情况。这种不均衡情况是指两个并行代码块没有同时完成其执行任务。这两个块没有同步完成其执行任务的原因在于,它们要完成的工作量不相等。应该注意到这一点,因为正确均衡线程在游戏线程处理中是非常关键的。

图中的屏障表示所有线程完成其执行任务之前等待的时间。等待是很关键的,因为您不想让并行代码覆盖其串行代码的执行。(在本示例中,串行代码取决于并行代码段的结果。)

并行模型

点此在新窗口浏览图片

有两种类型的并行模型:数据分解功能分解。简言之,数据分解就是对单个功能进行线程处理,以同时在两个或多个数据块上操作。另一方面,功能分解必须将不同的功能代码块置于不同的线程上,以充分利用并行优势。

本文仅涉及到功能分解,原因有两个:a.) 与数据分解模型相比,它适用于更多游戏;b.) 实现这种分解更难一些。有关游戏中的两种不同并行模型的深入讨论,请参阅本文的“其他资源”部分。
线程正确性步骤
游戏的实际线程处理可以开始之前,可执行几个步骤以帮助线程代码以最佳方式运行。针对功能分解线程模型对游戏进行线程处理时,这些步骤是必要的。

确定功能块

首先,确定游戏中的不同功能块。这应该是一个相当简单的过程,因为典型的游戏设计围绕着说明功能块的文档开展。此步骤包括确定块之间的交互作用和程序执行顺序,这应该会形成一个块流程图。

请注意,可能需要根据下面两部分中的步骤结果重新设计此块流程图。这些更改将在项目设计期间和实施阶段发生。

确定块之间的相关性

列出功能块及它们之间的相互连接后,必须确定所有块之间的数据相关性。此信息有助于确定是否应该复制数据组,或者是否应该同步对数据组的访问。决定这些情况时需要考虑几个因素,但主要因素是数据大小。在串行代码段中复制数据以及在并行代码段中同步数据访问可能会比较快。
点此在新窗口浏览图片
(单击查看大图)

确定线程划分

可以根据数据相关性和处理器使用情况来匹配不同的代码块。请尝试公平处理,将相应的代码块置于相应的线程中。在两个或四个线程的每一个线程中放置功能的方法要使得线程几乎同时完成其执行任务。这样,一个线程在其他线程完成执行任务之前不会等待太长时间,因而不会浪费时间。

要考虑的另一个问题是正确均衡执行任务是否会导致程序花费更多时间复制数据或同步数据访问。用于访问同一数据的功能应该位于同一线程上(如果可能)。这样,它们便可以按顺序执行,而且可避免同步需要。选择的方法取决于数据访问方式。如果按顺序访问数据且处理器均衡支持将两个功能置于单独的线程上,则同步访问效果会更好。

遗憾的是,确定均衡时没有一种妥善的解决方案。为了获得最佳的可能组合,必须进行某些实验。请注意,即使线程实现并不理想,但仍然还是比让处理器浪费时间更好一点。

在考虑在多个处理器上均衡性能时,不要与特定数目的处理器进行太多连接,这一点非常重要。针对线程处理优化代码时,还应该针对未知数目的处理器对其进行优化。我们正在开发的一些芯片支持许多处理器,因此几乎无法预测未来代码可以利用多少处理器。请设计并行化方案,以将功能进行排队并在处理器可用时执行。这样一来,代码将支持系统中的 1 到 n 个处理器,其中 n 为所管理的要实现并行化的任务数。
对游戏进行线程处理
确定要并行处理的块后,便可以创建应用程序的线程。可以使用多种方法对代码进行线程处理。例如,可以使用目标操作系统提供的 API 或跨平台 API。另外,还可以选择使用 OpenMP*,该方法在编译器内提供内置线程处理支持。上述方法中最简单的一种方法是 OpenMP,此方法注重线程创建和同步。某些编译器(如英特尔® C++ 编译器)提供此支持。

OpenMP“段”

点此在新窗口浏览图片OpenMP 段指令是一种对不同的功能块快速执行线程处理的方法。图 4 中的代码段显示了将四个不同功能的执行过程分解到两个线程中的简单程度。在这里,仅用了三行代码(大括号不计算在内)对这些功能进行线程处理。

尽管这是一种执行功能线程的便捷方式,但还是有缺陷:OpenMP 创建的线程数限于定义的段组数或可用的处理器数之间较少的那一个。因此,即使该示例中的代码已在包含四个处理器的系统中运行了,也还是仅使用两个处理器。相反,如果段块比处理器多,则 OpenMP 将确定如何调度要执行的块,这可能不是最理想的。

该示例中的屏障显示应用程序在等待所有线程完成之前所处的位置。

请注意,还可以使用 OpenMP 创建不安全的线程代码,编程人员必须考虑所有线程处理条件(即使使用 OpenMP,也是如此)。

英特尔的任务队列

点此在新窗口浏览图片英特尔 C++ 编译器包括对 OpenMP 规范的扩展,允许对线程池内的任务进行排队。这样一来,如果您具备的可并行化功能数与处理器数一样多时,就可以更容易地利用系统中的所有可用处理器。

图 5 中的代码显示如何任务排队对功能进行线程处理。每个任务指令都会将相应的功能置于队列中。某个处理器可用时,就会在该处理器上执行队列中的下一个功能。

这样一来,将目标锁定在数量可变的处理器上就容易多了。但是,此方法需要更好地规划如何将功能发放到队列,因为运行并行化段的处理器数目是未知的。最终,此方法将帮助 OpenMP 代码段充分利用所有可用的处理器。
对游戏进行线程处理不是一项很难的任务。妥善规划后,可以一开始就在游戏中设计线程处理,以避免许多潜在的障碍。随着台式机系统中处理器数目的增加,线程处理将变得越来越重要。使用两个以上处理器设计线程算法的想法有助于确保在游戏中实现最大处理功率。

除了编译器中的支持 OpenMP 功能以外,英特尔还提供了多种其他工具,用于帮助简化线程处理流程。您可以在英特尔开发人员网站中找到这些工具。
相关文章:
 
下一篇:常见问题回答
 
  ·关于我们 ·访客留言 ·友情链接 ·
粤ICP备08022036号
站长:ds1010 站长信箱:0101ds@gmail.com

0101部落开发 @ www.ds0101.net 2007-2008