Otimização de código com a biblioteca DirectXMath
Este tópico descreve as considerações e estratégias de otimização com a Biblioteca DirectXMath.
- Usar acessadores com moderação
- Usar as configurações de compilação corretas
- Usar funções Est quando apropriado
- Usar operações e tipos de dados alinhados
- Alinhar corretamente as alocações
- Evitar sobrecargas de operador quando possível
- Desnormalizado
- Aproveitar a dualidade do ponto flutuante inteiro
- Preferir Formulários de Modelo
- Usando DirectXMath com Direct3D
- Tópicos relacionados
Usar acessadores com moderação
As operações baseadas em vetor usam os conjuntos de instruções SIMD e usam registros especiais. Acessar componentes individuais requer a mudança dos registros SIMD para os escalares e voltar novamente.
Quando possível, é mais eficiente inicializar todos os componentes de um XMVECTOR ao mesmo tempo, em vez de usar uma série de acessadores de vetor individuais.
Usar as configurações de compilação corretas
Para destinos do Windows x86, habilite /arch:SSE2. Para todos os destinos do Windows, habilite /fp:fast.
Por padrão, a compilação em relação aos destinos da Biblioteca DirectXMath para Janela x86 é feita com _XM_SSE_INTRINSICS_ definidos. Isso significa que todas as funcionalidades do DirectXMath usarão as instruções SSE2. No entanto, o mesmo não é verdadeiro para outro código.
O código fora do DirectXMath é tratado usando padrões do compilador. Sem essa opção, o código gerado geralmente pode usar o código x87 menos eficiente.
É altamente recomendável que você sempre use a versão mais recente disponível do compilador.
Usar funções Est quando apropriado
Muitas funções têm uma função de estimativa equivalente que termina em Est. Essas funções trocam alguma precisão para melhorar o desempenho. As funções Est são apropriadas para cálculos não críticos em que a precisão pode ser sacrificada por velocidade. A quantidade exata de precisão perdida e aumento de velocidade dependem da plataforma.
Por exemplo, a função XMVector3AngleBetweenNormalsEst pode ser usada no lugar da função XMVector3AngleBetweenNormals .
Usar operações e tipos de dados alinhados
Os conjuntos de instruções SIMD em versões do Windows que dão suporte ao SSE2 normalmente têm versões alinhadas e não alinhadas de operações de memória. O uso das operações alinhadas é mais rápido e deve ser preferencial sempre que possível.
A Biblioteca DirectXMath fornece funcionalidade alinhada e desalinhada de acesso por meio de tipos de vetor, estrutura e funções variantes. Essas variantes são indicadas por um "A" no final do nome.
Por exemplo, há uma estrutura XMFLOAT4X4 não assinada e uma estrutura XMFLOAT4X4A alinhada, que são usadas pelas funções XMStoreFloat4 e XMStoreFloat4A , respectivamente.
Alinhar corretamente as alocações
As versões alinhadas dos intrínsecos SSE subjacentes à Biblioteca DirectXMath são mais rápidas do que as não assinadas.
Por esse motivo, as operações DirectXMath que usam objetos XMVECTOR e XMMATRIX pressupõem que esses objetos estejam alinhados em 16 bytes. Isso é automático para alocações baseadas em pilha, se o código for compilado na Biblioteca DirectXMath usando as configurações recomendadas do compilador do Windows (consulte Usar configurações de compilação corretas). No entanto, é importante garantir que a alocação de heap que contém objetos XMVECTOR e XMMATRIX ou conversões para esses tipos atenda a esses requisitos de alinhamento.
Embora as alocações de memória do Windows de 64 bits estejam alinhadas a 16 bytes, por padrão, as versões de 32 bits da memória do Windows alocadas são alinhadas apenas a 8 bytes. Para obter informações sobre como controlar o alinhamento da memória, consulte _aligned_malloc.
Ao usar tipos DirectXMath alinhados com a STL (Biblioteca de Modelos Padrão), você precisará fornecer um alocador personalizado que garanta o alinhamento de 16 bytes. Consulte o blog da Equipe do Visual C++ para obter um exemplo de como escrever um alocador personalizado (em vez de malloc/gratuito, você desejará usar _aligned_malloc e _aligned_free em sua implementação).
Observação
Alguns modelos STL modificam o alinhamento do tipo fornecido. Por exemplo, make_shared<> adiciona algumas informações de acompanhamento internas que podem ou não respeitar o alinhamento do tipo de usuário fornecido, resultando em membros de dados não alinhados. Nesse caso, você precisa usar tipos não alinhados em vez de tipos alinhados. Se você derivar de classes existentes, incluindo muitos objetos Windows Runtime, também poderá modificar o alinhamento de uma classe ou estrutura.
Evitar sobrecargas de operador quando possível
Como um recurso de conveniência, vários tipos, como XMVECTOR e XMMATRIX , têm sobrecargas de operador para operações aritméticas comuns. Essas sobrecargas de operador tendem a criar vários objetos temporários. Recomendamos que você evite essas sobrecargas de operador no código sensível ao desempenho.
Desnormalizado
Para dar suporte a cálculos próximos a 0, o padrão de ponto float do IEEE 754 inclui suporte para fluxo de subfluxo gradual. O fluxo de subfluxo gradual é implementado por meio do uso de valores desnormalizados e muitas implementações de hardware são lentas ao lidar com desnormal. Uma otimização a ser considerada é desabilitar o tratamento de desnormals para as operações de vetor usadas pelo DirectXMath.
A alteração da manipulação de desnormal é feita usando a rotina _controlfp_s em uma base pré-thread e pode resultar em melhorias de desempenho. Use este código para alterar o tratamento de desnormals:
#include <float.h>;
unsigned int control_word;
_controlfp_s( &control_word, _DN_FLUSH, _MCW_DN );
Observação
Em versões de 64 bits do Windows, as instruções SSE são usadas para todos os cálculos, não apenas para as operações de vetor. Alterar o tratamento desnormal afeta todos os cálculos de ponto flutuante em seu programa, não apenas as operações de vetor usadas pelo DirectXMath.
Aproveitar a dualidade do ponto flutuante inteiro
O DirectXMath dá suporte a vetores de quatro valores de ponto flutuante de precisão única ou quatro valores de 32 bits (assinados ou não assinados).
Como os conjuntos de instruções usados para implementar a Biblioteca DirectXMath têm a capacidade de tratar os mesmos dados que vários tipos diferentes, por exemplo, trate o mesmo vetor que as otimizações específicas de dados de ponto flutuante e inteiro podem ser obtidas. Você pode obter essas otimizações usando as rotinas de inicialização de vetor inteiro e operadores bit a bit para manipular valores de ponto flutuante.
O formato binário de números de ponto flutuante de precisão única usados pela Biblioteca DirectXMath está completamente em conformidade com o padrão IEEE 754:
SIGN EXPONENT MANTISSA
X XXXXXXXX XXXXXXXXXXXXXXXXXXXXXXX
1 bit 8 bits 23 bits
Ao trabalhar com o número de ponto flutuante de precisão única IEEE 754, é importante ter em mente que algumas representações têm um significado especial (ou seja, elas não estão em conformidade com a descrição anterior). Os exemplos incluem:
- Zero positivo é 0
- Zero negativo é 0x80000000
- Q_NAN é 07FC0000
- +INF está 0x7F800000
- -INF está 0xFF800000
Preferir Formulários de Modelo
O formulário de modelo existe para XMVectorSwizzle, XMVectorPermute, XMVectorInsert, XMVectorShiftLeft, XMVectorRotateLeft e XMVectorRotateRight. Usá-los em vez do formulário de função geral permite que o compilador crie implementações muito mais efígies. Para a SSE, isso geralmente recolhe um ou dois valores _mm_shuffle_ps. Para ARM-NEON, o modelo XMVectorSwizzle pode utilizar vários casos especiais em vez do swizzle/permute VTBL mais geral.
Usando DirectXMath com Direct3D
Um uso comum para DirectXMath é executar cálculos gráficos para uso com Direct3D. Com o Direct3D 10.x e o Direct3D 11.x, você pode usar a biblioteca DirectXMath das seguintes maneiras diretas:
Use as constantes de namespace Colors diretamente no parâmetro ColorRGBA em uma chamada para o método ID3D11DeviceContext::ClearRenderTargetView ou ID3D10Device::ClearRenderTargetView . Para Direct3D 9, você deve converter para o tipo XMCOLOR para usá-lo como o parâmetro Color em uma chamada para o método IDirect3DDevice9::Clear .
Use os tiposXMVECTOR XMFLOAT4/ e XMFLOAT4X4/XMMATRIX para configurar estruturas de buffer constantes para referência por tipos HLSL float4 ou matrix/float4x4.
Observação
XMFLOAT4X4/Os tipos XMMATRIX estão no formato principal de linha. Portanto, se você usar a opção do compilador /Zpr (o sinalizador de compilação D3DCOMPILE_PACK_MATRIX_COLUMN_MAJOR) ou omitir o palavra-chave row_major ao declarar o tipo de matriz em HLSL, transporá-la ao defini-la no buffer constante.
Com Direct3D 10.x e Direct3D 11.x, você pode supor que o ponteiro retornado pelo método Map (por exemplo, ID3D11DeviceContext::Map) no membro pData (D3D10_MAPPED_TEXTURE2D.pData, D3D10_MAPPED_TEXTURE3D. pData ou D3D11_MAPPED_SUBRESOURCE. pData) será alinhado com 16 bytes se você usar o nível de recurso 10_0 ou superior ou sempre que usar D3D11_USAGE_STAGING recursos.