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
add eax, ebx
cmp eax, 0x10
cmovne ebx, ecx
add eax, ecx
Zum Zeitpunkt der Auswertung dieses ASM-Befehls ist das Ergebnis des vorhergehenden CMP-Befehls noch nicht bekannt.
Vielleicht, aber die CPU weiß immer noch, dass der Befehl nach dem cmov
Befehl direkt danach ausgeführt wird, unabhängig vom Ergebnis des Befehls cmp
und cmov
. 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
add eax, ebx
cmp eax, 0x10
je .skip
mov ebx, ecx
.skip:
add eax, ecx
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 Befehl mov 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.