SSE intrinsics - Vergleich if / else Optimierung

8

Ich habe versucht, einen Code zu optimieren, der rohe Pixeldaten verarbeitet. Derzeit ist die C ++ - Implementierung des Codes zu langsam, also habe ich versucht, einige Gründe mit SSE intrinsics (SSE / 2/3, die nicht 4 verwenden) mit MSVC 2008 zu machen. Wenn ich bedenke, dass ich das erste Mal in diesem Tief graben Ich habe gute Fortschritte gemacht.

Leider bin ich zu einem bestimmten Code gekommen, an dem ich hängengeblieben bin:

%Vor%

Zur Zeit verwende ich standardmäßig eine C ++ - Implementierung für diesen Abschnitt, weil ich nicht genau verstehen kann, wie dies mit SSE optimiert werden kann - ich finde die SSE-Eigenarten für Vergleiche ein bisschen knifflig sein.

Alle Vorschläge / Tipps würden sehr geschätzt werden.

BEARBEITEN: Der entsprechende C ++ - Code, der jeweils ein einzelnes Pixel behandelt, wäre:

%Vor%

Grundsätzlich mache ich eine nichtlineare Kantenerstreckung von 4: 3 bis 16: 9.

    
ZeroDefect 24.01.2012, 12:07
quelle

2 Antworten

7

Ok, ich weiß nicht, was dieser Code macht, aber ich weiß, dass Sie sich fragen, wie Sie die Operatoren optimieren können und dass dieser Teil des Codes nur in SSE funktioniert. Als ersten Schritt würde ich empfehlen, einen Ansatz zu versuchen, der ganzzahlige Flags und Multiplikationen verwendet, um einen bedingten Operator zu vermeiden. Zum Beispiel:

Dieser Abschnitt

%Vor%

Ist syntaktisch äquivalent zu diesem

%Vor%

Durch die Aufteilung in zwei Schleifen verlieren Sie zwar die Leistungssteigerung des seriellen Speicherzugriffs, aber Sie verlieren eine Modulo-Operation und zwei bedingte Operatoren.

Nun sagen Sie, Sie bemerken, dass es zwei boolesche Operatoren pro Schleife gibt, und die Multiplikationen , die ich hinzufügen kann, sind keine intrinsischen SSE-Implementierungen . Was ist in Ihrem vn1.m123i_u16 [] Array gespeichert? Sind es nur Nullen und Einsen? Dann brauchen Sie dieses Teil nicht und können es beseitigen. Wenn nicht, können Sie Ihre Daten in diesem Array so normalisieren, dass nur Nullen und Einsen vorhanden sind? Wenn das Array vn1.m123i_u16 nur Einsen und Nullen enthält, wird dieser Code

%Vor%

Sie werden auch bemerken, dass ich keine SSE-Multiplikationen verwende, um isEvenFloor * vnPx... part auszuführen oder die Register iIsEvenFloor und iIsOddFloor zu speichern. Es tut mir leid, ich kann mich nicht erinnern, die SSE intrinsics für u16 multiplizieren / registrieren von oben, aber trotzdem hoffe ich, dass dieser Ansatz hilfreich ist. Einige Optimierungen, auf die Sie achten sollten:

%Vor%

In diesem Codeabschnitt, den Sie gepostet haben, und meiner Änderung machen wir immer noch nicht die SSE1 / 2/3-Eigenarten voll aus, aber es könnte einige Punkte dazu liefern, wie das gemacht werden könnte (wie man den Code vektorisiert) ).

Schließlich würde ich sagen, alles zu testen. Führen Sie den obigen Code unverändert aus und profilieren Sie ihn, bevor Sie Änderungen vornehmen und erneut ein Profil erstellen. Die tatsächlichen Leistungszahlen können Sie überraschen!

Update 1 :

Ich habe die Intel SIMD Intrinsics-Dokumentation durchlaufen wählen Sie relevante intrinsics aus, die dafür nützlich sein könnten. Schauen Sie sich speziell die bitweise XOR, AND und MULT / ADD

an
  

__ m128 Datentypen
    Der Datentyp __m128i kann sechzehn 8-Bit-, acht 16-Bit-, vier 32-Bit- oder zwei 64-Bit-Ganzzahlwerte enthalten.

     

__ m128i _mm_add_epi16 (__m128i a, __m128i b)
    Addiere die 8 vorzeichenbehafteten oder vorzeichenlosen 16-Bit-Ganzzahlen in a zu den 8 vorzeichenbehafteten oder unsignierten 16-Bit-Ganzzahlen in b

     

__ m128i _mm_mulhi_epu16 (__ m128i a, __m128i b)
    Multipliziert die 8 vorzeichenlosen 16-Bit-Ganzzahlen von a mit den 8-vorzeichenlosen 16-Bit-Ganzzahlen von b.     Packt die oberen 16 Bits der 8-vorzeichenlosen 32-Bit-Ergebnisse

     

R0 = hiword (a0 * b0)
    R1 = hiword (a1 * b1)
    R2 = hiword (a2 * b2)
    R3 = hiword (a3 * b3)
    ..
    R7 = Hiwort (a7 * b7)

     

__ m128i _mm_mullo_epi16 (__ m128i a, __m128i b)
    Multipliziert die 8 vorzeichenbehafteten oder vorzeichenlosen 16-Bit-Ganzzahlen von a mit den 8-vorzeichenbehafteten oder unsignierten 16-Bit-Ganzzahlen aus b.     Packt die oberen 16 Bits der 8-signierten oder vorzeichenlosen 32-Bit-Ergebnisse

     

R0 = tief (a0 * b0)
    R1 = tief (a1 * b1)
    R2 = tief (a2 * b2)
    R3 = tief (a3 * b3)
    ..
    R7 = Tiefpunkt (a7 * b7)

     

__ m128i _mm_and_si128 (__ m128i a, __m128i b)
    Führen Sie eine bitweise UND-Verknüpfung des 128-Bit-Werts in m1 mit dem 128-Bit-Wert in m2 durch.

     

__ m128i _mm_andnot_si128 (__ m128i a, __m128i b)
    Berechnet das bitweise UND des 128-Bit-Wertes in b und das bitweise NICHT-Bit des   Bit-Wert in a.

     

__ m128i _mm_xor_si128 (__ m128i a, __m128i b)
     Führen Sie eine bitweise XOR-Verknüpfung des 128-Bit-Werts in m1 mit dem 128-Bit-Wert in m2 durch.

     

Auch von Ihrem Codebeispiel als Referenz

     

uint16 u1 = u2 = u3 ... = u15 = 0x1
    __m128i vnMask = _mm_set1_epi16 (0x0001); // Setzt die 8 vorzeichenbehafteten 16-Bit-Ganzzahlwerte.

     

uint16 vn1 [i] = vnFloors [i] & amp; 0x1
    __m128i vn1 = _mm_and_si128 (vnFloors, vnMask); // Berechnet das bitweise UND des 128-Bit-Werts in a und den 128-Bit-Wert in b.

    
Dr. ABT 24.01.2012, 12:29
quelle
2

Andrew Ihre Vorschläge führen mich den Weg zu einer fast optimalen Lösung.

Mit einer Kombination aus einer Wahrheitstabelle und Karnaugh-Map habe ich herausgefunden, dass der Code

ist %Vor%

hat sich zu einer! xor (nicht xor) Funktion entwickelt. Von nun an konnte ich mit der SSE-Vektorisierung die Lösung optimieren:

%Vor%

Danke für die Hilfe ...

    
ZeroDefect 25.01.2012 10:50
quelle

Tags und Links