PEP 774 – Entfernen der LLVM-Abhängigkeit für JIT-Builds
- Autor:
- Savannah Ostrowski <savannah at python.org>
- Discussions-To:
- Discourse thread
- Status:
- Verschoben
- Typ:
- Standards Track
- Erstellt:
- 27. Januar 2025
- Python-Version:
- 3.14
- Post-History:
- 27. Januar 2025
- Resolution:
- 14-Mrz-2025
Zusammenfassung
Seit Python 3.13 kann CPython mit einem experimentellen Just-in-Time (JIT)-Compiler über das Flag --enable-experimental-jit unter Linux und Mac und --experimental-jit unter Windows konfiguriert und gebaut werden. Um CPython mit aktiviertem JIT zu bauen, müssen Benutzer LLVM auf ihrer Maschine installiert haben (anfänglich mit LLVM 16, aber neuerdings mit LLVM 19). LLVM ist für die Generierung von Schablonen verantwortlich, die für unseren Copy-and-Patch-JIT unerlässlich sind (siehe PEP 744). Diese Schablonen sind vordefinierte, architekturabhängige Vorlagen, die zur Laufzeit zur Generierung von Maschinencode verwendet werden.
Diese PEP schlägt vor, die LLVM-Build-Zeit-Abhängigkeit für JIT-aktivierte Builds zu entfernen, indem die generierten Schablonen im CPython-Repository gehostet werden. Dieser Ansatz ermöglicht es uns, die eingecheckten Schablonen für unterstützte Plattformen zur Build-Zeit zu nutzen, was die Erfahrung der Mitwirkenden vereinfacht und Bedenken aufgreift, die beim Python Core Developer Sprint im September 2024 geäußert wurden. Dennoch gibt es einen klaren Kompromiss zu berücksichtigen, da eine verbesserte Entwicklererfahrung mit erhöhter Repository-Größe einhergeht.
Es ist wichtig zu beachten, dass diese PEP kein Vorschlag zur Annahme oder Ablehnung des JIT selbst ist, sondern dazu dient zu bestimmen, ob die Build-Zeit-Abhängigkeit von LLVM für JIT-Builds akzeptabel ist. Wenn diese PEP abgelehnt wird, werden wir mit dem Status quo fortfahren und die LLVM-Build-Zeit-Anforderung beibehalten. Während diese Abhängigkeit den JIT-Entwicklungsprozess bisher effektiv unterstützt hat, führt sie zu Komplexität bei der Einrichtung und zusätzlichen Herausforderungen, die diese PEP zu mildern versucht.
Motivation
Auf dem Python Core Developer Sprint im September 2024 gab es eine Diskussion über die nächsten Schritte für den JIT – eine ähnliche Diskussion fand auch auf GitHub statt. Im Rahmen dieser Diskussion gab es auch eine klare Bereitschaft, die LLVM-Anforderung für JIT-Builds zu entfernen, um den JIT in 3.14 standardmäßig auszuliefern. Der Konsens auf dem Sprint war, dass es ausreichen würde, vorab generierte Schablonen für Nicht-Debug-Builds für Tier-1-Plattformen bereitzustellen, und dass das Einchecken dieser Dateien in das CPython-Repository für die begrenzte Anzahl von Plattformen ausreichend wäre (obwohl weitere Optionen geprüft wurden; siehe Abgelehnte Ideen).
Derzeit erfordert das Erstellen von CPython mit dem JIT LLVM als Build-Zeit-Abhängigkeit. Obwohl diese Abhängigkeit für Endbenutzer nicht sichtbar ist, ist sie suboptimal. Die Anforderung von LLVM stellt eine zusätzliche Einrichtungshürde für Entwickler und diejenigen dar, die CPython mit aktiviertem JIT bauen möchten. Abhängig vom Betriebssystem kann sich die von Ihrem Betriebssystem gelieferte LLVM-Version von der für unsere JIT-Builds erforderlichen unterscheiden, was zusätzliche Komplexität bei der Fehlersuche und -behebung mit sich bringt. Da nur wenige Kernentwickler derzeit zum JIT beitragen und diesen pflegen, möchten wir auch sicherstellen, dass die Hürden für die Arbeit an JIT-bezogenem Code so gering wie möglich gehalten werden.
Mit dem vorgeschlagenen Ansatz können vorab kompilierte Schablonen für unterstützte Architekturen im Voraus generiert, an einem zentralen Ort gespeichert und automatisch während der Builds verwendet werden. Dieser Ansatz gewährleistet reproduzierbare Builds und macht den JIT zu einem stabileren und nachhaltigeren Bestandteil der Zukunft von CPython.
Begründung
Diese PEP schlägt vor, JIT-Schablonen direkt in das CPython-Repository einzuchecken, als den besten Weg, um unsere Build-Zeit-Abhängigkeit von LLVM zu beseitigen.
Dieser Ansatz
- Bietet die beste End-to-End-Erfahrung für diejenigen, die CPython mit dem JIT bauen möchten
- Senkt die Einstiegshürde für diejenigen, die zum JIT beitragen möchten
- Stellt sicher, dass Builds plattformübergreifend reproduzierbar und konsistent bleiben, ohne auf externe Infrastruktur oder Download-Mechanismen angewiesen zu sein
- Eliminiert die durch Netzwerkbedingungen oder potenzielle Abweichungen zwischen gehosteten Dateien und dem Zustand des CPython-Repositorys eingeführte Variabilität und
- Unterwirft die Schablonen denselben Überprüfungsprozessen wie alle anderen JIT-bezogenen Codes
Dieser Ansatz führt jedoch zu einer leichten Erhöhung der Repository-Größe. Vergleicht man das Repository-Wachstum bei Commits der letzten 90 Tage, so beträgt der Unterschied zwischen den tatsächlichen Commits und denselben Commits mit hinzugefügten Schablonen 0,03 MB pro Schablonendatei. Dies ist eine geringe Erhöhung im Kontext der Gesamtgröße des Repositorys, das im gleichen Zeitraum um 2,55 MB gewachsen ist. Für sechs Schablonendateien ergibt sich daraus eine Obergrenze von 0,18 MB. Die aktuelle Gesamtgröße der Schablonendateien für alle sechs Plattformen beträgt 7,2 MB. [1]
Diese Schablonen könnten in Zukunft mit Änderungen bei der Registerallokation größer werden, was 5-6 Varianten pro Anweisung in jeder Schablonendatei einführen würde (5-6x größer). Wenn wir uns jedoch für diesen Weg entscheiden, könnten wir zusätzliche Modifikationen an den Schablonendateien vornehmen, um diese Größensteigerung auszugleichen (z. B. Kommentare entfernen, die Schablonen minimieren).
Spezifikation
Diese Spezifikation beschreibt die vorgeschlagenen Änderungen zur Entfernung der Build-Zeit-Abhängigkeit von LLVM und die Erfahrung der Mitwirkenden, wenn diese PEP angenommen wird.
Repository-Änderungen
Das CPython-Repository würde nun die vorab kompilierten JIT-Schablonen in einem neuen Unterverzeichnis in Tools/jit namens stencils/ hosten. Derzeit wird der JIT für sechs Plattformen getestet und gebaut, daher würden wir zunächst sechs Schablonendateien einchecken. Zukünftig könnten wir zusätzliche Schablonendateien einchecken, wenn Unterstützung für zusätzliche Plattformen gewünscht oder relevant ist.
cpython/
Tools/
jit/
stencils/
aarch64-apple-darwin.h
aarch64-unknown-linux-gnu.h
i686-pc-windows-msvc.h
x86_64-apple-darwin.h
x86_64-pc-windows-msvc.h
x86_64-pc-linux-gnu.h
Workflow
Die Workflow-Änderungen lassen sich in zwei Teile aufteilen, nämlich das Erstellen von CPython mit aktiviertem JIT und das Arbeiten an der Implementierung des JIT.
CPython mit dem JIT erstellen
Vorkompilierte JIT-Schablonendateien werden im Verzeichnis Tools/jit/stencils gespeichert, wobei jeder Dateiname seinem Ziel-Triple entspricht, wie oben dargelegt. Zur Build-Zeit ermitteln wir, ob die eingecheckten Schablonen verwendet oder eine neue Schablone für die Plattform des Benutzers generiert werden soll. Insbesondere für Mitwirkende mit installiertem LLVM ermöglicht das Skript build.py in Tools/jit/stencils die Neuerstellung der Schablone für ihre Plattform. Personen ohne LLVM können sich direkt auf die vorkompilierten Schablonendateien aus dem Repository verlassen.
Arbeiten an der Implementierung des JIT (oder Berühren von JIT-Dateien)
In der kontinuierlichen Integration (CI) werden Schablonendateien automatisch validiert und aktualisiert, wenn Änderungen an JIT-bezogenen Dateien vorgenommen werden. Wenn ein Pull-Request geöffnet wird, der diese Dateien berührt, wird der Workflow jit.yml, der unsere Builds erstellt und testet, wie gewohnt ausgeführt.
Als Teil dessen führen wir jedoch einen neuen Schritt ein, der die aktuellen Schablonen im Repo gegen die in der CI generierten abgleicht. Wenn es eine Differenz für die Schablonendatei einer Plattform gibt, wird eine Patchdatei für die aktualisierte Schablone generiert und der Schritt schlägt fehl. Jeder Patch wird zu GitHub Actions hochgeladen. Nachdem die CI auf allen Plattformen abgeschlossen ist, werden die Patches zur Bequemlichkeit zu einer einzigen Patchdatei aggregiert. Sie können diesen aggregierten Patch herunterladen, lokal anwenden und die aktualisierten Schablonen zurück in Ihren Branch committen. Dann wird der nachfolgende CI-Lauf erfolgreich sein.
Referenzimplementierung
Schlüsselkomponenten der Referenzimplementierung umfassen
.github/workflows/jit.yml: Der CI-Workflow, der für die Generierung von Schablonen-Patches verantwortlich ist.Tools/jit/stencils: Das Verzeichnis, in dem die Schablonen gespeichert sind.Tools/jit/_targets: Der Code zum Kompilieren und Parsen der Vorlagen zur Build-Zeit.
Wenn man die Schablonen selbst und notwendige JIT-README-Änderungen außer Acht lässt, sind die Änderungen am Quellcode zur Unterstützung der reproduzierbaren Schablonengenerierung und -speicherung minimal (etwa 150 Zeilen Änderungen).
Abgelehnte Ideen
Mehrere alternative Ansätze wurden im Rahmen der Forschung und Erkundung für diese PEP in Betracht gezogen. Die unten aufgeführten Ideen beinhalten jedoch entweder Infrastrukturkosten, Wartungsaufwand oder eine schlechtere Gesamterfahrung für Entwickler.
Verwenden von Git-Submodulen
Git-Submodule bieten eine schlechte Entwicklererfahrung für das Hosting von Schablonen, da sie eine andere Art unerwünschter Reibung erzeugen. Zum Beispiel würden alle Updates am JIT das Neuerstellen der Schablonen und deren Committen in ein separates Repository erfordern. Dies führt zu einem komplizierten Prozess: Sie müssen die Schablonen im Submodul-Repository aktualisieren, diese Änderungen committen und dann die Submodul-Referenz im Haupt-CPython-Repository aktualisieren. Diese Trennung fügt unnötige Komplexität und Overhead hinzu, was den Prozess für Mitwirkende und Wartende fehleranfällig und brüchig macht.
Verwenden von Git-Subtrees
Bei der Verwendung von Subtrees wird das eingebettete Repository Teil des Haupt-Repositorys, ähnlich dem, was in dieser PEP vorgeschlagen wird. Subtrees erfordern jedoch zusätzliche Werkzeuge und Schritte für die Wartung, was die Workflows unnötig verkompliziert.
Hosting in einem separaten Repository
Während die Aufteilung von JIT-Schablonen in ein separates Repository den Speicheraufwand für das Hosten der Schablonen vermeidet, erhöht es die Komplexität des Build-Prozesses. Zusätzliche Werkzeuge wären erforderlich, um die Schablonen abzurufen und potenziell zusätzliche und unnötige Fehlerpunkte im Workflow zu schaffen. Diese Trennung erschwert auch die Sicherstellung der Konsistenz zwischen den Schablonen und dem CPython-Quellbaum, da Updates über die Repositories hinweg koordiniert werden müssen.
Hosting in Cloud-Speicher
Das Hosten von Schablonen in Cloud-Speichern wie S3-Buckets oder GitHub Raw Storage führt zu externen Abhängigkeiten und erschwert Offline-Entwicklungsworkflows. Je nach Anbieter sind solche Hoster auch mit zusätzlichen Kosten verbunden, die wir vermeiden möchten.
Verwenden von Git LFS
Git Large File Storage (LFS) fügt eine Werkzeugabhängigkeit für Mitwirkende hinzu, was den Entwicklungsworkflow erschwert, insbesondere für diejenigen, die Git LFS noch nicht verwenden. Git LFS funktioniert nicht gut mit Offline-Workflows, da LFS-verwaltete Dateien beim Auschecken spezifischer Commits eine Internetverbindung zum Abrufen benötigen, was selbst grundlegende Git-Workflows stört. Git LFS hat ein kostenloses Kontingent, aber es gibt zusätzliche Kosten für die Überschreitung dieses Kontingents, die ebenfalls unerwünscht sind.
Beibehalten des Status quo mit LLVM als Build-Zeit-Abhängigkeit
Die Beibehaltung von LLVM als Build-Zeit-Abhängigkeit aufrechterhält die bestehenden Barrieren für Adoption und Beitrag. Letztendlich adressiert diese Option nicht die Kernprobleme der Zugänglichkeit und Einfachheit und beseitigt nicht die Abhängigkeit, die auf dem Python Core Developer Sprint im Herbst als unerwünscht erachtet wurde (der Anstoß für diese PEP), was sie zu einer schlechten langfristigen Lösung macht.
Fußnoten
Urheberrecht
Dieses Dokument wird in die Public Domain oder unter die CC0-1.0-Universal-Lizenz gestellt, je nachdem, welche Lizenz permissiver ist.
Quelle: https://github.com/python/peps/blob/main/peps/pep-0774.rst
Zuletzt geändert: 26. Mai 2025 16:58:47 GMT