Otimização de código com a biblioteca DirectXMath
Este tópico descreve considerações e estratégias de otimização com a Biblioteca DirectXMath.
- Use acessadores com moderação
- Use as configurações de compilação corretas
- Use as funções Est quando apropriado
- Usar tipos de dados alinhados e operações
- alinhar corretamente as alocações
- Evite sobrecargas do operador quando possível
- Denormals
- Aproveite a dualidade de ponto flutuante inteiro
- Prefira formulários de modelo
- Usando o DirectXMath com o Direct3D
- Tópicos relacionados
Use os acessadores com moderação
As operações baseadas em vetores usam os conjuntos de instruções SIMD e estes fazem uso de registros especiais. O acesso a componentes individuais requer a mudança dos registos SIMD para os registos escalares e vice-versa.
Quando possível, é mais eficiente inicializar todos os componentes de um XMVECTOR de uma só vez, em vez de usar uma série de acessadores vetoriais individuais.
Use as configurações de compilação corretas
Para destinos x86 do Windows, habilite /arch:SSE2. Para todos os destinos do Windows, habilite /fp:fast.
Por padrão, a compilação na Biblioteca DirectXMath para destinos x86 do Windows é feita com _XM_SSE_INTRINSICS_ definidos. Isso significa que todas as funcionalidades do DirectXMath usarão instruções SSE2. No entanto, o mesmo não acontece com outros códigos.
O código fora do DirectXMath é tratado usando padrões do compilador. Sem essa opção, o código gerado pode muitas vezes usar o código x87 menos eficiente.
É altamente recomendável que você sempre use a versão mais recente disponível do compilador.
Use as funções Est quando apropriado
Muitas funções têm uma função de estimativa equivalente terminando em Est. Estas funções trocam alguma precisão para melhorar o desempenho. As funções Est são apropriadas para cálculos não críticos, onde a precisão pode ser sacrificada pela velocidade. A quantidade exata de precisão perdida e o aumento de velocidade dependem da plataforma.
Por exemplo, a funçãoXMVector3AngleBetweenNormalsEst pode ser usada no lugar da funçãoXMVector3AngleBetweenNormals.
Usar tipos de dados e operações alinhados
Os conjuntos de instruções SIMD em versões do Windows que suportam SSE2 normalmente têm versões alinhadas e desalinhadas de operações de memória. A utilização das operações alinhadas é mais rápida e deve ser preferida sempre que possível.
A Biblioteca DirectXMath fornece funcionalidade alinhada e não alinhada de acesso por meio de tipos de vetores variantes, estrutura e funções. Estas variantes são indicadas por um "A" no final do nome.
Por exemplo, há uma estrutura XMFLOAT4X4 não alinhada e uma estrutura XMFLOAT4X4A alinhada, que são usadas pelasXMStoreFloat4 e funções XMStoreFloat4A, respectivamente.
Alinhar corretamente as alocações
As versões alinhadas do SSE intrínsecos subjacentes à biblioteca DirectXMath são mais rápidas do que as não alinhadas.
Por esse motivo, as operações DirectXMath usando XMVECTOR e objetos XMMATRIX assumem que esses objetos estão alinhados a 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 corretas de compilação). No entanto, é importante garantir que a alocação de heap contendo XMVECTOR e objetos XMMATRIX, ou moldes para esses tipos, atenda a esses requisitos de alinhamento.
Enquanto as alocações de memória do Windows de 64 bits são alinhadas a 16 bytes, por padrão, nas versões de 32 bits da memória do Windows alocada é de apenas 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 de blog do Visual C++ Team para obter um exemplo de como escrever um alocador personalizado (em vez de malloc/free, 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 rastreamento interno que podem ou não respeitar o alinhamento do tipo de usuário fornecido, resultando em membros de dados desalinhados. Nesse caso, você precisa usar tipos não alinhados em vez de tipos alinhados. Se você derivar de classes existentes, incluindo muitos objetos do Tempo de Execução do Windows, também poderá modificar o alinhamento de uma classe ou estrutura.
Evite sobrecargas do 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. Tais sobrecargas do operador tendem a criar inúmeros objetos temporários. Recomendamos que você evite essas sobrecargas de operador em código sensível ao desempenho.
Denormais
Para suportar cálculos próximos de 0, o padrão de ponto flutuante IEEE 754 inclui suporte para subfluxo gradual. O fluxo inferior gradual é implementado através do uso de valores desnormalizados, e muitas implementações de hardware são lentas ao lidar com denormais. Uma otimização a considerar é desabilitar a manipulação de denormais para as operações vetoriais usadas pelo DirectXMath.
A alteração do tratamento de denormais é feita usando a rotina de _controlfp_s em uma base pré-thread e pode resultar em melhorias de desempenho. Use este código para alterar a manipulação de denormais:
#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, instruções de SSE são usadas para todos os cálculos, não apenas para as operações vetoriais. Alterar a manipulação desnormal afeta todos os cálculos de ponto flutuante em seu programa, não apenas as operações vetoriais usadas pelo DirectXMath.
Aproveite a dualidade de ponto flutuante inteiro
O DirectXMath suporta vetores de 4 pontos flutuantes 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 como vários tipos diferentes - por exemplo, tratar o mesmo vetor como dados de ponto flutuante e inteiros - certas otimizações podem ser alcançadas. 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 usado 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, não estão de acordo com a descrição anterior). Os exemplos incluem:
- Zero positivo é 0
- Zero negativo é 0x80000000
- Q_NAN é 07FC0000
- +INF é 0x7F800000
- -INF é 0xFF800000
Prefira formulários de modelo
O formulário de modelo existe para XMVectorSwizzle, XMVectorPermute, XMVectorInsert, XMVectorShiftLeft, XMVectorRotateLefte XMVectorRotateRight. Usando estes em vez da forma de função geral permite que o compilador crie implementações muito mais eficientes. Para SSE , isso geralmente cai para um ou dois valores _mm_shuffle_ps. Para ARM-NEON, o modelo de XMVectorSwizzle pode utilizar vários casos especiais em vez do swizzle/permute VTBL mais geral.
Usando o DirectXMath com o Direct3D
Um uso comum para o DirectXMath é executar cálculos gráficos para uso com o Direct3D. Com o Direct3D 10.x e o Direct3D 11.x, você pode usar a biblioteca DirectXMath das seguintes maneiras diretas:
Use o namespace Colors constantes diretamente no parâmetro ColorRGBA em uma chamada para o ID3D11DeviceContext::ClearRenderTargetView ou método ID3D10Device::ClearRenderTargetView. Para Direct3D 9, você deve converter para o tipo de XMCOLOR para usá-lo como o parâmetro Color em uma chamada para o IDirect3DDevice9::Clear método.
Use os tipos XMFLOAT4/ XMVECTOR e XMFLOAT4X4/ XMMATRIX para configurar estruturas de buffer constantes para referência porde matriz HLSLfloat4 ou tipos de matriz/float4x4.
Observação
XMFLOAT4X4/tipos de XMMATRIX estão no formato de linha principal. Portanto, se você usar a opção de compilador /Zpr (o sinalizador de compilação D3DCOMPILE_PACK_MATRIX_COLUMN_MAJOR) ou omitir a palavra-chave row_major quando declarar o tipo de matriz em HLSL, deverá transpor a matriz ao defini-la para o buffer constante.
Com o Direct3D 10.x e o Direct3D 11.x, você pode assumir que o ponteiro retornado pelo método Map (por exemplo, ID3D11DeviceContext::Map) no membro do pData (D3D10_MAPPED_TEXTURE2D.pData, D3D10_MAPPED_TEXTURE3D.pDataou D3D11_MAPPED_SUBRESOURCE.pData) é alinhado a 16 bytes se você usar nível de recurso 10_0 ou superior ou sempre que utilizar D3D11_USAGE_STAGING recursos.