Multiplicação de matrizes, ao estilo SSE3

Eis uma última demonstração do poder de SSE, dessa vez usarei SSE3 para fazer multiplicação de matrizes. As nossas matrizes serão 4×4 para aproveitar os 4 floats contidos num registrador XMM e, portanto, serão definidas de acordo com o seguinte typedef:

typedef union
{
  float m[4][4];
  __m128 x[4];
} matrix4_t;

Eis as duas funções, construídas para o uso com OpenGL, de multiplicação de matrizes (MatrixMult e MatrixMult_SSE3). Lembro que as matrizes são transpostas no OpenGL, ou seja, os componentes x,y,z e w são orientados por colunas, não por linhas, como acontece com o Direct3D:

#include <x86intrin.h>

void MatrixMult(matrix4_t *result,
                matrix4_t m1, matrix4_t m2)
{
  int row, col, k;

  for (col = 0; col < 4; col++)
    for (row = 0; row < 4; row++)
    {
      result->m[col][row] = 0.0f;

      for (k = 0; k < 4; k++)
        result->m[col][row] += m1.m[k][row] * m2.m[col][k];
    }
}

void MatrixMult_SSE3(matrix4_t *result,
                     matrix4_t m1, matrix4_t m2)
{
  matrix4_t temp;
  __m128 t;
  int row, col;

  /* Copia matriz para podermos transpô-la */
  temp.x[0] = m1.x[0];
  temp.x[1] = m1.x[1];
  temp.x[2] = m1.x[2];
  temp.x[3] = m1.x[3];

  /* Transpõe a primeira matriz porque estmos lidando
     com matrizes no estilo OpenGL aqui
     (ou seja, já transpostas! */
  _MM_TRANSPOSE4_PS(temp.x[0],
                    temp.x[1],
                    temp.x[2],
                    temp.x[3]);

  for (col = 0; col < 4; col++)
    for (row = 0; row < 4; row++)
    {
      t = _mm_mul_ps(temp.x[col], m2.x[row]);
      t = _mm_hadd_ps(t, t);
      t = _mm_hadd_ps(t, t);
      _mm_store_ss(&result->m[col][row], t);
    }
}

A primeira função faz o trabalho com 128 operações (64 multiplicações e 64 adições). A segunda função faz a mesma coisa com 48 (16 multiplicações e 32 adições). São 80 operações a menos.

Pelas minhas medições, usando rdtsc(), a primeira rotina gasta pouco mais de 2000 ciclos. A segunda, 1300. Um ganho de 54% na performance.

A segunda rotina só não é mais rápida graças ao uso do macro _MM_TRANSPOSE4_PS (definido em xmmintrin.h), faz muitas movimentações nos registradores XMM.

Lembro que, se você for usar as rotinas, tenha certeza de compilar usando as opções -msse3 e -mfpmath=sse. A segunda opção não é obrigatória, mas tende a gerar código mais performático (já que não usa a pilha do co-processador matemático).

Anúncios

Deixe um comentário

Faça o login usando um destes métodos para comentar:

Logotipo do WordPress.com

Você está comentando utilizando sua conta WordPress.com. Sair / Alterar )

Imagem do Twitter

Você está comentando utilizando sua conta Twitter. Sair / Alterar )

Foto do Facebook

Você está comentando utilizando sua conta Facebook. Sair / Alterar )

Foto do Google+

Você está comentando utilizando sua conta Google+. Sair / Alterar )

Conectando a %s