2013-08-30 4 views
8

Поразмыслив, я придумал следующий код для умножения двух кватернионов с помощью SSE:Как умножить два кватерниона с минимальными инструкциями?

#include <pmmintrin.h> /* SSE3 intrinsics */ 

/* multiplication of two quaternions (x, y, z, w) x (a, b, c, d) */ 

__m128 _mm_cross4_ps(__m128 xyzw, __m128 abcd) 
{ 
    /* The product of two quaternions is:         */ 
    /* (X,Y,Z,W) = (xd+yc-zb+wa, -xc+yd+za+wb, xb-ya+zd+wc, -xa-yb-zc+wd) */ 

    __m128 wzyx = _mm_shuffle_ps(xyzw, xyzw, _MM_SHUFFLE(0,1,2,3)); 
    __m128 baba = _mm_shuffle_ps(abcd, abcd, _MM_SHUFFLE(0,1,0,1)); 
    __m128 dcdc = _mm_shuffle_ps(abcd, abcd, _MM_SHUFFLE(2,3,2,3)); 

    /* variable names below are for parts of componens of result (X,Y,Z,W) */ 
    /* nX stands for -X and similarly for the other components    */ 

    /* znxwy = (xb - ya, zb - wa, wd - zc, yd - xc) */ 
    __m128 ZnXWY = _mm_hsub_ps(_mm_mul_ps(xyzw, baba), _mm_mul_ps(wzyx, dcdc)); 

    /* xzynw = (xd + yc, zd + wc, wb + za, yb + xa) */ 
    __m128 XZYnW = _mm_hadd_ps(_mm_mul_ps(xyzw, dcdc), _mm_mul_ps(wzyx, baba)); 

    /* _mm_shuffle_ps(XZYnW, ZnXWY, _MM_SHUFFLE(3,2,1,0)) */ 
    /*  = (xd + yc, zd + wc, wd - zc, yd - xc)  */ 
    /* _mm_shuffle_ps(ZnXWY, XZYnW, _MM_SHUFFLE(2,3,0,1)) */ 
    /*  = (zb - wa, xb - ya, yb + xa, wb + za)  */ 

    /* _mm_addsub_ps adds elements 1 and 3 and subtracts elements 0 and 2, so we get: */ 
    /* _mm_addsub_ps(*, *) = (xd+yc-zb+wa, xb-ya+zd+wc, wd-zc+yb+xa, yd-xc+wb+za)  */ 

    __m128 XZWY = _mm_addsub_ps(_mm_shuffle_ps(XZYnW, ZnXWY, _MM_SHUFFLE(3,2,1,0)), 
           _mm_shuffle_ps(ZnXWY, XZYnW, _MM_SHUFFLE(2,3,0,1))); 

    /* now we only need to shuffle the components in place and return the result  */ 
    return _mm_shuffle_ps(XZWY, XZWY, _MM_SHUFFLE(2,1,3,0)); 

    /* operations: 6 shuffles, 4 multiplications, 3 compound additions/subtractions */ 
} 

Я ожидал сборки, чтобы иметь минимальное количество инструкций. Однако, когда я скомпилировать его сборку с НКУ -msse3 -S, результирующая функция имеет 67 инструкцию:

.text 
    .globl __Z13_mm_cross4_psU8__vectorfS_ 
__Z13_mm_cross4_psU8__vectorfS_: 
LFB594: 
    pushq %rbp 
LCFI0: 
    movq %rsp, %rbp 
LCFI1: 
    subq $232, %rsp 
    movaps %xmm0, -336(%rbp) 
    movaps %xmm1, -352(%rbp) 
    movaps -336(%rbp), %xmm0 
    movaps -336(%rbp), %xmm1 
    shufps $27, %xmm1, %xmm0 
    movaps %xmm0, -16(%rbp) 
    movaps -352(%rbp), %xmm0 
    movaps -352(%rbp), %xmm1 
    shufps $17, %xmm1, %xmm0 
    movaps %xmm0, -32(%rbp) 
    movaps -352(%rbp), %xmm0 
    movaps -352(%rbp), %xmm1 
    shufps $187, %xmm1, %xmm0 
    movaps %xmm0, -48(%rbp) 
    movaps -16(%rbp), %xmm0 
    movaps %xmm0, -112(%rbp) 
    movaps -48(%rbp), %xmm0 
    movaps %xmm0, -128(%rbp) 
    movaps -128(%rbp), %xmm0 
    movaps -112(%rbp), %xmm1 
    mulps %xmm1, %xmm0 
    movaps -336(%rbp), %xmm1 
    movaps %xmm1, -144(%rbp) 
    movaps -32(%rbp), %xmm1 
    movaps %xmm1, -160(%rbp) 
    movaps -160(%rbp), %xmm1 
    movaps -144(%rbp), %xmm2 
    mulps %xmm2, %xmm1 
    movaps %xmm1, -176(%rbp) 
    movaps %xmm0, -192(%rbp) 
    movaps -176(%rbp), %xmm0 
    hsubps -192(%rbp), %xmm0 
    movaps %xmm0, -64(%rbp) 
    movaps -16(%rbp), %xmm0 
    movaps %xmm0, -208(%rbp) 
    movaps -32(%rbp), %xmm0 
    movaps %xmm0, -224(%rbp) 
    movaps -224(%rbp), %xmm0 
    movaps -208(%rbp), %xmm1 
    mulps %xmm1, %xmm0 
    movaps -336(%rbp), %xmm1 
    movaps %xmm1, -240(%rbp) 
    movaps -48(%rbp), %xmm1 
    movaps %xmm1, -256(%rbp) 
    movaps -256(%rbp), %xmm1 
    movaps -240(%rbp), %xmm2 
    mulps %xmm2, %xmm1 
    movaps %xmm1, -272(%rbp) 
    movaps %xmm0, -288(%rbp) 
    movaps -272(%rbp), %xmm0 
    haddps -288(%rbp), %xmm0 
    movaps %xmm0, -80(%rbp) 
    movaps -64(%rbp), %xmm0 
    movaps -80(%rbp), %xmm1 
    shufps $177, %xmm1, %xmm0 
    movaps -80(%rbp), %xmm1 
    movaps -64(%rbp), %xmm2 
    shufps $228, %xmm2, %xmm1 
    movaps %xmm1, -304(%rbp) 
    movaps %xmm0, -320(%rbp) 
    movaps -304(%rbp), %xmm0 
    addsubps  -320(%rbp), %xmm0 
    movaps %xmm0, -96(%rbp) 
    movaps -96(%rbp), %xmm0 
    movaps -96(%rbp), %xmm1 
    shufps $156, %xmm1, %xmm0 
    leave 
LCFI2: 
    ret 

Что я делаю неправильно? Там должен быть лучший способ перемешать элементы вокруг, не используя столько инструкций movaps.

+0

Чтобы получить большинство из SIMD вы должны выполнять несколько операций параллельно, а не одну операцию. Для SSE это означает, что вы должны сделать четыре умножения на кватернионы (умножить восемь кватернионов) сразу. Тогда вам не нужно использовать каких-либо горизонтальных операторов, и вы должны получить 4x скорость кода, отличного от SIMD. –

+0

Старайтесь избегать горизонтальных операций - я думаю, что такое же количество инструкций возможно при организации вычисления в виде вертикальной последовательности 'xyzw. * Dddd + yzwx. * CacA + zwxy. * BbbB - wxyz. * Acac' и (' A = -a, B = -b'). –

+0

Я понимаю, что вы должны сказать, но кватернион уже состоит из четырех поплавков, и, как правило, в моем коде у меня нет цикла с множеством умножений, но одно умножение за раз, поэтому сразу несколько кватернионов Помогите. – Guilherme

ответ

3

Ничего. Если я скомпилировать код с НКУ -msse3 -O1 -S вместо этого, я получаю следующее:

.text 
    .align 4,0x90 
    .globl __Z13_mm_cross4_psU8__vectorfS_ 
__Z13_mm_cross4_psU8__vectorfS_: 
LFB644: 
    movaps %xmm0, %xmm5 
    movaps %xmm1, %xmm3 
    movaps %xmm0, %xmm2 
    shufps $27, %xmm0, %xmm5 
    movaps %xmm5, %xmm4 
    shufps $17, %xmm1, %xmm3 
    shufps $187, %xmm1, %xmm1 
    mulps %xmm3, %xmm2 
    mulps %xmm1, %xmm4 
    mulps %xmm5, %xmm3 
    mulps %xmm1, %xmm0 
    hsubps %xmm4, %xmm2 
    haddps %xmm3, %xmm0 
    movaps %xmm2, %xmm1 
    shufps $177, %xmm0, %xmm1 
    shufps $228, %xmm2, %xmm0 
    addsubps  %xmm1, %xmm0 
    shufps $156, %xmm0, %xmm0 
    ret 

Вот только 18 инструкции, в настоящее время. Это то, чего я ожидал в начале. К сожалению.

+3

Nrgh! Никогда не измеряйте производительность без необходимости оптимизации! –

2

Возможно, вас заинтересует Agner Fog's C++ vector class library. Он предоставляет классы и Quaternion4d (включая, конечно, операторы * и *=), реализованные с использованием наборов команд SSE2 и AVX соответственно. Библиотека представляет собой проект с открытым исходным кодом, поэтому вы можете вникнуть в код и найти хороший пример реализации для создания вашей функции.

Позже, вы можете обратиться к "optimizing subroutines in assembly language" manual и обеспечивают оптимальное, чистое выполнение сборочного функции или, в то же время известно о некоторых трюков низкого уровня, попробуйте перестроить Intrinsics подход в С.

+0

Сейчас я веду подобное исследование по матричному умножению. Удачи! –

+0

Спасибо за указатели. Ранее я видел этот документ «Оптимизация подпрограмм в ассемблере», но потом я его потерял. – Guilherme

Смежные вопросы