SSE store


  • Mod

    nt



  • Was heißt 'nt'?


  • Mod

    nt = no text

    in diesem forum kann man ja nicht löschen...

    kannst du den algorithmus mal in normalem c++ ohne intrinsics zeigen? einiges hier sieht ein bisschen suspekt aus. einen so dramatischen geschwindigkeits unterschied kann ich im übrigen hier nicht feststellen - welchen compiler/welche cpu hast du?



  • Kein Problem, ich werde mal was vorbereiten, das Du testen kannst....

    Aber zunächst möchte ich mein Problem eingrenzen:
    Am Ende der Berechnung in der Schleife wird das Ergebnis einer Multiplikation in der Variablen xs gespeichert.
    Wenn ich diese Variable mit store in ein float[4] Array(aligned) speichern will dauert das eine Ewigkeit; speichere ich jedoch z.b. xs=_mm_setzero_ps() bleibt die hohe Geschwindigkeit erhalten.

    Vieleicht kann jemand damit was anfangen(aus nem Profiler). Erst kommt die Intrinsic Methode, dann der Assemblercode, den der Compiler daraus gemacht hat.(leider kann ich selbst nicht wirklich Assembler)

    xs=_mm_mul_ps(xd,xa);  
           mulps    xmm0, xmm2
      _mm_store_ps(inp_sse1,xs);
          movaps   XMMWORD PTR [eax], xmm0
          jnz      Extract+1a0	
          mov      edi, DWORD PTR [esp+02ch]
          mov      ecx, DWORD PTR [ebp+0ch]
          add      DWORD PTR [esp+01ch], ecx
          sub      DWORD PTR [esp+020h], 01h
          jnz      Extract+160
    

    Ich benutze den msvc++7.1 Compiler. Getestet ist das auf einem Pentium M, einem AthlonXP und einem P4HT.
    Falls sich jemand über die undurchsichtigen Variablennamen wundert:
    nach meinen Tests ist SSE nur dann schnell, wenn ich auf Variablen arbeite, die auf dem Stack der jeweiligen Methode liegen. Bei allem, was was sonst irgendwo auf dem heap liegt, ist load/store viel zu langsam..
    Und ich will nur wenige Variablen, da diese bei jedem Methodenaufruf angelegt werden müssen...

    Gruß
    Michael



  • hmm...täusch ich mich, oder sind das stinknormale assembler befehle?
    sollten da nicht irgendwelche SSE teile kommen?

    //edit ahso, is schon ok



  • [..]

    msvc++ 7.1 Compiler; release;

    kompiliert mit:
    /O2 /Ot /G7 /I "D:\test" /D "WIN32" /D "NDEBUG" /D "_MBCS" /FD /EHsc /MT /GS /arch:SSE /Fo"Release/" /Fd"Release/vc70.pdb" /W3 /nologo /c /Zi /TP

    //EDIT Hat sich erledigt! Es sieht so aus, dass der Compiler die SSE Befehle durch die eingeschalteten Optimierungsfunktionen eliminiert, weil nicht mehr auf das Ergebniss zugegriffen wird!
    Ist ohne Warnung des Compilers aber eine besonders schlechte Optimierungsstrategie. Ob der gcc auch auch einem Mist macht???

    Gruß
    Michael



  • hab keinen intel, und sse läuft doch nur auf intel, oder? 😕



  • //EDIT s.u.



  • ne das läuft auch auf amd athlon xp cpu's.


  • Mod

    lokale xmm variable, werden, wenn möglich, direkt auf register gemappt und nicht auf dem stack angelegt (das geht immer dann, wenn die funktion selbst keine andren funktionen (ausser intrinsics) aufruft und man nicht auf die komponenten der xmm variable zugreifen muss). daher der geschwindigkeits vorteil.

    ich bin aber fast sicher, dass diese funktion nicht das macht, was sie soll (daher die bitte um normalen c++ quelltext) - ich habs hier mal hinzugefügt:

    inp_sse1[0]=data[y];			// red
       inp_sse1[1]=data[y+1];		// green
       inp_sse1[2]=data[y+2];		// blue
       inp_sse1[3]=0;				// 0
       xa = _mm_load_ps(inp_sse1);	// 0, blue, green, red
       xb = _mm_load_ps(inp_sse2);	// .39, .39, .39, .39
       xc = _mm_mul_ps(xa, xb);		// 0, blue*.39, green*.39, red*.39  - *.39 im folgenden weggelassen
    
       xa=_mm_shuffle_ps(xc,xc,1);	// red, red, red, green
       xb=_mm_shuffle_ps(xc,xc,2);	// red, red, red, blue
       xa=_mm_max_ps(_mm_max_ps(xc , xa),xb );	// red, max(red,blue), max(red,green), max(red,green,blue)
       xb=_mm_min_ps(_mm_min_ps(xc , xa),xb );	// 0, red, red, min(red,blue)
       xs=_mm_sub_ss(xa,xb);		// red, max(red,blue), max(red,green), max(red,green,blue)-min(red,blue)
       xs=_mm_div_ss(xs,xa);		// red, max(red,blue), max(red,green), 1-min(red,blue)/max(red,green,blue)
    
       xd=_mm_div_ps(xb,xa);		// 0, red/max(red,blue), red/max(red,green), min(red,blue)/max(red,green,blue),
       xa=_mm_movelh_ps(xs,xs);		// max(red,green), 1-min(red,blue)/max(red,green,blue),
    								//	max(red,green), 1-min(red,blue)/max(red,green,blue)
       xs=_mm_mul_ps(xd,xa);
    
       _mm_store_ps(out_sse,xs);  //!!
    


  • 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.



  • Dieser Thread wurde von Moderator/in kingruedi aus dem Forum Rund um die Programmierung in das Forum Assembler verschoben.

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

    Dieses Posting wurde automatisch erzeugt.



  • Zunächst mal, danke für die Mühe die Du Dir mit meinem Problem gemacht hast.

    Es scheint mit aber so zu sein, das die Funktionen garnicht aufgerufen werden, wenn ich das Store nicht an das Ende der Berechnung stelle.(sieht im Profiler so aus) Im grunde ist das auch richtig, denn wenn ich das Ergebniss nicht weiter verwende kann der Compiler die berechnung auch weglassen....
    Wenn ich da falsch liege, bitte ich um Berichtigung, wäre wichtig.

    Desweiteren war die Methode nur ein Ausschnitt der vollständigen Version;
    aber darin war mein Problem ebenfalls vorhanden..

    Ich hab zum testen mal was hier hingelegt:
    http://www.uni-koblenz.de/~mmayer/testSSE.cpp
    (der V Kanal im HSV-Farbraum wird mit Absicht weggelassen)

    Danke
    Gruß
    Michael



  • hmm.. mal ne dumme frage..

    funktioniert das ausrichten des outputarrays eigentlich? bei mir wird der befehl zum ausrichten einfach ignoriert.. wenn das nicht funktioniert könnte ein ... sagen wir mal ein bruchteil deiner 600% leistungsverlust darauf zurück geführt werden. 🙂 (wenn die bib so schlau ist unausgerichtete speicheradressen mit movups anzusprechen).

    ausserdem hatte ich diese leistungsschwankungen auch bei meinen "optimierten" programmen. meistens war es auf unnötige konstruktoraufrufe zurückzuführen. womöglich auch auf den statuswechsel, von normaler fpu -> sse. das soll angeblich (aus intel/amd seite so zu hören) keine zeit mehr kosten.
    im zweifelsfall schreib das doch ganz zu nem assembler block um 🙂
    dann kannst du wenigstens kontrollieren was genau da passiert.. (und es mit dem vom compiler erzeugten code vergleichen compileroption -S).

    gruss

    eviluser



  • Eigentlich wollte ich dem Compiler ja so viel arbeit wie möglich überlassen; deshalb intrinsics, da kann der Compiler z.B. die Register selbst auswählen.

    Ich habe auch irgendwie das Gefühl, dass die Ausrichtung eher nach dem Zufallsprinzip stattfindet. 🙂 Und eigentlich habe ich alles probiert;
    _aligned_malloc, __declspec(align ; heap oder stack. 100% hats nie funktioniert.

    Vielleicht klappts ja noch..aber 2 Monate wollte ich nicht daran sitzen.. :-))))

    Gruß
    Michael



  • hehe.. schau mal nach meinen threads 🙂 mit dem problem hab ich mich auch schon beschäftigt..

    die bisher sicherste lösung war die von camper:

    float *p = (float*) malloc(grösse*sizeof(float)+15), *p_aligned;
    p_aligned = (float*) (((unsigned)p+15)&-16);
    //tu was mit p_aligned
    free(p);
    

    gruss

    eviluser



  • Klar, ich habe Deine Threads gelesen; aber ich dachte eigentlich es geht z.B. mit _aligned_malloc.... dafür gibts das ja.

    Ich werds aber auch mal so probieren...SSE muss doch irgend etwas bringen... 🙄

    Gruß
    Michael


  • Mod

    wie gesagt, die funktion macht definitiv nicht, was sie soll - die minimumberechnung klappt schon mal nicht, weil xa ja schon durch die maximumrechnung verändert wurde. allerdings würde ich vorschlagen, zunächst dan normalen c++ quelltext zu optimieren, der ist nähmlich grauenhaft 🙂 - davon ausgehend kann man dann versuchen noch etwas mittels sse herauszuholen.

    zunächst fällt auf, dass die skalierung mit .0039f völlig wertlos ist, sie fällt am ende der rechnung nähmlich wieder heraus.

    auch die konstanten für cos/sin sind etwas witzlos(und im sse teil falsch):

    cos(0)=1 sin(0)=0 klar
    cos(2pi/3)=-1/2
    cos(4pi/3)=-1/2
    sin(2pi/3)=sqrt(3)/2
    sin(4pi/3)=-sqrt(3)/2
    damit wird
    x = R*rx + G*gx + B*bx = R - 1/2 * ( G + B )
    y = R*ry + G*gy + B*by = ( G - B )*sqrt(3)/2

    oder besser:
    x = R + R - ( G + B )
    y = ( G - B ) * sqrt(3)
    (der faktor 2 fällt ja durch das skalieren wieder heraus)

    die maximum/minmumberechnung kann man auch aufschieben, bis x*x+y*y bekannt ist, im falle von 0 spart man so zeit

    inline void RGB2CCdb(const unsigned char r, const unsigned char g, const unsigned char b, float &ccx, float &ccy)
    {
        ccx = (float)( r + r - g - b );
        ccy = (float)( g - b ) * sqrtf( 3.0f );
        float sq;
        if ( ( sq = ccx * ccx + ccy * ccy ) > 0 )
        {
            float scale;
            if ( r >= g )
            {
                if ( r >= b )
                {
                    if ( g >= b )
                    {
                        scale = (float)( r - b ) / ( (float)r * sqrtf( sq ) );
                    }
                    else
                    {
                        scale = (float)( r - g ) / ( (float)r * sqrtf( sq ) );
                    }
                }
                else
                {
                    scale = (float)( b - g ) / ( (float)b * sqrtf( sq ) );
                }
            }
            else
            {
                if ( g >= b )
                {
                    if ( r >= b )
                    {
                        scale = (float)( g - b ) / ( (float)g * sqrtf( sq ) );
                    }
                    else
                    {
                        scale = (float)( g - r ) / ( (float)g * sqrtf( sq ) );
                    }
                }
                else
                {
                    scale = (float)( b - r ) / ( (float)b * sqrtf( sq ) );
                }
            }
            ccx *= scale;
            ccy *= scale;
        }
    }
    

    das wäre dann mal wieder ein fall wo der 2. vor dem ersten schritt der getan wurde 🙂

    zu einer division durch null kann es nie kommen, denn wenn max = 0 folgt sq = 0.


  • Mod

    wenn es jetzt unbedingt sse sein muss, könnte man es z.b. so machen (von vektoroperationen ist ja nichts übriggeblieben 😉 ):

    inline void RGB2CCdb(const unsigned char r, const unsigned char g, const unsigned char b, float &ccx, float &ccy)
    {
        const float sqrt3 = sqrtf( 3.0f );
    	const float null = 0.0f;
        __asm
        {
            movzx    eax, r
            cvtsi2ss xmm0, eax
            add      eax, eax
            movzx    ecx, g
            cvtsi2ss xmm1, ecx
            sub      eax, ecx
            movzx    edx, b
            cvtsi2ss xmm2, edx
            sub      ecx, edx
            sub      eax, edx
            cvtsi2ss xmm4, ecx    // y
            mov      ecx, ccx
            cvtsi2ss xmm3, eax    // x
            mov      edx, ccy
            mulss    xmm4, sqrt3
            movss    xmm6, xmm3
            movss    xmm7, xmm4
            mulss    xmm3, xmm3
            mulss    xmm4, xmm4
            addss    xmm3, xmm4
            comiss   xmm3, null
            je       get_out
            sqrtss   xmm3, xmm3
            movss    xmm5, xmm0
            maxss    xmm0, xmm1
            minss    xmm5, xmm1
            maxss    xmm0, xmm2
            minss    xmm5, xmm2
            mulss    xmm3, xmm0
            subss    xmm0, xmm5    // max - min
            divss    xmm0, xmm3
            mulss    xmm6, xmm0
            mulss    xmm7, xmm0
    get_out:movss    [ ecx ], xmm6
            movss    [ edx ], xmm7
        }
    }
    


  • Hmm,

    der Code ist eine Anpassung des orginal RGB2HSV Codes.(Ich glaube der orginale ist von Smith selbst). Stammt aus der Lehre und hat deshalb diese Struktur...

    Ich dachte 2 Skalarprodukte und die min/max Berechnung ist schneller/sse kompatibler als die vielen if's....

    Die Konstanten sind nur für die Lesbarkeit; sind ja nur Konstanten und sollten nur einmal berechnet werden. Aus diesen Werten wird eine ONB für einen Unterraum des R^3 (rgb) erzeugt; die Werte in der SSE Berechnung sind nur eine andere ONB.

    Mir ist nicht ganz klar, warum die normierung der RGB Werte von [0..255] auf den Bereich [0..1] mit der multiplikation von 0.0039 rausfällt(wink mal etwas mehr mit dem Zaunfahl... 😉 )

    Schonmal
    Danke danke danke....

    Michael


Anmelden zum Antworten