Share via


Optimización de código con la biblioteca DirectXMath

En este tema se describen las consideraciones y estrategias de optimización con la biblioteca DirectXMath.

Uso de descriptores de acceso con moderación

Las operaciones basadas en vectores usan los conjuntos de instrucciones SIMD y usan registros especiales. El acceso a componentes individuales requiere pasar de los registros SIMD a los escalares y volver a hacerlo de nuevo.

Cuando sea posible, es más eficaz inicializar todos los componentes de un XMVECTOR a la vez, en lugar de usar una serie de descriptores de acceso vectoriales individuales.

Uso de la configuración de compilación correcta

Para destinos de Windows x86, habilite /arch:SSE2. Para todos los destinos de Windows, habilite /fp:fast.

De forma predeterminada, la compilación con la biblioteca DirectXMath para destinos de Windows x86 se realiza con _XM_SSE_INTRINSICS_ definido. Esto significa que todas las funciones de DirectXMath usarán instrucciones de SSE2. Sin embargo, lo mismo no es cierto para otro código.

El código fuera de DirectXMath se controla mediante los valores predeterminados del compilador. Sin este modificador, el código generado a menudo puede usar el código x87 menos eficaz.

Se recomienda encarecidamente usar siempre la versión más reciente disponible del compilador.

Uso de funciones Est cuando corresponda

Muchas funciones tienen una función de estimación equivalente que termina en Est. Estas funciones intercambian cierta precisión para mejorar el rendimiento. Las funciones est son adecuadas para cálculos no críticos donde se puede sacrificar la precisión para la velocidad. La cantidad exacta de precisión perdida y el aumento de velocidad dependen de la plataforma.

Por ejemplo, la función XMVector3AngleBetweenNormalsEst podría usarse en lugar de la función XMVector3AngleBetweenNormals .

Usar tipos de datos alineados y operaciones

Los conjuntos de instrucciones SIMD en versiones de windows compatibles con SSE2 normalmente tienen versiones alineadas y no alineadas de las operaciones de memoria. El uso de las operaciones alineadas es más rápido y debe preferirse siempre que sea posible.

La biblioteca DirectXMath proporciona funcionalidad alineada y no alineada a través de tipos de vectores variantes, estructura y funciones. Estas variantes se indican mediante una "A" al final del nombre.

Por ejemplo, hay una estructura XMFLOAT4X4 no alineada y una estructura XMFLOAT4X4A alineada, que las funciones XMStoreFloat4 y XMStoreFloat4A usan respectivamente.

Alinear correctamente las asignaciones

Las versiones alineadas de los intrínsecos de SSE subyacentes a la biblioteca directXMath son más rápidas que las no alineadas.

Por este motivo, las operaciones de DirectXMath que usan objetos XMVECTOR y XMMATRIX asumen que esos objetos están alineados de 16 bytes. Esto es automático para las asignaciones basadas en la pila, si el código se compila en la biblioteca directXMath mediante la configuración recomendada del compilador de Windows (consulte Usar la configuración correcta de compilación). Sin embargo, es importante asegurarse de que la asignación de montón que contenga objetos XMVECTOR y XMMATRIX , o que las conversiones a estos tipos cumplan estos requisitos de alineación.

Aunque las asignaciones de memoria de Windows de 64 bits están alineadas de 16 bytes, de forma predeterminada en las versiones de 32 bits de memoria de Windows asignadas solo está alineada con 8 bytes. Para obtener información sobre cómo controlar la alineación de la memoria, consulte _aligned_malloc.

Al usar tipos DirectXMath alineados con la biblioteca de plantillas estándar (STL), deberá proporcionar un asignador personalizado que garantice la alineación de 16 bytes. Consulte el blog del equipo de Visual C++ para obtener un ejemplo de escritura de un asignador personalizado (en lugar de malloc/free, querrá usar _aligned_malloc y _aligned_free en la implementación).

Nota:

Algunas plantillas de STL modifican la alineación del tipo proporcionado. Por ejemplo, make_shared<> agrega información de seguimiento interna que puede o no respetar la alineación del tipo de usuario proporcionado, lo que da lugar a miembros de datos no asignados. En este caso, debe usar tipos no alineados en lugar de tipos alineados. Si deriva de clases existentes, incluidos muchos objetos Windows Runtime, también puede modificar la alineación de una clase o estructura.

 

Evitar sobrecargas de operador cuando sea posible

Como característica de conveniencia, varios tipos como XMVECTOR y XMMATRIX tienen sobrecargas de operador para operaciones aritméticas comunes. Estas sobrecargas de operador tienden a crear numerosos objetos temporales. Se recomienda evitar estas sobrecargas de operador en código sensible al rendimiento.

Desnormalizados

Para admitir cálculos cercanos a 0, el estándar de punto flotante IEEE 754 incluye compatibilidad con subflujo gradual. El subflujo gradual se implementa mediante el uso de valores desnormalizados y muchas implementaciones de hardware son lentas al controlar los desnormales. Una optimización que se debe tener en cuenta es deshabilitar el control de los desnormales para las operaciones vectoriales usadas por DirectXMath.

El cambio del control de los desnormales se realiza mediante el uso de la rutina _controlfp_s de forma previa al subproceso y puede dar lugar a mejoras en el rendimiento. Use este código para cambiar el control de los desnormales:

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

Nota:

En las versiones de 64 bits de Windows, las instrucciones de SSE se usan para todos los cálculos, no solo para las operaciones vectoriales. Cambiar el control desnormal afecta a todos los cálculos de punto flotante del programa, no solo a las operaciones vectoriales usadas por DirectXMath.

 

Aprovechar la dualidad de punto flotante entero

DirectXMath admite vectores de 4 valores de punto flotante de precisión sencilla o de cuatro valores de 32 bits (con signo o sin signo).

Dado que los conjuntos de instrucciones usados para implementar la biblioteca DirectXMath tienen la capacidad de tratar los mismos datos que varios tipos diferentes; por ejemplo, se puede tratar el mismo vector que las optimizaciones de datos enteros y de punto flotante. Puede obtener estas optimizaciones mediante las rutinas de inicialización de vectores enteros y los operadores bit a bit para manipular valores de punto flotante.

El formato binario de números de punto flotante de precisión sencilla utilizados por la biblioteca DirectXMath se ajusta completamente al estándar IEEE 754:

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

Al trabajar con el número de punto flotante de precisión sencilla IEEE 754, es importante tener en cuenta que algunas representaciones tienen un significado especial (es decir, no se ajustan a la descripción anterior). Algunos ejemplos son:

  • Cero positivo es 0
  • Cero negativo es 0x80000000
  • Q_NAN es 07FC0000
  • +INF es 0x7F800000
  • -INF es 0xFF800000

Preferir formularios de plantilla

El formulario de plantilla existe para XMVectorSwizzle, XMVectorPermute, XMVectorInsert, XMVectorShiftLeft, XMVectorRotateLeft y XMVectorRotateRight. El uso de estos elementos en lugar del formulario de función general permite al compilador crear implementaciones mucho más eficientes. En el caso de SSE, esto a menudo se contrae hasta uno o dos valores _mm_shuffle_ps. Para ARM-NEON, la plantilla XMVectorSwizzle puede usar una serie de casos especiales en lugar de los más generales Swizzle/permute de VTBL.

Uso de DirectXMath con Direct3D

Un uso común de DirectXMath es realizar cálculos gráficos para su uso con Direct3D. Con Direct3D 10.x y Direct3D 11.x, puedes usar la biblioteca DirectXMath de estas maneras directas:

Guía de programación de DirectXMath