SSE?
-
habe folgenden C++ Code, den ich gerne mit SSE optimieren möchte, da er sich meiner Meinung nach hervorragend für Parallelisierung eignet
for(int i = 0; i < size; i++) { if(before[i] != after[i]) buffer[i] = after[i]; else buffer[i] = 0xFFFF00FF; }
before, after und buffer sind eindimensionale Arrays vom Typ unsigned int - die Arrays sind gleich groß (480000 Elemente) - diese Codestelle wird mehrmals in der Sekunde aufgerufen und ich möchte sie daher etwas optimieren
Momentan frage ich mich gerade wie ich das am dümmsten mache...
unsigned int Array1[] = {1,1,1,1}; unsigned int Array2[] = {2,1,2,1}; __asm { movups xmm0, Array1 movups xmm1, Array2 cmpps xmm0, xmm1, 1 ... }
jetzt kann ich vier Array Elemente gleichzeitig vergleichen, aber wie weiße ich meinem buffer Array den entsprechenden Wert zu? mmh...
-
1. 480 000 elemente sind also min 480kb also ~1.5mb bei 3buffer. das wirft dich also schonmal sehr zurück weil es nicht in den cache passt.
2. mit cmov wäre es eventuel genauso schnell wie mit sse spezialitäten.
3. bei deinem vergleich bekommst du ein register mit werten von 00 bzw ff. dann kannst du mittels OR(AND(register,MM0),NAND(register,MM1))... so in etwa... in worten: du verknüpfst den einen wert AND mit dem inhalt des registers und den zweiten wert mit NAND mit dem inhalt des registers, also AND mit dem inversen davon. danach die beiden resultate OR verknüpfen, denn einer dürfte komplett 0 sein, so erhälst du den wert. aber cmov macht das an sich auch
rapso->greets();
-
Die Verwendung des PS-Datentyps (Packed Single precision floating-point, also gepackte "float"-Werte) geht ohnehin schief. (SSE führt keine automatische Typkonvertierung durch!)
Für Ganzzahlen sollte man MMX verwenden, da bei Fließkommazahlen immer einige Ganzzahlen außérhalb des darstellbaren Wertebereichs liegen. Da die Werte unmittelbar nach dem Laden verglichen werden, könnte das in diesem Fall aber funktionieren, vorausgesetzt, die Rückumwandlung klappt.
Das Alignment der Arrays ist außerdem extrem wichtig für MMX bzw. SSE. D.h. alle Datenworte müssen auf Wortgröße hin ausgerichtet sein, sonst kommt es zu drastischen Performanceeinbrüchen (bei häufigen Iterationen).
Der Setup-Aufwand für SSE ist in dem vorliegenden Fall besonders hoch, da die Werte zuerst in MMX-Register geladen, dann in SSE-Register übertragen, dann in Fließkomma konvertiert, verglichen, und ggf. zurückkonvertiert werden müssen.
Für optimalen SSE-Durchsatz sollten die Werte zumindest als Ganzzahlen gespeichert werden, und zwar interleaved:
<wert1> <wert2> <default-wert> <ergebnis>
Dadurch wird das Cache-Problem umgangen, das rapso erwähnt hat.
Bei Verwendung von Fließkommazahlen wäre der Durchsatz noch höher, jedoch könnten die Vergleiche gelegentlich fehlschlagen, je nachdem, wie die Fließkommawerte berechnet wurden.
Falls es sich um eine Art Grafikoperation handelt, könnte man ggf. auch andere Lösungsmöglichkeiten in Erwägung ziehen.
-
Power Off schrieb:
Falls es sich um eine Art Grafikoperation handelt, könnte man ggf. auch andere Lösungsmöglichkeiten in Erwägung ziehen.
es handelt sich um ein grafisches Problem
es sind zwei Bilder gegeben:
eines beschreibt den Zustand "vorher", das andere den Zustand "nachher"http://turing.fh-landshut.de/~jamann/upload/vorher.JPG
http://turing.fh-landshut.de/~jamann/upload/nachher.JPGich möchte nun alles was sich verändert hat mit der Farbe aus Nachher einfärben und alles was sich nicht verändert hat Rosa machen - also ein Differenz Bild erstellen:
http://turing.fh-landshut.de/~jamann/upload/diff.JPG
Die bisherige Lösung ist hier downloadbar:
http://turing.fh-landshut.de/~jamann/upload/diff.zip
(ist ein MsVC Net 2003 Projekt - C++ + WinAPI)@raspo
Ich werde mal deine Lösungsansätze versuchen
-
Schau Dir mal das Kapitel "Raster Operations" in der Platform SDK (Windows API) Dokumentation an. Der GDI-Blitter kann diese Operation auch als einzigen Grafikbefehl ausführen.
Hier die Regeln für Blitoperationen (Konzept vom Amiga Blitter, aber im Prinzip auch auf Windows anwendbar):
Es gibt drei Quellen A, B, C und ein Ziel D; die Grafikoperation wird für jedes Bit ausgeführt. Zuerst trägt man die gewünschten Ergebnisse (also die "logische Funktion") im Kanal D ein (wie, hängt davon ab, wie Du die 3 Quellen verwendest). Dadurch bekommst Du den "minterm" oder "lf" code. Bei einem Standard Cookie-Cut Blit mit Quelle A = Maske, Quelle B = Quellbild, Quelle C = Hintergrundbild, Ziel D = Ergebnisbild, ist die LF 0xCA (sieht man, wenn man den Kopf nach links neigt):
A B C | D ----------+----- 0 0 0 | 0 0 0 1 | 1 0 1 0 | 0 0 1 1 | 1 0xA ----------+----- 1 0 0 | 0 1 0 1 | 0 1 1 0 | 1 1 1 1 | 1 0xC
Bei Deiner Operation mußt Du Dir überlegen, welche Quellen und welches Ergebnis Du haben willst. D.h. nimmst Du für Quelle A das alte Bild, und für Quelle B das neue Bild, und für Quelle C die Füllfarbe, sieht die Tabelle so aus:
A B C | D ----------+----- 0 0 0 | 0 ( A == B, d.h. D = Wert von B ) 0 0 1 | 0 ( A == B, d.h. D = Wert von B ) 0 1 0 | 0 ( A != B, d.h. D = Wert von C ) 0 1 1 | 1 0x8 ( A != B, d.h. D = Wert von C ) ----------+----- 1 0 0 | 0 ( A != B, d.h. D = Wert von C ) 1 0 1 | 1 ( A != B, d.h. D = Wert von C ) 1 1 0 | 1 ( A == B, d.h. D = Wert von B ) 1 1 1 | 1 0xE ( A == B, d.h. D = Wert von B )
d.h. da A und B gleich sein müssen, kann man sagen:
D = ( A & B ) | ( ~( A & B ) & C )
Bei Windows sind die 3 Quellen als Quelle, Ziel und Pattern (=Brush) definiert. Da Du als Pattern nur eine Farbe brauchst, erzeugst Du also zunächst einen Brush in der Farbe und selektierst ihn in den Ziel-DC.
Such in der MSDN Online Library bzw. Platform SDK unter "Ternary Raster Operations". Dort heißen die drei Quellen P, S und D (R für Result hier):
P S D | R ----------+----- 0 0 0 | 0 ( S == D, d.h. R = Wert von S ) 0 0 1 | 0 ( S != D, d.h. R = Wert von P ) 0 1 0 | 0 ( S != D, d.h. R = Wert von P ) 0 1 1 | 1 0x8 ( S == D, d.h. R = Wert von S ) ----------+----- 1 0 0 | 0 ( S == D, d.h. R = Wert von S ) 1 0 1 | 1 ( S != D, d.h. R = Wert von P ) 1 1 0 | 1 ( S != D, d.h. R = Wert von P ) 1 1 1 | 1 0xE ( S == D, d.h. R = Wert von S )
Mit D ist hier das alte Bild (und das Ziel) gemeint, mit S das neue Bild, oder umgekehrt (spielt ja keine Rolle, da ja nur auf Gleichheit geprüft wird).
Du siehst, der Minterm Code ist hier derselbe (0xE8).
In der o.a. Ternary Raster Operation Table von Microsoft, ist 0xE8 mit dem Code 0X00E81D74 bzw. SSPxDSxax angegeben. Diesen Code kannst Du direkt bei BitBlt() usw. angeben.
Der Code SSPxDSxax besagt, was die Operation tut (und ist ggf. als define definiert). Sie beschreibt eine Stack-basierte Maschine (ähnlich FORTH), die die folgenden Operationen ausführt:
R = S ^ ( ( S ^ P ) & ( D ^ S ) )
Ich hoffe, das hilft!