Windows 游戏的首要问题
Microsoft Windows 游戏和图形技术开发人员关系组每年对许多 Windows 游戏执行性能分析。 在这些会话中,我们将获得实践经验,以与我们每天收到的开发人员反馈和查询联系在一起。 有时,我们帮助跟踪游戏中的神秘崩溃或其他问题,这让我们进一步了解开发人员遇到的问题。
本文重点介绍了我们在当前代电脑游戏中看到的许多常见问题。
- CPU-Limited 性能
- 不良批处理管理
- 过多的内存复制
- 动态绘图提交 过度使用
- 文件处理 高开销
- 缓慢和令人沮丧的安装
- 缺少物理内存
- Real-Time 音频采样率转换 Over-Reliance
- 虚拟内存 的 碎片
- 控制字 Floating-Point作
- DirectX 运行时 的可选安装
- 线程同步的过度使用
- RDTSC 的使用
CPU-Limited 性能
绝大多数游戏受高性能图形处理单元(GPU)系统上的 CPU 性能限制。 这有时是由于对绘图提交的批处理使用不当,但更常见的是,这是因为其他游戏系统消耗了大部分可用 CPU 周期。 在少数情况下,我们已将 GPU 视为限制,原因是高填充率或像素着色器需求、高分辨率设置或视频卡的顶点着色器性能低。
由于大多数游戏受 CPU 限制,因此最大的性能胜利来自优化 CPU 密集型游戏系统。 通常,AI 或物理系统和关联的碰撞检测逻辑是运行良好的 Microsoft Direct3D 应用程序中 CPU 周期的主要使用者。 任何改进这些系统的工作都可以提高游戏的整体性能。
批量管理不佳
实现与 GPU 的良好并行度要求绘制批包含足够的几何图形,并且着色器具有适当的复杂性,以使视频卡保持忙碌,同时不使用这么多批,命令缓冲区被淹没。 在当前生成硬件上,我们建议每帧大约 300 或更少的绘图批处理提交(性能较低的 CPU 更少),以防止驱动程序处理命令缓冲区成为性能瓶颈。 其他一些 API 状态调用和驱动程序组合可能会导致昂贵的 CPU 处理(例如,驱动程序编译着色器),因此强烈建议进行例行性能分析。
过多的内存复制
在开发大多数电脑游戏期间,开发人员使用方便的数据结构和字符串进行内容管理。 字符串比较、复制和其他作所需的 CPU 工作通常具有可度量的开销,尤其是在考虑到与缓存和内存子系统关联的性能命中时。 在开发这些系统时,应制定计划,以便在产品进入主要测试和发布阶段后,删除或尽量减少对字符串处理的依赖。
过度使用动态绘图提交
处理静态数据时,新式视频硬件性能良好。 高端适配器通常具有大量的视频内存,但动态数据无法有效地利用此内存。
虽然可为动态内容实现动态顶点缓冲区/索引缓冲区的合理有效使用模式,但许多游戏对其他静态内容过度使用此成语。 我们最常看到,二进制空间分区(BSP)树和基于门户的系统将几何图形存储在不映射到硬件的数据结构中,并且必须处理到每个帧的缓冲区中。 尽可能多地将内容放入静态资源可以大大减少将数据传输到视频卡的带宽开销,更好地利用板载 VRAM,并减少处理此内容时涉及的 CPU/缓存开销。
文件处理中的高开销
电脑游戏在长时间加载时间方面享有盛誉,特别是与具有严格加载时间要求的主机游戏相比。 我们对许多游戏使用文件子系统的方式的分析揭示了一些常见问题。
打开文件的开销通常远高于开发人员预期。 随着按需病毒扫描程序广泛使用,以及 NTFS 的附加功能,打开文件是一项相当昂贵的作。 因此,多次打开多个文件或反复打开和关闭同一文件是处理文件管理的一种不良方法。 某些游戏尝试通过在打开文件之前测试是否存在文件来降低此性能成本。 现实情况是,测试 NTFS 上是否存在文件需要打开文件,因此在打开前进行测试会导致支付两次费用。
允许加载项修改或模式的游戏,或者仍然包含用于检查替代数据文件的开发基架的游戏,在加载游戏时可能会因检查这些文件而出现重大延迟,即使这些文件不存在也是如此。 我们建议游戏仅在使用特殊命令行开关或其他模式指示器运行时检查这些文件,以便仅那些使用此功能的用户实际支付这些(通常广泛)检查的性能成本。
可以通过以下方法从文件系统获取其他性能:
- 适当地使用文件系统提示FILE_FLAG_RANDOM_ACCESS和FILE_FLAG_SEQUENTIAL_SCAN
- 调整缓冲区大小以避免大量调用 OS 的读/写 API
- 异步访问文件
- 在后台加载线程
我们还强烈建议脱机转换数据(在生成或安装时),而不是在游戏首次运行时依赖转换,因为这样做会对每个用户征收显著的性能税。
缓慢和令人沮丧的安装
我们看到的另一个常见问题是许多现代电脑游戏需要很长时间的安装时间。 安装程序多次提示用户,有时只是告诉用户,例如,“你不需要安装 DirectX”。通常,这些有问题的安装程序要求用户在实际开始安装游戏之前多次选择 下一个 或 正常。 一旦它开始,我们已经看到一些游戏需要一个小时或更多时间,然后用户有机会玩游戏。 我们强烈地认为,游戏体验的第一个小时不应该是安装。
建议使用多种方法来处理安装。 首先,保持提示简单且最少。 其次,构建游戏数据,以便尽可能直接从分发磁盘使用部分或所有数据文件 - 新式 DVD 驱动器的带宽非常高。 第三,请考虑在游戏中实施按需安装,以减少或消除安装过程,并允许用户尽快进入游戏。 (有关按需安装的详细信息,请参阅 Install-on-Demand for Games。)
有关游戏安装的详细信息,请参阅 简化游戏安装。
缺少物理内存的注意事项
由于市场上电脑硬件的可变性很大,游戏通常使用即席配置测试来选择图形详细信息级别的默认设置。 我们看到的一些游戏在这些测试中使用视频内存大小,但未能将它与物理内存大小相关联。 为了处理丢失的设备情况,大多数视频内存(卡上的本地 VRAM 和非本地 AGP 内存光圈)都必须通过使用托管资源或自定义数据结构来支持物理内存。 一些高端视频卡的 VRAM 大小与低端 CPU 内存的大小相媲美。 如果系统与视频卡相比物理内存有限,则无法有效利用大部分 VRAM,并且应配置较低的详细信息设置。
Real-Time 音频采样率转换 Over-Reliance
当音频系统需要在混合期间将播放速率转换为硬件缓冲区时,会出现 CPU 周期燃烧的另一个常见源。 使用 Windows 驱动程序模型(WDM)驱动程序时,硬件缓冲区格式不受直接应用程序控制,因为它是内核级资源;而是根据所有源的最高质量格式和硬件功能选择格式。 默认情况下,Windows XP 为此过程使用高质量的采样率转换,如果大多数音频样本需要速率转换,则会消耗大量 CPU 周期。
建议使用相同的采样率创建所有 DirectSound 缓冲区。 如果你使用 Microsoft Win32 waveOut 函数,则也应该对这些函数使用一致的采样率。 使用 WDM 驱动程序时,缓冲区将全部由内核混合,如果对其中一些驱动程序使用更高的采样率,则其余所有缓冲区的采样率将转换为匹配。 请注意,这意味着对所有音频示例使用相同的播放速率,包括任何流式处理音频解压缩缓冲区。 除非以 Windows 98 或 Windows Millennium Edition 为目标,否则设置主缓冲区速率不起作用。
注意
在 Windows Vista 及更高版本的作系统上,DirectSound 和 waveOut 对所有音频输出使用 Windows 音频会话 API (WASAPI)。
虚拟内存碎片
我们看到了与进程内存空间的 32 位限制相关的许多最近问题。 尽管用户模式进程的虚拟地址空间已超过 2 GB,但使用大型内存映射文件、自定义内存分配器以及增加 VRAM 大小(必须映射到进程空间)已开始导致虚拟内存空间分配失败的情况。 一些非Microsoft DLL 在虚拟地址空间中间使用固定启动位置,这会导致碎片导致分配失败。
当游戏使用尝试分配大量连续大量虚拟内存空间的自定义内存分配方案时,通常会出现这些问题。 建议编写分配器,以便根据需要请求更合理大小的虚拟地址空间部分。 例如,一次请求 64 或 256 MB,但不请求 1 GB。 但是,应注意不要造成进一步的碎片。 64 位作系统和硬件的出现将极大地帮助将来这些问题,但必须注意当前第 32 代系统。
Floating-Point Control Word 的作
作为调试帮助,一些开发人员通过对浮点控制词的作在浮点单元(FPU)上启用异常。 执行此作非常有问题,并可能导致进程崩溃。 与调用约定要求保留 ebx 寄存器一样,大多数系统假定 FPU 处于默认状态,将给出合理的结果,并且不会生成异常。 驱动程序和其他系统组件通常会根据标准错误值出现在坏情况的寄存器中的假设来计算结果,但如果启用了异常,这些结果将未经处理并导致崩溃。
Direct3D 会将浮点单位设置为单精度、舍入到最接近的调用线程初始化的一部分,除非使用D3DCREATE_FPU_PRESERVE标志,在这种情况下,浮点控制单词保持不变。 由于控制词是每线程设置,因此确保所有应用程序线程都设置为单精度模式可以优化性能。 请记住,调用 _control87 对 x64 本机编码无效,后者专门使用 SSE,在基于 PowerPC 的 Xbox 360 CPU 体系结构上非常昂贵。
注意
如果修改控件词,请使用 _controlfp_s,并且请注意,对于 x64 平台,无法通过控制词更改浮点精度。
在任何需要使用不同的舍入规则或其他行为(例如处理软件顶点着色器或编译)的库中,我们保存并还原控制词。 如果游戏需要使用非标准舍入或 FPU 异常,则应保存和还原浮点控制词,并且应确保它不会调用任何尚未证明无法证明免受这些问题(包括系统 API)的外部代码。
DirectX 运行时的可选安装
许多游戏询问用户是否安装 DirectX。 如果用户假定系统安装了最新的 DirectX 可再发行组件并跳过安装,并且随后安装成功,则可能会导致问题。 如果游戏需要特定版本的 D3DX,或者未安装的其他更新功能,则游戏将不起作用,并且用户将感到非常沮丧。
强烈建议游戏的安装程序以无提示方式安装游戏生成的 DirectX 可再发行组件。 DirectX 安装过程经过设计,以便验证是否需要更新任何内容,并在不更新时快速返回。 因此,无需询问用户安装 DirectX。
可以通过从安装包运行此命令来完成 DirectX 的无提示安装:dxsetup.exe /silent
此外,可以将可再发行组件文件夹的实际大小配置为仅包含游戏目标平台和使用情况实际需要的内阁文件(.cab)。
注意
在使用 dxsetup之前,请阅读 不是直接安装程序。
线程同步过度使用
分析游戏时,通常会发现顶级热点与进入和离开关键部分相关。 随着多核 CPU 的普及,游戏中多线程的使用急剧增加,许多实现依赖于大量使用线程同步。 即使没有任何争用,占用关键部分的 CPU 时间也相当重要,而且所有其他形式的线程同步都更加昂贵。 因此,必须注意尽量减少使用这些基元。
游戏中过度同步的常见来源是使用 D3DCREATE_MULTITHREADED。 此标志虽然使 Direct3D 线程安全用于从多个线程进行呈现,但采用非常保守的方法,从而导致高同步开销。 游戏应避免此标志。 相反,构建引擎,以便与 Direct3D 的所有通信都来自单个线程,并且直接处理线程之间的任何通信。 有关设计多线程游戏的详细信息,请参阅文章 Xbox 360 上的多个核心编码和 Microsoft Windows。
使用 RDTSC
不建议使用 x86 指令 RDTSC。 RDTSC 无法在一些动态更改 CPU 频率的电源管理方案中正确计算计时,并且无法在内核之间同步循环计数器的多个多核 CPU 上正确计算计时。 游戏应改用 QueryPerformanceCounter API。 有关 RDTSC 和 QueryPerformanceCounter实现高分辨率计时问题的详细信息,请参阅 游戏计时和多核处理器一文。