- Tail Call: Ein Funktionsaufruf, der direkt vor der Rückgabe einer Funktion erfolgt. Wenn Tail-Call-Optimierung greift, wird der
jmp-Befehl verwendet, um den Aufruf-Stack zu verkleinern.
- Vorteile:
- Reduziert die Nutzung von Stack-Speicher von O(n) auf O(1).
- Beseitigt den Performance-Overhead von Funktionsaufrufen und kann als effiziente iterative Kontrollstruktur verwendet werden.
Probleme der Interpreter-Schleife
- Probleme:
- Je größer eine Funktion wird und je komplexer der Kontrollfluss ist, desto schwieriger ist es, wichtige Daten in Registern zu halten.
- Wenn schneller und langsamer Pfad vermischt werden, leidet die Code-Qualität.
Verbesserung der Interpreter-Schleife mit Tail Calls
- Lösung: Mit Tail Calls wird jede Aufgabe in kleine Funktionen aufgeteilt, und jede Funktion ruft die nächste Aufgabe per Tail Call auf.
- Vorteile:
- Die Registerzuweisung lässt sich steuern.
- Schneller und langsamer Pfad lassen sich trennen, sodass die Code-Qualität erhalten bleibt.
- Unabhängige Befehlssequenzen können optimiert werden.
Grenzen
- Probleme bei nicht-Tail-Calls: Wenn nicht-Tail-Calls vorhanden sind, werden Stack-Frames erzeugt und Daten auf dem Stack gespeichert, was die Performance verschlechtert.
- Komplexe Ausnahmebehandlung: Wenn die Ausnahmebehandlung komplex ist, nehmen Codeduplizierung und Komplexität zu.
- Portabilitätsproblem: Da das Attribut
musttail kein Standard ist, wird es nicht von allen Compilern unterstützt.
Zusammenfassung von GN⁺
- Tail-Call-Optimierung spielt eine wichtige Rolle für Performance-Verbesserungen und zeigt besonders beim Protobuf-Parsen große Wirkung.
- Diese Technik lässt sich auch auf wichtige in C geschriebene Sprachinterpreter anwenden, darunter Python, Ruby, PHP und Lua.
- Das Portabilitätsproblem des Attributs
musttail bleibt eine offene Aufgabe.
- Projekte mit ähnlicher Funktionalität sind unter anderem LuaJIT und der WebAssembly-Interpreter wasm3.
1 Kommentare
Hacker-News-Kommentare
Ein Vorschlag für den C-Standard enthält Tail Calls in der Form
return goto (expression);[[musttail]]ist dadurch die Lebensdauer lokaler Objekte garantiert, sodass keine umfassende Escape-Analyse nötig istFür Rust-Enthusiasten gab es ein altes RFC zur Einführung des Schlüsselworts
becomeIn C++ werden Interpreter meist mit computed goto beschleunigt
Das Problem beim Wechseln des Kontexts mit Tail Calls ist, dass eine Funktion benötigt wird, die die Calling Convention verwendet
Es wird gehofft, dass sich das Attribut
[[musttail]]auf GCC, Visual C++ und andere populäre Compiler ausbreitet[[musttail]]befindet sich im Prozess der Aufnahme in GCCMit Verweis auf die C++-Unterstützung wird angemerkt, dass es in C++ kaum Tail Calls gibt
Es wird gefragt, was passiert, wenn in einer C++-
[[musttail]]-Funktion eine Ausnahme geworfen wirdEs wird angemerkt, dass einfache Beispiele für gute Code-Erzeugung kein
__attribute__((musttail))benötigenEs wird gefragt, wie schnell der Ansatz ist, bei dem ein per Trampolin zurückgegebener Funktionszeiger in einer äußeren Schleife aufgerufen wird
Es gibt die Bitte, ein Beispiel für mit
[[musttail]]umhüllte Ausnahme-Pfade klarer zu machen[[musttail]]das Anlegen von Stack Frames und das Spilling von Registern verhindert