Code-Optimierung ist die Compiler-Phase, die ein übersetztes Programm effizienter macht — also schneller, kleiner oder energieärmer — ohne dass sich das beobachtbare Verhalten ändert. Sie arbeitet meist auf der Zwischendarstellung (IR), teilweise aber auch direkt auf dem Maschinencode. Zusammen mit der Codegenerierung bildet sie das Compiler-Backend.
Die as-if-Regel
Die wichtigste Grenze jeder Optimierung ist die as-if-Regel: Der Compiler darf das Programm beliebig umformen, solange das Ergebnis sich so verhält, als wäre der ursprüngliche Code ausgeführt worden. Was „beobachtbar“ ist, definiert die Sprachspezifikation — für C und C++ etwa den Zugriff auf volatile-Variablen, Ein-/Ausgabe und Systemaufrufe.
Klassische Optimierungen
- Konstantenfaltung:
2 + 3wird schon beim Übersetzen zu5. - Konstantenpropagierung: Der bekannte Wert einer Konstante wird an allen Verwendungsstellen eingesetzt.
- Tote-Code-Eliminierung: Berechnungen, deren Ergebnis nie benutzt wird, verschwinden komplett.
- Gemeinsame Unterausdrücke (CSE): Derselbe Teilausdruck wird nur noch einmal berechnet und wiederverwendet.
- Schleifenoptimierungen: Invarianten werden aus der Schleife gezogen (loop invariant code motion), Schleifen werden abgerollt (unrolling) oder vektorisiert, damit mehrere Elemente pro Durchlauf verarbeitet werden.
- Inlining: Kleine Funktionen werden an der Aufrufstelle direkt eingefügt — der Aufruf-Overhead entfällt.
- Endrekursion: Tail Calls werden in Sprünge umgewandelt statt neue Stack-Frames anzulegen.
- Strength Reduction: Teure Operationen werden durch billige ersetzt, etwa Multiplikation mit 2 durch ein Linksschieben.
Viele dieser Verfahren bauen auf einer Datenflussanalyse auf (zum Beispiel Liveness-Analyse oder Reaching Definitions) und profitieren stark von der SSA-Form der Zwischendarstellung, bei der jede Variable genau einmal zugewiesen wird.
Wo die Optimierung passiert
Ein großer Teil läuft auf der plattformunabhängigen IR (zum Beispiel LLVM IR) — so profitiert jedes Ziel-Backend. Maschinennah kommen noch Verfahren wie die Peephole-Optimierung hinzu, die kleine Codemuster im erzeugten Maschinencode verbessert (beispielsweise mov r1, r1 entfernt), sowie die Registerallokation, die Werte geschickt auf CPU-Register verteilt.
Optimierungsstufen
Compiler wie GCC und Clang bieten Stufen von -O0 (keine Optimierung, schnelles Übersetzen, gut debugbar) bis -O3 (aggressive Optimierung) sowie -Os (kleiner Code). Mehr Optimierung kostet Übersetzungszeit und erschwert das Debugging — deshalb wählt man die Stufe je nach Einsatzzweck. Die Grenze des Machbaren ist oft die NP-Schwere der zugrunde liegenden Probleme; echte Compiler verwenden daher Heuristiken statt perfekter Lösungen.
Verwandte Compiler-Grundlagen: Compiler, Interpreter, lexikalische Analyse, semantische Analyse, Zwischendarstellung, Codegenerierung, Typsystem.