Nachdem ich diesen Beitrag gelesen hatte (Antwort auf StackOverflow) (im Optimierungsabschnitt), habe ich mich gefragt, warum bedingte Verschiebungen nicht für Branch Prediction Failure anfällig sind. Ich habe hier einen Artikel über Cond Moves gefunden (PDF von AMD) . Auch dort beanspruchen sie den Leistungsvorteil von cond. bewegt sich. Aber warum ist das so? Ich sehe es nicht Zum Zeitpunkt der Auswertung dieses ASM-Befehls ist das Ergebnis des vorhergehenden CMP-Befehls noch nicht bekannt.
performance
assembly
optimization
cpu-architecture
branch-prediction
Martijn Courteaux
quelle
quelle
Antworten:
Falsch vorhergesagte Filialen sind teuer
Ein moderner Prozessor führt im Allgemeinen zwischen einem und drei Befehlen pro Zyklus aus, wenn die Dinge gut laufen (wenn er nicht auf Datenabhängigkeiten wartet, bis diese Befehle aus vorherigen Befehlen oder aus dem Speicher eintreffen).
Die obige Aussage gilt überraschend gut für enge Schleifen, aber dies sollte Sie nicht für eine zusätzliche Abhängigkeit blind machen, die verhindern kann, dass ein Befehl ausgeführt wird, wenn sein Zyklus kommt: Damit ein Befehl ausgeführt werden kann, muss der Prozessor mit dem Abrufen und Decodieren begonnen haben es 15-20 Zyklen vor.
Was soll der Prozessor tun, wenn er auf einen Zweig stößt? Das Abrufen und Decodieren beider Ziele wird nicht skaliert (wenn weitere Verzweigungen folgen, müsste eine exponentielle Anzahl von Pfaden parallel abgerufen werden). Der Prozessor holt und spekuliert also nur spekulativ einen der beiden Zweige.
Aus diesem Grund sind falsch vorhergesagte Zweige teuer: Sie kosten die 15 bis 20 Zyklen, die aufgrund einer effizienten Befehlspipeline normalerweise unsichtbar sind.
Bedingter Umzug ist nie sehr teuer
Ein bedingter Zug erfordert keine Vorhersage, daher kann er diese Strafe niemals haben. Es hat Datenabhängigkeiten, wie gewöhnliche Anweisungen. Tatsächlich weist eine bedingte Verschiebung mehr Datenabhängigkeiten auf als normale Anweisungen, da die Datenabhängigkeiten sowohl Fälle von "Bedingung wahr" als auch "Bedingung falsch" umfassen. Nach einer Anweisung , dass bedingt bewegt
r1
zur2
, deren Inhalter2
sowohl auf dem vorherigen Wert von scheinen zu hängenr2
und aufr1
. Ein gut vorhergesagter bedingter Zweig ermöglicht es dem Prozessor, genauere Abhängigkeiten abzuleiten. Es dauert jedoch in der Regel ein bis zwei Zyklen, bis Datenabhängigkeiten eintreffen, wenn sie überhaupt Zeit benötigen.Beachten Sie, dass ein bedingter Wechsel vom Speicher zum Register manchmal eine gefährliche Wette ist: Wenn die Bedingung so ist, dass der aus dem Speicher gelesene Wert nicht dem Register zugewiesen wird, haben Sie auf nichts im Speicher gewartet. Die in Befehlssätzen angebotenen bedingten Verschiebungsbefehle werden jedoch typischerweise von Register zu Register registriert, wodurch dieser Fehler seitens des Programmierers verhindert wird.
quelle
Es dreht sich alles um die Anweisungspipeline . Denken Sie daran, dass moderne CPUs ihre Anweisungen in einer Pipeline ausführen, was zu einer erheblichen Leistungssteigerung führt, wenn der Ausführungsfluss von der CPU vorhersehbar ist.
cmov
Vielleicht, aber die CPU weiß immer noch, dass der Befehl nach dem
cmov
Befehl direkt danach ausgeführt wird, unabhängig vom Ergebnis des Befehlscmp
undcmov
. Der nächste Befehl kann somit sicher vorzeitig abgerufen / decodiert werden, was bei Verzweigungen nicht der Fall ist.Die nächste Anweisung könnte sogar vor der Ausführung ausgeführt werden
cmov
(in meinem Beispiel wäre dies sicher).Ast
In diesem Fall muss der Decoder der CPU, wenn er dies sieht
je .skip
, entscheiden, ob das Vorabrufen / Decodieren von Befehlen entweder 1) vom nächsten Befehl oder 2) vom Sprungziel fortgesetzt werden soll. Die CPU wird davon ausgehen, dass diese bedingte Vorwärtsverzweigung nicht stattfinden wird, sodass der nächste Befehlmov ebx, ecx
in die Pipeline aufgenommen wird.Ein paar Zyklen später wird das
je .skip
ausgeführt und der Zweig genommen. Verdammt! Unsere Pipeline enthält jetzt zufälligen Junk, der niemals ausgeführt werden sollte. Die CPU muss alle zwischengespeicherten Anweisungen leeren und neu starten.skip:
.Dies ist der Leistungsverlust von falsch vorhergesagten Zweigen, der niemals auftreten kann,
cmov
da er den Ausführungsfluss nicht verändert.quelle
Das Ergebnis ist zwar noch nicht bekannt, aber wenn andere Umstände dies zulassen (insbesondere die Abhängigkeitskette), kann die CPU Anweisungen gemäß den Anweisungen neu anordnen und ausführen
cmov
. Da es sich nicht um eine Verzweigung handelt, müssen diese Anweisungen in jedem Fall ausgewertet werden.Betrachten Sie dieses Beispiel:
Die beiden folgenden Anweisungen
cmov
hängen nicht vom Ergebnis des abcmov
, sodass sie auch ausgeführt werden können, während dascmov
selbst ansteht (dies wird als Ausführung außerhalb der Reihenfolge bezeichnet ). Auch wenn sie nicht ausgeführt werden können, können sie abgerufen und dekodiert werden.Eine Verzweigungsversion könnte sein:
Das Problem hierbei ist, dass sich der Kontrollfluss ändert und die CPU nicht klug genug ist, um zu erkennen, dass sie die übersprungene
mov
Anweisung einfach "einfügen" kann, wenn der Zweig als genommen falsch vorhergesagt wurde. Stattdessen wirft sie alles weg, was sie nach dem Zweig getan hat, und startet neu von Grund auf neu. Hier kommt die Strafe her.quelle
Sie sollten diese lesen. Suchen Sie mit Fog + Intel einfach nach CMOV.
Linus Torvalds Kritik an CMOV um 2007
Agner Fogs Vergleich der Mikroarchitekturen Referenzhandbuch zur Optimierung von
Intel® 64- und IA-32-Architekturen
Kurze Antwort, korrekte Vorhersagen sind "kostenlos", während bedingte Verzweigungsvorhersagen auf Haswell 14 bis 20 Zyklen kosten können. CMOV ist jedoch niemals kostenlos. Trotzdem denke ich, dass CMOV jetzt viel besser ist als damals, als Torvalds schimpfte. Es gibt keinen einzigen, der für alle Zeiten korrekt ist, und alle Prozessoren antworten jemals.
quelle
cmov
ist immer noch eine Datenabhängigkeit, sodass durch Schleifen übertragene Abhängigkeitsketten erstellt werden können, die die Verzweigungsvorhersage verborgen hätte. Intel Broadwell / Skylake dekodieren es in ein einzelnes UOP anstelle von 2 (Haswell und früher), sodass es jetzt etwas günstiger ist. Der UOP-Cache von Sandybridge und höher bedeutet, dass die Strafe für den Decodierungsdurchsatz für Multi-UOP-Anweisungen normalerweise ebenfalls kein Faktor ist. Der grundlegende Unterschied zwischen einer Daten- und einer Steuerelementabhängigkeit wird dadurch jedoch nicht geändert. Außerdem verfügt x86cmov
immer noch nicht über ein Formular mit einem unmittelbaren Operanden und ist daherx = x<3 ? x : 3
immer noch umständlich zu implementieren.Ich habe diese Illustration von [Peter Puschner et al.] Folie, die erklärt, wie sie sich in Einzelpfadcode umwandelt und die Ausführung beschleunigt.
quelle
cmp
/ könnteswplt
, wenn es eine Swap / Exchange-Anweisung hätte.) Wie auch immer, moderne CPUs haben im Allgemeinen keine Blasen von genommenen Zweigen, sondern Blasen von falschen Vorhersagen : stackoverflow.com/questions/11227809/… . In Code mit hohem Durchsatz können korrekt vorhergesagte genommene Verzweigungen die Decodierungs- / Front-End-Bandbreite jedoch etwas reduzieren.