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 + 3 wird schon beim Übersetzen zu 5.
  • 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.