Problem mit Inline Assembler + SSE



  • Hab zwar vom Assembler-teil nicht so viel Ahnung. Aber std::vector hat auf jeden Fall ein Speicherlayout wie ein gewöhnliches Array. Bekommen kann man die Startadresse mit &gaga[0].

    MfG Jester


  • Mod

    @Jester: danke, bedeutet das aber, dass &gaga dasselbe ist wie &gaga[0] ? - und ist gaga[0] überhaupt ein gültiger bezug, solange noch keine elemente eingefügt wurden?

    @***: es gibt verschiedene möglichkeiten, das zu heilen. falls es sich hier generell nur um 3- oder 4-dimensionale vectoren handelt, wäre es günstiger, auf std:vector zu verzichten und statt dessen xmm intrinsics zu verwenden, das erlaubt dem compiler effizienter mit resourcen umzugehen. falls naked_cross nur in dieser einen funktion benötigt wird, sollte der assembler text direkt in die methode eingefügt werden, dann ist man den funktionsoverhead schon mal los (wenn man mittels inline assembler auf argumente zugreift, wird immer ein stackrahmen eingerichtet, daher nützt inline im zusammenhang mit dem inline assembler nicht viel - bei gcc ist das nat. ganz anders).

    etwa so - ungetestet:

    const vector<float> vector<float>::crossProduct(const vector<T> &other)
    {
        vector<float> gaga;
        float* this0 = (float*)&(*this)[0];
        float* other0 = (float*)&other[0];
        float* gaga0 = (float*)&gaga[0];
        __asm
        {
             mov     ecx, this0
             movups  xmm0, [ ecx ]
             mov     edx, other0
             movaps  xmm2, xmm0
             shufps  xmm0, xmm0, 0xC9  // 11001001
             movups  xmm1, [ edx ]
             shufps  xmm1, xmm1, 0xD2  // 11010010
    
             mulps   xmm0, xmm1
             shufps  xmm2, xmm2, 0xD2  // 11010010
             mov     eax, gaga0
             shufps  xmm1, xmm1, 0xD2  // 11010010  (11001001)
    
             mulps   xmm2, xmm1
    
             subps   xmm0, xmm2
    
             movups  [ eax ], xmm0
        }
        return gaga;
    }
    

    effizienter sind hier intrinsics - allerdings ist dann das gesamte design zu überarbeiten, z.b. so:

    #include <xmmintrin.h>
    inline __m128 crossProduct(const __m128 &left, const __m128 &right)
    {
        return _mm_sub_ps( _mm_mul_ps(
    		_mm_shuffle_ps( left, left, _MM_SHUFFLE( 3, 0, 2, 1 ) ),
    		_mm_shuffle_ps( right, right, _MM_SHUFFLE( 3, 1, 0, 2 ) )
    	), _mm_mul_ps(
    		_mm_shuffle_ps( left, left, _MM_SHUFFLE( 3, 1, 0, 2 ) ),
    		_mm_shuffle_ps( right, right, _MM_SHUFFLE( 3, 0, 2, 1 ) ) ) );
    }
    


  • Hi,

    danke der 1. code bringt meinen PC zum absturz, der 2 ebenso 😞

    Was sind denn intrinsics? ist das ne neue form des assemblers?



  • normalerweise verwenden compiler nur die einfachsten befehle der cpu. bessere compiler dann schon mehr, aber SSE und solche spezialsachen dann doch nicht (ja, es gibt ausnahmen :-)). die intrinsics geben dem programmierer die möglichkeit, solche spezialbefehle der cpu doch direkt ohne inline asm zu verwenden.

    björn


  • Mod

    welche art fehler bekommst du bei der zweiten variante? und wie rufst du es auf?

    der grosse vorteil von intrinsics besteht darin, dass der compiler ihre seiteneffekte kennt und auch die registerverwendung selbst bestimmen kann. bei echtem inline assembler ist dass nicht der fall, was verhindert, dass der compiler irgendwelche optimierungen über einen assemblerblock hinweg vornimmt (das geht schon damit los, dass keine variablen in registern gehalten werden können). gcc ist in dieser hinsicht wesentlich mächtiger durch constraints.



  • Hi,

    ich habs mal getestet, funzt prima, weis nicht was *** damit für probleme hat?

    wie benutzt man eigentlich diese Funktion??

    #include <xmmintrin.h> 
    inline __m128 crossProduct(const __m128 &left, const __m128 &right) 
    { 
        return _mm_sub_ps( _mm_mul_ps( 
            _mm_shuffle_ps( left, left, _MM_SHUFFLE( 3, 0, 2, 1 ) ), 
            _mm_shuffle_ps( right, right, _MM_SHUFFLE( 3, 1, 0, 2 ) ) 
        ), _mm_mul_ps( 
            _mm_shuffle_ps( left, left, _MM_SHUFFLE( 3, 1, 0, 2 ) ), 
            _mm_shuffle_ps( right, right, _MM_SHUFFLE( 3, 0, 2, 1 ) ) ) ); 
    }
    

    Ich kann doch keinen vector in einen _m128 casten?



  • achja noch was: gibt es sonst noch irgendwo diese instricts funktionen? z.B. für matrizenmultiplikation oder vector*matrix?


  • Mod

    Ich kann doch keinen vector in einen _m128 casten?

    das würde höchstwahrscheinlich auch zu einer zugriffsverletzung führen
    _m128 ist prinzipiell als ausgerichtetes float[4] array zu verstehen (zugriff über xyz.f32[...]) - für die exacte deklaration schau ins headerfile. aus performance gründen sollte man den direkten zugriff aber vermeiden (wenn er zeitgleich mit intrinsics erfolgen würde), da dieser nur über den speicher möglich ist, es gibt auch für diesen zweck geeignete intrinsics.

    achja noch was: gibt es sonst noch irgendwo diese instricts funktionen? z.B. für matrizenmultiplikation oder vector*matrix?

    siehe MSDN - prinzipiell sind intrinsics nur wrapper für assemblerinstructionen, komplexere operationen musst du daraus selbst aufbauen; assemblerkenntnisse sind nützlich, da nicht alle kombinationen zu optimalen code führen - das ist aber nicht zwingend erforderlich. das gegenstück bei gcc sind die x86 built-in functions - da verweise ich einfach auch mal auf die entsprechende dokumentation



  • wie benutze ich denn die funktion wenn ich 2 Arrays á 4 floats habe?


  • Mod

    die funktion geht davon aus, dass sich die x,y,z koordinaten jeweils in [0] [1] bzw [2] befinden, das ergebnis hat das selbe format, der 4te wert wird hier nicht benötigt - im ergebnis ist er immer 0 (wenn es nicht gerade zum überlauf kommt, bzw. er vorher schon NaN war).


Anmelden zum Antworten