SSE store



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


  • Mod

    float scale = (delta / max) / (float)sqrt(sq);
    			ccx = x * scale;
    

    delta is ja max-min - max ist selbst einer der farbwerte... alle mit .0039 skaliert, (also fällt es in delta/max heraus). sqrt(sq) enthält den faktor ebenfalls, genauso wie x - also fällt es dort ebenfalls heraus.

    wenn man schon floats hat ist eine max/min berechnung sicher günstiger als ifs (das hab ich im sse teil ja auch gemacht) - für integer ist das nicht so (man kann die sprünge vermeiden, wenn man movcc benutzt, aber der vergleich ist trotzdem notwendig) - in jedem falle werden integer wesentlich schneller (und zum teil parallel) verarbeitet, so dass ich ziemlich sicher bin, dass es so günstiger ist. man sollte immer bedenken, dass sse befehle an sich recht langsam sind, nur durch grossen datendurchsatz erreicht man einen geschwindigkeitsvorteil (und in manchen fällen ist man schneller als mit der fpu, die ja auch nicht schnell ist).



  • Habs jetzt auch kapiert..:-)))

    Und ich muss sagen, ich bin mehr als begeistert. Vielen Dank!!

    Falls Dir noch mehr gutes zu schnellem Code einfällt, auch Literatur, her damit; ich will ja nicht Dumm sterben....

    Respekt.
    Gruß Michael


  • Mod

    da fällt mir auf:

    ccx = (float)( r + r - g - b );
        ccy = (float)( g - b ) * sqrtf( 3.0f );
    

    muss nat. sein:

    ccx = (float)( (int)r + (int)r - (int)g - (int)b );
        ccy = (float)( (int)g - (int)b ) * sqrtf( 3.0f );
    

    es kann ja sonst zu unangenehmen überläufen kommen (im if teil ist das ja ausgeschlossen).


Anmelden zum Antworten