Brandt Bucher von Microsoft, einer der Hauptentwickler von CPython und Mitglied des Teams zur Leistungssteigerung des CPython-Interpreters, hat einen JIT-Compiler für Python veröffentlicht, der die Copy-and-Patch-Technik verwendet. Die JIT-Veröffentlichung fällt auf Weihnachten, und die Ankündigung ist in Versform verfasst.
Der vorgeschlagene JIT ist bemerkenswert durch sehr hohe Geschwindigkeiten bei der Codegenerierung, einfache Wartbarkeit und vollständige Integration mit dem Interpreter. Die vorgeschlagene Methode ermöglicht es, den in C geschriebenen Interpreter automatisch in einen JIT-Compiler umzuwandeln, ohne separate Logik für die Codegenerierung zu erstellen und ohne manuelle Erstellung von Assembler-Darstellungen. Bei diesem Ansatz führt das Beheben eines Fehlers im Interpreter automatisch auch zur Behebung des gleichen Problems im JIT, da ein gemeinsamer Codegenerator verwendet wird.
Die Funktionsweise der Copy-and-Patch-Methode beruht darauf, dass die Relokation von Code im Speicher beim Laden durch einen Linker von Objektdateien und das Ersetzen von Maschinenanweisungen durch Bytecode im JIT ähnliche Aufgaben sind. Während der JIT-Ausführung wird eine Iteration über die vom Interpreter erstellten Bytecode-Anweisungen vorgenommen, wobei für jede Anweisung der bereits kompilierte Maschinen-Code in den Bereich des ausführbaren Codes kopiert wird. Zudem werden die Anweisungen in Echtzeit modifiziert, um die verarbeiteten Daten einzusetzen (JIT kopiert vorgefertigte Vorlagen bereits kompilierter Funktionen und ersetzt die erforderlichen Werte wie Argumente und Konstanten). Auch beim Laden von Objektdateien wird Maschinen-Code in den Speicher kopiert und externe Symbole ersetzt.
Im Copy-and-Patch JIT wird mit LLVM eine Objektdatei im ELF-Format erstellt, die Daten über die Bytecode-Anweisungen und Informationen über die erforderliche Datenaustausch enthält. Der JIT ersetzt die während der Programmausführung erzeugten Bytecode-Anweisungen durch Darstellungen im Maschinen-Code und setzt dabei die für Berechnungen notwendigen Daten ein. Die Implementierung des JIT erfordert LLVM als Abhängigkeit during der Build-Phase, doch die Runtime-Komponenten sind nicht an externe Abhängigkeiten gebunden und bestehen aus etwa 300 manuell geschriebenen Zeilen in C und 3000 generierten Zeilen C-Code.
Im Vergleich zu traditionellen JIT-Tools (LLVM -O0) gewährleistet der vorgeschlagene JIT eine 100-mal schnellere Codegenerierung und einen um 15 % schnelleren resultierenden Code. Im Vergleich zur Kompilierung nach WebAssembly (Liftoff) generiert der neue JIT Code fünfmal schneller, und der resultierende Code funktioniert 50 % schneller. Vergleicht man den neuen JIT mit einem optimierenden JIT (LuaJIT), der manuell geschriebene Assembler-Code verwendet, so war der vorgeschlagene Ansatz in 13 von 44 Tests schneller, hatte jedoch im Durchschnitt eine 35 % geringere Leistung, was jedoch erhebliche Vereinfachungen bei der Wartung und einen geringeren Implementierungsaufwand mit sich brachte.

Quelle: opennet.ru
