AKTUALISIERT - Überprüfen Sie unten
Wird dies so kurz wie möglich halten. Gerne fügen Sie bei Bedarf weitere Details hinzu.
Ich habe etwas Code zum Normalisieren eines Vektors. Ich benutze QueryPerformanceCounter () (eingepackt in eine Hilfsstruktur), um die Leistung zu messen.
Wenn ich so messe
%Vor%Die Ergebnisse, die ich bekomme, sind oft langsamer als nur eine Standardnormalisierung mit 4 Doppelpunkten, die einen Vektor darstellen (Test in derselben Konfiguration).
%Vor%Allerdings wird nur die gesamte Schleife wie folgt getaktet
%Vor%zeigt an, dass der SSE-Code eine Größenordnung schneller ist, beeinflusst aber die Messungen für die Double-Version nicht wirklich. Ich habe ein gutes Stück experimentiert und gesucht und finde keine vernünftige Antwort, warum.
Zum Beispiel weiß ich, dass es Strafen geben kann, wenn die Ergebnisse in Umlauf gebracht werden, aber nichts davon passiert hier.
Kann jemand Einblick geben? Was ist der Aufruf von QueryPerformanceCounter zwischen jeder Normalize, die den SIMD-Code so sehr verlangsamt?
Danke fürs Lesen:)
Weitere Details unten:
Einfache Vektorstruktur
%Vor%Code zum Normalisieren von SSE:
%Vor%Code zum Normalisieren von Doppelbildern
%Vor%Hilfsstruktur
%Vor%Aktualisieren Also, dank Johns Kommentaren, glaube ich, dass ich es geschafft habe zu bestätigen, dass es QueryPerformanceCounter ist, der meinem simd-Code schlechte Dinge zufügt.
Ich habe eine neue Timer-Struktur hinzugefügt, die RDTSC direkt verwendet, und es scheint Ergebnisse zu ergeben, die konsistent sind mit dem, was ich erwarten würde. Das Ergebnis ist immer noch viel langsamer als das Timing der gesamten Schleife, statt jeder einzelnen Iteration, aber ich erwarte, dass dies daran liegt, dass das RDTSC geleert wird, indem die Befehlspipeline geleert wird (Check Ссылка für weitere Informationen).
%Vor%Wenn nur der SSE-Code die Schleife ausführt, sollte der Prozessor in der Lage sein, seine Pipelines voll zu halten und eine große Anzahl von SIMD-Befehlen pro Zeiteinheit auszuführen. Wenn Sie den Timer-Code innerhalb der Schleife hinzufügen, gibt es jetzt eine ganze Reihe von Nicht-SIMD-Anweisungen, die möglicherweise weniger vorhersehbar sind, zwischen jedem der einfach zu optimierenden Operationen. Es ist wahrscheinlich, dass der Queryperformancecounter Anruf entweder teuer genug ist, die Datenmanipulation Teil unbedeutend, oder die Art des Codes zu machen führt sie wreaks Verwüstung mit der Fähigkeit des Prozessors mit der maximalen Rate Ausführen von Anweisungen zu halten (möglicherweise aufgrund von Cache-Räumungen oder Zweigen, die sind nicht gut vorhergesagt).
Sie könnten versuchen, die tatsächlichen Aufrufe an QPC in Ihrer Timer-Klasse zu kommentieren und zu sehen, wie es funktioniert - dies könnte Ihnen helfen herauszufinden, ob es die Konstruktion und Zerstörung der Timer-Objekte ist, die das Problem sind, oder die QPC-Aufrufe. Versuchen Sie ebenfalls, QPC direkt in der Schleife aufzurufen, anstatt einen Timer zu erstellen, und sehen Sie, wie dieser vergleicht.
QPC ist eine Kernel-Funktion, deren Aufruf einen Kontextwechsel verursacht, der von Natur aus viel teurer und destruktiver ist als jeder andere funktionierende Funktionsaufruf, und die Prozessorfähigkeit des Prozessors mit normaler Geschwindigkeit aufheben wird. Beachten Sie außerdem, dass QPC / QPF Abstraktionen sind und ihre eigene Verarbeitung erfordern - was wahrscheinlich die Verwendung von SSE selbst beinhaltet.