Ich teste verschiedene Optimierungen in C / C ++ mit dem GCC-Compiler. Ich habe derzeit eine Schleife mit mehreren verschachtelten if-Anweisungen. Die Bedingungen werden zu Beginn der Programmausführung berechnet. Es sieht ungefähr so aus:
%Vor% Wobei doATrueStuff()
eine Inline-Funktion ist, die einige einfache numerische Berechnungen durchführt, so dass beim Aufruf kein Overhead entsteht.
Leider können die Bedingungen nicht vorher definiert werden, sie müssen zur Laufzeit berechnet werden. Wir können nicht einmal zuverlässig vorhersagen, ob sie wahr oder falsch sind. getA()
könnte auch rand()%2
sein. Aber einmal berechnet, ändert sich ihr Wert nie.
Es gibt zwei Lösungen, an die ich gedacht habe. Eine davon sind globale Funktionszeiger, mit denen die entsprechende Funktion in der Schleife aufgerufen wird:
%Vor%Auf diese Weise kann ich alle Zweige aus der Schleife entfernen, aber dann werde ich den Overhead von mehreren Funktionsaufrufen haben, die mich verlangsamen.
Oder ich könnte einfach eine andere Schleife für jede Kombination von Bedingungen haben, etwa so:
%Vor%Aber das ist viel weniger elegant und es wird unmöglich, dies effizient zu tun, sobald man zu viele Bedingungen hat, da man für X-Bedingungen 2 ^ X-Schleifen schreiben muss.
Gibt es eine elegante / schnellere Möglichkeit, dies zu optimieren?
Gibt es überhaupt irgendeinen Punkt in diesem oder wird der Compiler irgendwie verstehen, dass sich die Bedingung während der Schleife nicht ändert und sie selbst optimiert?
Und gibt es aus Neugier eine andere Programmiersprache, die das Schreiben solcher Codes einfacher / möglich macht? Oder wäre das nur möglich, wenn man die Anweisungen des Programms, nachdem es in den Speicher geladen wurde, mit Assembly ändert?
Die Theorie:
Wenn Sie versuchen, Ihren Code durch ein verrücktes Neuschreiben zu optimieren, kann es für den Compiler schwierig werden, die üblichen Optimierungen vorzunehmen. Der Compiler und auch der Prozessor können den Code mit 2 Techniken optimieren:
Als einfacher Entwickler, der gcc verwendet, können Sie die Verzweigungsvorhersage oder Codegenerierung mithilfe der "likely" und "unlikely" Kompilierungshinweise unterstützen. Überprüfen Sie hier für weitere Details. Dies könnte funktionieren, wenn Sie beispielsweise wissen, dass eine Bedingung wahrscheinlicher ist als eine andere.
Um die Effizienz der Verzweigungsvorhersage zu sehen, verwenden Sie perf stat ./binary und prüfen Sie die Verzweigungsfehlgeschlagenheit und die Anzahl der Verzweigungsfehlschläge für jede durchgeführte Optimierung.
In Ihrem Code-Fall:
Wenn conditionA, conditionB und conditionC vor der Schleife berechnet werden und sich nicht ändern, ist es für den Verzweigungsvorhersager leicht, das Muster zu erkennen. Der Prädiktor der CPU macht dies, indem er die letzten Äste, die er / sie nicht genommen hat, im Auge behält, und er verwendet den aufgezeichneten Verlauf, um die folgenden Äste vorherzusagen. Daher erwarte ich tatsächlich sehr wenig Leistungseinbußen aufgrund von Zweigen in Ihrem Code, die Sie wie oben bestätigen können.
Betrachten Sie Vorlagen. Die Herausforderung besteht darin, die Laufzeitwerte den Template-Parametern für die Kompilierung zuzuordnen. Der folgende Textbaustein ist eine Dispatch-Funktion pro Parameter, und der Compiler erstellt den Baum der Kombinationen für Sie. Nicht gerade elegant, aber viel besser skalierbar als eine Mehrparameter-Schaltanlage zu öffnen.
Sie können die Template-Parameter (oder deren Funktionen) auch direkt in Ihren Berechnungen verwenden, und diese werden ebenfalls optimiert, z. B. die Auswahl einer Konstanten auf der Basis eines Template-Parameters oder die Multiplikation einer 0 in einen Ausdruck du willst nichts beitragen.
%Vor%Tags und Links optimization c c++ gcc loops