Fast byteweise ersetzen if

8

Ich habe eine Funktion, die binäre Daten von einem Bereich in einen anderen kopiert, aber nur wenn die Bytes sich von einem bestimmten Wert unterscheiden. Hier ist ein Codebeispiel:

%Vor%

Das Problem ist, dass das für meinen momentanen Bedarf zu langsam ist. Gibt es eine Möglichkeit, das gleiche Ergebnis schneller zu erhalten?

Aktualisierung: Basierend auf Antworten habe ich zwei neue Implementierungen ausprobiert:

%Vor%

Und ich habe folgende Ergebnisse:

%Vor%

Ich schätze, mein Compiler konnte nicht vektorisiert werden (letzter MSVC), aber die SIMD-Lösung ist gut genug, danke!

Aktualisierung (bis) Ich habe es geschafft, es mit einigen Pragma-Anweisungen für meine Kompilierung (MSVC) zu vektorisieren und tatsächlich ist es tatsächlich schneller, dass SIMD, hier ist der endgültige Code:

%Vor%     
J. Edmond 23.02.2016, 13:11
quelle

4 Antworten

6

Mein gcc 4.8.4 vektorisiert den folgenden Code:

%Vor%

Beachten Sie, dass sowohl der Ladevorgang von - als auch die Zuweisung zu dest[i] unbedingt sind. Daher ist der Compiler nicht durch das Verbot der Erfindung von Speichern in einem Multithread-Programm eingeschränkt.

Für -march=core-avx2 enthält die generierte Assembly diese vektorisierte Schleife, die jeweils mit 32 Bytes arbeitet:

%Vor%

Für generische x86-64 enthält die generierte Assembly diese vektorisierte Schleife, die jeweils 16 Bytes bearbeitet:

%Vor%

Für armv7l-neon generiert clang-3.7 die folgende Schleife, die jeweils 16 Bytes gleichzeitig bearbeitet:

%Vor%

So ist der Code nicht nur lesbarer als Assembly oder intrinsics, er ist auch portable für mehrere Architekturen und Compiler. Neue Architekturen und Befehlssatzerweiterungen können einfach durch Neukompilierung verwendet werden.

    
EOF 23.02.2016, 13:34
quelle
5

Hier ist ein Beispiel, das SSE2-Instrinsics verwendet, um die auszuwerten maskmovdqu Anweisung. Die SIMD-Version scheint auf einer Haswell-CPU etwa doppelt so schnell zu laufen wie die ursprüngliche Version (Code, der mit clang kompiliert wurde):

%Vor%

Kompilieren und testen:

%Vor%

(Hinweis: frühere Version dieser Antwort hatte einen Streufaktor von 16 im Timing-Code, so dass frühere Zahlen 16x höher waren, als sie hätten sein sollen.)

AKTUALISIEREN

Inspiriert von @ EOFs Lösung und Compiler-generiertem Code habe ich einen anderen Ansatz mit SSE4 versucht und viel bessere Ergebnisse erzielt:

%Vor%

Kompilieren und testen:

%Vor%

Fazit : Während _mm_maskmoveu_si128 aus funktionaler Sicht eine gute Lösung für dieses Problem zu sein scheint, scheint es nicht so effizient zu sein wie explizite Ladevorgänge, Maskierung und Speicherung. Darüber hinaus scheint Compiler-generierten Code (siehe @ EOF-Antwort) genauso schnell zu sein wie explizit SIMD in diesem Fall.

    
Paul R 23.02.2016 14:02
quelle
0

Das Folgende wäre eine Verbesserung, obwohl Compiler das selbst entwickeln könnten.

%Vor%     
Paul Ogilvie 23.02.2016 13:21
quelle
0

Wenn die Häufigkeit der Ignorierung nicht zu hoch ist, kann ein Memcpy-Code wie unten hilfreich sein.

%Vor%     
user3177100 23.02.2016 14:56
quelle

Tags und Links