Ich habe eine Weile mit der Leistung der Netzwerkcodierung in einer Anwendung gekämpft, die ich entwickle (siehe Optimierung des SSE-Codes , Verbessern der Leistung der Codierung des Netzwerkcodes und OpenCL-Verteilung ). Jetzt bin ich ziemlich nah dran, eine akzeptable Leistung zu erreichen. Dies ist der aktuelle Zustand der innersten Schleife (in der & gt; 99% der Ausführungszeit ausgegeben wird):
%Vor%Haben Sie Vorschläge, wie Sie dies weiter optimieren können? Ich verstehe, dass es schwierig ist, ohne Kontext auszukommen, aber jede Hilfe wird geschätzt!
Jetzt, wo ich wach bin, hier ist meine Antwort:
In Ihrem ursprünglichen Code ist der Engpass fast sicher _mm_set_epi32
. Dieser einzelne intrinsische wird in diesem Chaos in Ihrer Versammlung kompiliert:
Was ist das? 9 Anweisungen?!?!?! Pure Overhead ...
Eine andere Stelle, die seltsam erscheint, ist, dass der Compiler die Adds und Loads nicht zusammengeführt hat:
%Vor%sollte in Folgendes eingefügt worden sein:
%Vor% Ich bin mir nicht sicher, ob der Compiler hirntot wurde, oder ob er tatsächlich einen legitimen Grund hatte, das zu tun ... Wie auch immer, es ist eine kleine Sache im Vergleich zu _mm_set_epi32
.
Haftungsausschluss: Der Code, den ich hier vorstellen werde, verstößt gegen das strikte Aliasing. Häufig werden jedoch nicht standardkonforme Methoden benötigt, um eine maximale Leistung zu erzielen.
Lösung 1: Keine Vektorisierung
Diese Lösung setzt voraus, dass allZero
wirklich nur aus Nullen besteht.
Die Schleife ist eigentlich einfacher als es aussieht. Da es nicht viel Arithmetik gibt, ist es besser, einfach nicht zu vektorisieren:
%Vor%Was kompiliert zu diesem auf x64:
%Vor%und das auf x86:
%Vor%Es ist möglich, dass beide bereits schneller sind als Ihr ursprünglicher (SSE) Code ... Auf x64 wird das Abrollen noch besser.
Lösung 2: SSE2 Integer Shuffle
Diese Lösung entrollt die Schleife auf zwei Iterationen:
%Vor%was zu diesem (x86) kompiliert wird: (x64 ist nicht zu verschieden)
%Vor%Nur etwas länger als die nicht vektorisierte Version für zwei Iterationen. Dies verwendet sehr wenige Register, so dass Sie diese sogar auf x86 weiter ausrollen können.
Erläuterungen:
_mm_set_epi32
(das in etwa ~ 9 Anweisungen in Ihrem ursprünglichen Code kompiliert wird) durch ein einzelnes _mm_shuffle_epi32
ersetzt werden. Ich schlage vor, dass Sie Ihre Schleife um einen Faktor von 2 auflösen, so dass Sie 4 messageField-Werte mit einem _mm_load_XXX laden können, und entpacken Sie dann diese vier Werte in zwei Vektorpaare und verarbeiten Sie sie gemäß der aktuellen Schleife. Auf diese Weise wird nicht viel unordentlicher Code vom Compiler für _mm_set_epi32 generiert und alle Ihre Ladevorgänge und Speicher werden 128 Bit SSE-Lade- / Speichervorgänge sein. Dies gibt dem Compiler auch mehr Gelegenheit, Befehle innerhalb der Schleife optimal zu planen.
Tags und Links optimization c x86 sse