2013-08-30 11 views
8

Dopo qualche pensiero, mi si avvicinò con il seguente codice per moltiplicare due quaternioni utilizzando SSE:Come moltiplicare due quaternioni con istruzioni minime?

#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 */ 
} 

ho aspettato l'assemblea di avere una quantità minima di istruzioni. Tuttavia, quando compilo al montaggio con gcc -msse3 -S, la funzione risultante ha 67 istruzioni:

.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 

Che cosa sto facendo di sbagliato? Ci deve essere un modo migliore per mescolare gli elementi senza usare così tante istruzioni movaps.

+0

Per ottenere il la maggior parte di SIMD è necessario eseguire più operazioni in parallelo, non una sola operazione. Per SSE questo significa che dovresti fare quattro moltiplicazioni quaternion (moltiplicare otto quaternioni) contemporaneamente. Quindi non devi utilizzare alcun operatore orizzontale e dovresti ottenere la velocità 4x del codice non SIMD. –

+0

Cerca di evitare operazioni orizzontali - Penso che lo stesso numero di istruzioni sia possibile quando si organizza il calcolo come sequenza verticale di 'xyzw. * Dddd + yzwx. * CacA + zwxy. * BbbB - wxyz. * Acac' e (' A = -a, B = -b'). –

+0

Capisco cosa hai da dire, ma un quaternion è già composto da quattro float, e di solito nel mio codice non ho un ciclo con molte moltiplicazioni, ma una singola moltiplicazione alla volta, quindi fare diversi quaternioni in una volta non fa aiuto. – Guilherme

risposta

3

Non importa. Se compilo il codice con gcc -msse3 -O1 -S invece, ricevo il seguente:

.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 

Questo è solo 18 istruzioni adesso. Questo è quello che mi aspettavo all'inizio. Ops.

+3

Nrgh! Non misurare mai le prestazioni senza abilitare le ottimizzazioni! –

2

Potreste essere interessati a Agner Fog's C++ vector class library. Fornisce una classe Quaternion4f e Quaternion4d (compresi gli operatori * e *=, ovviamente), implementata utilizzando rispettivamente set di istruzioni SSE2 e AVX. La libreria è un progetto Open Source, quindi puoi scavare nel codice e trovare un buon esempio di implementazione per costruire la tua funzione.

In seguito, si può consultare il "optimizing subroutines in assembly language" manual e fornire un'implementazione ottimizzata di assemblaggio, pura della funzione o, pur essendo a conoscenza di alcuni trucchi di basso livello, prova a ridisegnare l'approccio intrinseche in C.

+0

Sto conducendo una ricerca simile sulla moltiplicazione della matrice ora. In bocca al lupo! –

+0

Grazie per i suggerimenti. Avevo già visto quel documento di "ottimizzazione delle subroutine in linguaggio assembly", ma poi l'ho perso. – Guilherme

Problemi correlati