Ich bin gerade über dieses Ding gestolpert, und ich bin wirklich neugierig, ob moderne CPUs (aktuelle, vielleicht auch mobile (embedded)) tatsächlich keine Verzweigungskosten in der folgenden Situation haben. p>
1. Sagen wir, wir haben das:
%Vor%2.Im Vergleich dazu:
%Vor% Angenommen, A
und B
unterscheiden sich in den Anweisungen der Pipeline (fetch, decode, execute, etc) völlig:
Wird der zweite Ansatz schneller sein?
Sind CPUs intelligent genug, um zu sagen, dass unabhängig von der Flagge die nächste Anweisung die gleiche ist (sie müssen also keine Pipelinestufen wegen der Verzweigungsfehlervorhersage verwerfen)?
Im ersten Fall hat die CPU keine Option, sondern die ersten paar Pipeline-Stufen von do A
oder do B
zu verwerfen, wenn eine Verzweigungsfehler-Vorhersage passiert ist, weil sie anders sind. Ich sehe das zweite Beispiel als eine irgendwie verzögerte Verzweigung wie: "Ich werde diese Flagge überprüfen, auch wenn ich die Flagge nicht kenne, kann ich mit der nächsten Anweisung weitermachen, weil es gleich ist, egal Was die Flagge ist, ich habe bereits die nächste Anweisung und es ist in Ordnung für mich, sie zu benutzen. "
BEARBEITEN:
Ich habe etwas recherchiert und ich habe ein paar gute Ergebnisse. Wie würdest du dieses Verhalten erklären? Sorry für meine letzte Bearbeitung, aber ich hatte einige Cache-Probleme, soweit ich sehen konnte, das sind genauere Ergebnisse und Code-Beispiele, hoffe ich.
Hier ist der Code, der mit gcc Version 4.8.2 (Ubuntu 4.8.2-19ubuntu1) mit -O3 kompiliert wurde.
Fall 1.
%Vor%Fall 2
%Vor% Es gibt einen ziemlich unmerklichen Unterschied zwischen den -O3-Versionen beider Ansätze, aber ohne -O3 läuft der zweite Fall zumindest auf meiner Maschine etwas schneller.
Ich habe ohne -O3 und mit der Schleife = 0xfffffffe getestet.
Beste Zeiten:
alin @ ubuntu: ~ / Desktop $ time ./1
real 0m20.231s
Benutzer 0m20.224s
sys 0m0.020s
alin @ ubuntu: ~ / Desktop $ time ./2
real 0m19.932s
Benutzer 0m19.890s
sys 0m0.060s
Es gibt zwei Teile:
Zuerst optimiert der Compiler das?
Lassen Sie uns ein Experiment durchführen:
test2.cc und test2.h existieren beide nur, um zu verhindern, dass der Compiler alles wegoptimiert. Der Compiler kann nicht sicher sein, dass es keine Nebenwirkung gibt, weil diese Funktionen in einer anderen Übersetzungseinheit vorhanden sind.
Jetzt kompilieren wir zur Assembly:
%Vor%Und lassen Sie uns zu dem Teil der Versammlung springen, der interessant ist:
%Vor%So können wir sehen, dass der Compiler es nicht optimiert hat. Aber wir haben auch nicht darum gebeten.
%Vor%und dann die interessante Versammlung:
%Vor%Dies ist ein bisschen mehr als meine Fähigkeit, eine 1: 1-Beziehung zwischen der Assembly und dem Code sauber zu zeigen, aber Sie können anhand der Aufrufe von doA und doB erkennen, dass die Einrichtung alles üblich ist und außerhalb der if-Anweisung erfolgt. (Über der Linie jne. L83). Also, Compiler führen diese Optimierung durch.
Teil 2:
Wie können wir wissen, ob CPUs diese Optimierung durchführen, wenn sie den ersten Code erhalten?
Ich bin mir eigentlich nicht bewusst, wie ich das testen kann. Also ich weiß es nicht. Ich würde es als plausibel einstufen, da es eine ausserordentliche und spekulative Ausführung gibt. Aber der Beweis liegt im Pudding, und ich habe keine Möglichkeit, diesen Pudding zu testen. Deshalb zögere ich, auf die eine oder andere Art zu behaupten.
Damals unterstützten die CPUs explizit etwas ähnliches - nach einem Verzweigungsbefehl würde der nächste Befehl immer ausgeführt werden, unabhängig davon, ob der Zweig tatsächlich ausgeführt wurde (siehe "Verzweigungsverzögerungs-Slot").
Ich bin mir ziemlich sicher, dass moderne CPUs die ganze Pipeline nur auf einen Verzweigungsfehler werfen. Es hat keinen Sinn, die von Ihnen vorgeschlagene Optimierung zur Ausführungszeit auszuführen, wenn der Compiler dies zur Kompilierungszeit leicht tun kann.
Tags und Links c c++ pipeline branch-prediction