Auf der untersten Ebene (in der Hardware), ja, wenn s teuer sind. Um zu verstehen, warum, müssen Sie verstehen, wie Pipelines funktionieren.
Der aktuell auszuführende Befehl wird in etwas gespeichert, das typischerweise als Befehlszeiger (IP) oder Programmzähler (PC) bezeichnet wird. Diese Begriffe sind synonym, aber unterschiedliche Begriffe werden mit unterschiedlichen Architekturen verwendet. Bei den meisten Anweisungen ist der PC des nächsten Befehls nur der aktuelle PC plus die Länge des aktuellen Befehls. Bei den meisten RISC-Architekturen haben alle Anweisungen eine konstante Länge, sodass der PC um einen konstanten Betrag erhöht werden kann. Bei CISC-Architekturen wie x86 können Befehle eine variable Länge haben. Daher muss die Logik, die den Befehl decodiert, herausfinden, wie lange der aktuelle Befehl dauert, um den Ort des nächsten Befehls zu finden.
Bei Verzweigungsbefehlen ist der nächste auszuführende Befehl jedoch nicht der nächste Ort nach dem aktuellen Befehl. Zweige sind gotos - sie teilen dem Prozessor mit, wo der nächste Befehl ist. Zweige können entweder bedingt oder bedingungslos sein, und der Zielort kann entweder fest oder berechnet sein.
Bedingt gegen bedingungslos ist leicht zu verstehen - ein bedingter Zweig wird nur genommen, wenn eine bestimmte Bedingung erfüllt ist (z. B. ob eine Zahl einer anderen entspricht); Wenn die Verzweigung nicht genommen wird, fährt die Steuerung wie gewohnt mit der nächsten Anweisung nach der Verzweigung fort. Bei bedingungslosen Verzweigungen wird immer die Verzweigung verwendet. Bedingte Verzweigungen erscheinen in if
Aussagen und Kontrolltests vonfor
und while
Schleifen angezeigt. Unbedingte Verzweigungen werden in Endlosschleifen, Funktionsaufrufen, Funktionsrückgaben break
und continue
Anweisungen, der berüchtigten goto
Anweisung und vielem mehr angezeigt (diese Listen sind alles andere als vollständig).
Das Zweigziel ist ein weiteres wichtiges Thema. Die meisten Zweige haben ein festes Zweigziel - sie gehen zu einem bestimmten Ort im Code, der zur Kompilierungszeit festgelegt ist. Das beinhaltetif
Anweisungen, Schleifen aller Art, regelmäßige Funktionsaufrufe und vieles mehr. Berechnete Verzweigungen berechnet das Ziel der Verzweigung zur Laufzeit. Dies umfasst switch
(manchmal) Anweisungen, die von einer Funktion zurückkehren, virtuelle Funktionsaufrufe und Funktionszeigeraufrufe.
Was bedeutet das alles für die Leistung? Wenn der Prozessor einen Verzweigungsbefehl in seiner Pipeline sieht, muss er herausfinden, wie er seine Pipeline weiter füllen kann. Um herauszufinden, welche Anweisungen nach der Verzweigung im Programmstrom kommen, muss es zwei Dinge wissen: (1) ob die Verzweigung genommen wird und (2) das Ziel der Verzweigung. Dies herauszufinden heißt Verzweigungsvorhersage bezeichnet und ist ein herausforderndes Problem. Wenn der Prozessor richtig vermutet, wird das Programm mit voller Geschwindigkeit fortgesetzt. Wenn der Prozessor stattdessen falsch vermutet , hat er nur einige Zeit damit verbracht, das Falsche zu berechnen. Es muss nun seine Pipeline leeren und sie mit Anweisungen aus dem richtigen Ausführungspfad neu laden. Fazit: ein großer Performance-Hit.
Der Grund, warum Aussagen teuer sind, liegt in falschen Vorhersagen der Branche . Dies ist nur auf der niedrigsten Ebene. Wenn Sie Code auf hoher Ebene schreiben, müssen Sie sich um diese Details überhaupt nicht kümmern. Sie sollten sich nur darum kümmern, wenn Sie extrem leistungskritischen Code in C oder Assembly schreiben. In diesem Fall kann das Schreiben von verzweigungsfreiem Code dem verzweigten Code häufig überlegen sein, selbst wenn mehrere weitere Anweisungen erforderlich sind. Es gibt ein paar coole Bit-twiddling Tricks , die Sie Dinge wie berechnen tun können abs()
, min()
und max()
ohne Verzweigung.