DirectXMath 库的代码优化

本主题介绍 DirectXMath 库的优化注意事项和策略。

谨慎使用访问器

基于矢量的作使用 SIMD 指令集,这些作使用特殊寄存器。 访问各个组件需要从 SIMD 寄存器移动到标量寄存器,然后再次移动。

如果可能,最好一次初始化 XMVECTOR 的所有组件,而不是使用一系列单独的向量访问器。

使用正确的编译设置

对于 Windows x86 目标,请启用 /arch:SSE2。 对于所有 Windows 目标,请启用 /fp:fast。

默认情况下,针对 Windows x86 目标的 DirectXMath 库进行编译,_XM_SSE_INTRINSICS_定义。 这意味着所有 DirectXMath 功能都将使用 SSE2 指令。 但是,其他代码也是如此。

DirectXMath 外部的代码使用编译器默认值进行处理。 如果没有此开关,生成的代码通常使用效率较低的 x87 代码。

强烈建议始终使用最新版本的编译器。

适当时使用 Est 函数

许多函数具有以 Est 结尾的等效估计函数。 这些函数会权衡一些准确性以提高性能。 Est 函数适用于非关键计算,可以在其中为速度牺牲准确性。 确切的丢失准确性和速度增加量取决于平台。

例如,可以使用 XMVector3AngleBetweenNormalsEst 函数代替 XMVector3AngleBetweenNormals 函数。

使用对齐数据类型和作

支持 SSE2 的 Windows 版本的 SIMD 指令集通常已对齐且未对齐的内存作版本。 对齐作的使用速度更快,应尽可能首选。

DirectXMath 库通过变体向量类型、结构和函数提供经过对齐和无对齐的功能。 这些变体由名称末尾的“A”表示。

例如,有一个未对齐的 XMFLOAT4X4 结构和一个对齐的 XMFLOAT4X4A 结构,它们分别由 XMStoreFloat4XMStoreFloat4A 函数使用。

正确对齐分配

DirectXMath 库基础 SSE 内部函数的对齐版本比未对齐的要快。

因此,使用 XMVECTORXMMATRIX 对象的 DirectXMath作假定这些对象是 16 字节对齐的。 如果使用建议的 Windows 针对 DirectXMath 库编译代码(请参阅 使用正确的编译设置)编译器设置,则这是基于堆栈的分配的自动。 但是,请务必确保包含 XMVECTOR 的堆分配XMMATRIX 对象,或强制转换为这些类型,以满足这些对齐要求。

虽然 64 位 Windows 内存分配是 16 字节对齐的,但默认情况下,分配的 32 位版本的 Windows 内存仅对齐 8 字节。 有关控制内存对齐的信息,请参阅 _aligned_malloc

将 DirectXMath 类型与标准模板库(STL)一起使用时,需要提供一个自定义分配器,以确保 16 字节对齐。 请参阅 Visual C++ Team 博客,获取编写自定义分配器的示例(而不是 malloc/free,你希望在实现中使用_aligned_malloc和_aligned_free)。

注意

某些 STL 模板修改所提供的类型的对齐方式。 例如,make_shared<> 添加一些内部跟踪信息,这些信息可能不符合提供的用户类型的对齐方式,从而导致未对齐的数据成员。 在这种情况下,需要使用无对齐类型而不是对齐类型。 如果派生自现有类(包括许多 Windows 运行时对象),则还可以修改类或结构的对齐方式。

 

尽可能避免运算符重载

作为一项便利功能,许多类型(例如 XMVECTORXMMATRIX 具有常见算术运算的运算符重载。 此类运算符重载往往创建许多临时对象。 建议避免在性能敏感代码中重载这些运算符。

非规范

为了支持接近 0 的计算,IEEE 754 浮点标准包括对逐步下溢的支持。 使用非规范化值实现逐渐下溢,处理非规范性时,许多硬件实现速度较慢。 要考虑的优化是禁用 DirectXMath 使用的向量作的反规范处理。

更改非规范的处理是通过在线程前使用 _controlfp_s 例程来完成的,并可能导致性能改进。 使用此代码可更改非规范的处理:

  #include <float.h>;
    unsigned int control_word;
    _controlfp_s( &control_word, _DN_FLUSH, _MCW_DN );

注意

在 64 位版本的 Windows 上,SSE 指令用于所有计算,而不仅仅是向量作。 更改非规范处理会影响程序中的所有浮点计算,而不仅仅是 DirectXMath 使用的向量运算。

 

利用整数浮点双精度

DirectXMath 支持 4 个单精度浮点或 4 个 32 位(带符号或无符号)值的向量。

由于用于实现 DirectXMath 库的指令集能够将相同的数据视为多个不同类型的,因此可以将同一向量视为浮点和整数数据特定的优化。 可以使用整数向量初始化例程和按位运算符作浮点值来获取这些优化。

DirectXMath 库使用的单精度浮点数的二进制格式完全符合 IEEE 754 标准:

     SIGN    EXPONENT   MANTISSA
     X       XXXXXXXX   XXXXXXXXXXXXXXXXXXXXXXX
     1 bit   8 bits     23 bits

使用 IEEE 754 单精度浮点数时,请务必记住,某些表示形式具有特殊含义(即它们不符合上述说明)。 示例包括:

  • 正零为 0
  • 负零为0x80000000
  • Q_NAN为 07FC0000
  • +INF 0x7F800000
  • -INF 0xFF800000

首选模板表单

存在 XMVectorSwizzleXMVectorPermuteXMVectorInsertXMVectorShiftLeftXMVectorRotateLeftXMVectorRotateRight。 使用这些函数形式而不是常规函数形式,编译器可以创建更多的实现。 对于 SSE,这通常折叠为一两个_mm_shuffle_ps值。 对于 ARM-NEON,XMVectorSwizzle 模板可以利用许多特殊情况,而不是更常规的 VTBL 重排/排列。

将 DirectXMath 与 Direct3D 配合使用

DirectXMath 的常见用途是执行图形计算,以便与 Direct3D 一起使用。 借助 Direct3D 10.x 和 Direct3D 11.x,可以通过以下直接方式使用 DirectXMath 库:

DirectXMath 编程指南