16Bit Zahlen ohne Wrap-around addieren bzw. subtrahieren
-
Bitte benutze die Code-Tags.
Das kannst du so machen, wenn dein Compiler die Daten korrekt ausrichtet. Du brauchst ein Aligment von höchstens 8 Bytes, damit das funktioniert. Das ist AFAIK standardmäßig schon so und sollte deshalb funktionieren.
Theoretisch brauchst du das Dummyelement nicht, wenn z. B. Höhe und Breite konstant sind und die Gesamtgröße "zufällig" sowieso schon ein Vielfaches von 4 ist. Wenn z.B. Width=640, Height=480, dann hast du 640*480*3 Words, die sich prima ohne Rest durch 4 teilen lassen. Dadurch sparst du 307200 unnütze Additionen.
Aber auch wenn das nicht so ist, könnte man unnütze Rechnerei verhindern, wenn man die Daten in 2 Brocken teilt; ein Brockenlen=(Width*Height*sizeof(BGR))>>2
erfüllt die Anforderungen und kann wie beschrieben abgearbeitet werden, die übrigen paar Werte
len=(Width*Height*sizeof(BGR))&3
können "von Hand" addiert werden. Das wäre dann immer noch ziemlich effizient.
Ich habe es allerdings nicht getestet und diesen Low-Level Kram schon länger nicht mehr gemacht. Unter Umständen liege ich falsch. Probier es doch einfach mal aus.
-
Hi,
ich hab sicher gestellt, daß die Länge durch 4 teilbar ist. Aber leider stürtzt das Programm ständig ab.045B17F5 emms 045B17F7 mov ecx,dword ptr [ebp+10h] 045B17FA mov esi,dword ptr [ebp+0Ch] 045B17FD mov edi,dword ptr [ebp+8] 045B1800 shr ecx,2 045B1803 movq mm0,mmword ptr [edi] <--------- Fehler hier 045B1806 movq mm1,mmword ptr [esi] 045B1809 paddusw mm0,mm1 045B180C movq mmword ptr [edi],mm0 045B180F add edi,8 045B1812 add esi,8 045B1815 dec ecx 045B1816 jne 045B1803
Programm:
typedef struct ssiBGR{ unsigned short b; unsigned short g; unsigned short r; } siBGR; typedef siBGR *piBGR; piBGR stack,dark,ibuf; void addSaturated(unsigned short *dest, const unsigned short* src, unsigned long len) { //assert((len&3)==0); // muss durch 4 teilbar sein __asm { emms // mmx-register vorbereiten mov ecx, len mov esi, src mov edi, dest shr ecx, 2 // durch 4 teilen, wir bearbeiten qwords l0: movq mm0, [edi] // dest[ecx*4] in mmx-register 0 movq mm1, [esi] // src[ecx*4] in mmx-register 1 paddusw mm0, mm1 // packed add of unsigned saturated words, summe in mm0 movq [edi], mm0 // summe in dest schreiben add edi, 8 // nächstes qword aus dest add esi, 8 // nächstes qword aus src dec ecx // zähler verringern jnz l0 // weiter kopieren, wenn werte übrig emms // mmx-register als unbenutzt markieren } } extern "C" __declspec( dllexport ) long OpenStack (long Wd,long Hg) { Width=Wd; Height=Hg; stack = (piBGR) malloc(Width*3*Height*2); ibuf = (piBGR) malloc(Width*3*Height*2); return 0; } extern "C" __declspec( dllexport ) long StackImage (void) { addSaturated((unsigned short*)stack,(unsigned short*)ibuf,Width*Height*6); return 0; }
-
Wie groß sind width und height?
-
const int WIDTH = 1280;
const int HEIGHT = 1024;
-
Hab ich evtl. etwas mit den Pointern falsch gemacht?
-
Du übergibst addSaturated die Größe des Speichers, das ist aber nicht richtig. Die Funktion erwartet die Anzahl der zu bearbeitenden unsigned shorts.
Du kannst einfach die Zeile
shr ecx, 2
durch
shr ecx, 3
ersetzen, dann funktioniert es auch mit deiner Größenangabe.
/edit: Erklärung: "shr ecx, 2" teilt die Größenangabe durch 4, konvertiert also von word nach qword. "shr ecx, 3" teilt die Größenangabe durch 8, konvertiert also von octet nach qword
Btw, die Casts bei malloc kannst du dir sparen.
long OpenStack (long Wd,long Hg) { Width=Wd; Height=Hg; stack = malloc(Width*Height*sizeof(siBGR)); ibuf = malloc(Width*Height*sizeof(siBGR)); return 0; }
-
Ich würd' die Anzahl der Berechnungen schon in unsigned shorts und nicht in sizeof( unsigned short ) angeben wollen müssen...
pock schrieb:
Btw, die Casts bei malloc kannst du dir sparen.
Immer wieder. Ich hab's auch erst hier im Forum gelernt ;). Stinkt förmlich nach FAQ...
Greetz, Swordfish
PS @ OP: Gewöhn Dir bitte intrinsics an.
-
Du kannst einfach die Zeile
shr ecx, 2
durch
shr ecx, 3
ersetzen, dann funktioniert es auch mit deiner Größenangabe.
Super es funktioniert!!!!!!
Ich hab auch das Subtrahieren umgesetzt - perfekt. Genau so hab ich mir das vorgestellt. Die Routine ist super schnell!!!!Ich hab noch zwei weitere Funktionen, die ich gern umsetzen würde:
for(x=0;x<Width;x++){ for(y=0;y<Height;y++){ a=x+y*Width; ibuf[a].g=(unsigned short)buf[a].g; ibuf[a].r=(unsigned short)buf[a].r; ibuf[a].b=(unsigned short)buf[a].b; } }
Kann man diese Funktion mit unpack umsetzen?
for(x=startx;x<sizex+startx;x++){ for(y=starty;y<sizey+starty;y++){ buf[x+(Height-y-1)*Width].g=limit(stack[x+y*Width].g); buf[x+(Height-y-1)*Width].r=limit(tack[x+y*Width].r); buf[x+(Height-y-1)*Width].b=limit(stack[x+y*Width].b); } }
limit: limit to 0...255
Ich bin mir bei dem Teil nicht ganz sicher, ob man hier "packuswb" verwenden kann, da diese Funktion signed short erwartet?Lg und vielen DANK!!!!!
-
Du kennst ja nun das Prinzip, alles weitere kannst du dir sicher selbst erarbeiten
Wenn du den Assembler-Part nicht komplett verstehst und du nicht absolut optimale Performance brauchst, solltest du vielleicht die Routine mit Intrinsics in C implementieren. Damit kannst du dann nach Belieben experimentieren.
Noch ein genereller Performance-Tip: dieses Schleifenkonstrukt
for(x=0; x<w; x++) for(y=0; y<h; y++) buf[x+y*w] = ibuf[x+y*w];
ist performancetechnisch ungünstig.
1. Die Schleife über die Breite sollte generell immer die Innerste sein, um maximalen Vorteil aus Caching und Prefetching zu ziehen.
2. Wenn dein Buffer x*y Elemente hat und du alle Elemente anfassen musst, musst du nicht Zeilen/Spaltenweise über die Buffer laufen, sondern kannst das auch Elementweise tun. Das spart Multiplikationen.
3. Möglicherweise ist es schneller, wenn die Schleife rückwärts läuft. Ein Check auf 0 ist schneller als ein Check auf einen anderen Wert.
So umgeschrieben läuft obige Schleife erheblich schneller:
c = w*h; foo = buf; bar = ibuf; for(i=c; i>0; i--) *foo++ = *bar++;
Das entspricht einem simplen memcpy, das Prinzip ist natürlich auch auf andere Operationen anwendbar.
-
Hi,
vielen Dank für den Hinweis, ich werde Ihn umsetzen. Ich bin zwar nicht so vertraut mit ASM, aber der Startpunkt reicht mir um weiterzukommen.Nochmal vielen Dank,
Matthias