SSE: Zugriff auf UNION / Shuffle sehr langsam



  • Hallo,

    was SSE angeht, bin ich ein blutiger Anfänger. Mir geht es darum, die folgende Iteration zu beschleunigen, wobei a[] Parameter und "o" und "n" Vektoren sind:

    for (i=0;i<100000000;i++) {
    n.x=a[1]+o.x*(a[2]+a[3]*o.x+a[4]*o.y+a[5]*o.z)+o.y*(a[6]+a[7]*o.y+a[8]*o.z)+o.z*(a[9]+a[10]*o.z);
    n.y=a[11]+o.x*(a[12]+a[13]*o.x+a[14]*o.y+a[15]*o.z)+o.y*(a[16]+a[17]*o.y+a[18]*o.z)+o.z*(a[19]+a[20]*o.z);
    n.z=a[21]+o.x*(a[22]+a[23]*o.x+a[24]*o.y+a[25]*o.z)+o.y*(a[26]+a[27]*o.y+a[28]*o.z)+o.z*(a[29]+a[30]*o.z);
    o = n;
    }
    

    Das lässt sich ja wunderbar vektorisieren und sieht dann so aus:

    // Variablendeklaration
    
    union {
        __m128 m128[10];
        float  f[4][10];
    } a;
    
    union {
        __m128 m128;
        float  f[4];
    } n;
    
    for (i=0;i<10;i++) {
        a.m128[i] = _mm_set_ps( 0.0, a[i+21], a[i+11], a[i+1] );  
    }
    
    __m128 block1;
    __m128 block2;
    __m128 block3;
    __m128 tmp1;
    __m128 tmp2;
    __m128 tmp3;
    __m128 tmp4;
    __m128 ox;
    __m128 oy;
    __m128 oz; 
    
    // Eigentliche Iteration
    
    for (i=0;i<100000000;i++) {
        tmp1 = _mm_mul_ps(a.m128[9], oz);
        tmp2 = _mm_add_ps(tmp1, a.m128[8]);
    
        block1 = _mm_mul_ps(tmp2, oz);
        tmp1 = _mm_mul_ps(a.m128[7], oz);
        tmp2 = _mm_mul_ps(a.m128[6], oy);      
        tmp3 = _mm_add_ps(tmp1, tmp2);
        tmp1 = _mm_add_ps(tmp3, a.m128[5]);
        block2 = _mm_mul_ps(tmp1, oy); 
    
        tmp1 = _mm_mul_ps(a.m128[4], oz);   
        tmp2 = _mm_mul_ps(a.m128[3], oy);  
        tmp3 = _mm_mul_ps(a.m128[2], ox);  
        tmp4 = _mm_add_ps(tmp1, tmp2);  
        tmp1 = _mm_add_ps(tmp3, tmp4); 
        tmp2 = _mm_add_ps(tmp1, a.m128[1]);
        block3 = _mm_mul_ps(tmp2, ox);
    
        tmp1 = _mm_add_ps(block1, block2);
        tmp2 = _mm_add_ps(tmp1, block3);
        n.m128 = _mm_add_ps(tmp2, a.m128[0]);
    
        // Hier der Problembereich: die Übertragung des Ergebnisses 
        // in die nächste Iterationsrunde. Der x-Wert von "n" muss in alle 4
        // Register von ox geschrieben werden.
    
        ox = _mm_shuffle_ps( n.m128, n.m128, _MM_SHUFFLE(0,0,0,0));
        oy = _mm_shuffle_ps( n.m128, n.m128, _MM_SHUFFLE(1,1,1,1));
        oz = _mm_shuffle_ps( n.m128, n.m128, _MM_SHUFFLE(2,2,2,2));
    }
    

    Für 100 Millionen Iterationen braucht die

    -> herkömmliche Variante : 1.907s
    -> SSE/Vektorisierte Variante: 1.621s

    Das ist zwar ein klein bisschen schneller, aber haut mich nicht wirklich vom Hocker. Stutzig geworden bin ich als ich in beiden Varianten einmal die Wertzuweisung am Ende weggelassen habe, also:

    // o = n; (bei der herkömmlichen Variante)
    

    und

    // ox = _mm_shuffle_ps( n.m128, n.m128, _MM_SHUFFLE(0,0,0,0));
    // oy = _mm_shuffle_ps( n.m128, n.m128, _MM_SHUFFLE(1,1,1,1));
    // oz = _mm_shuffle_ps( n.m128, n.m128, _MM_SHUFFLE(2,2,2,2));
    

    bei der SSE-Variante. Dann sieht das Ergebnis GANZ ANDERS AUS:

    -> herkömmliche Variante : 1.582s
    -> SSE/Vektorisierte Variante: 0.508s

    Dass die Iterationszeit abnimmt, war ja klar, aber dass SSE so viel schneller würde, hätte ich nicht gedacht. Im Umkehrschluss bedeutet dies, dass _mm_shuffle_ps wohl ziemlich langsam ist.

    Mit _mm_set_ps1 geht es leider noch langsamer:

    ox = _mm_set_ps1( n.f[0]); 
    oy = _mm_set_ps1( n.f[1]); 
    oz = _mm_set_ps1( n.f[2]);
    

    Dann ist SSE langsamer als die herkömmliche Version.

    Gibt es da noch einen Trick, den ich übersehen habe?

    Danke für eure Hilfe!

    Jens



  • Dieser Thread wurde von Moderator/in HumeSikkins aus dem Forum C++ in das Forum Rund um die Programmierung verschoben.

    Im Zweifelsfall bitte auch folgende Hinweise beachten:
    C/C++ Forum :: FAQ - Sonstiges :: Wohin mit meiner Frage?

    Dieses Posting wurde automatisch erzeugt.



  • Hierzu kann man Verschiedenes sagen, allerdings würde ich für die Diskussion vollständigen (d.h. per c&p unmittelbar compilierbaren) Code bevorzugen. Zudem wäre die Kenntnis des verwendeten Compilers und des Prozessors (um die Zeitangabe bewerten zu können) hilfreich.

    Möglicherweise ist dieses Thema im Assemblerforum am besten aufgehoben.


Anmelden zum Antworten