Stand der Entwicklung von No-GIL-CPython
(lwn.net)- Der Python Steering Council hat seine Absicht erklärt, PEP 703 zu genehmigen, wodurch der GIL über mehrere Releases hinweg optional werden soll; die endgültigen Bedingungen werden noch abgestimmt
- Der
--disable-gil-Build von CPython 3.13 wird experimentell vorbereitet; die stable ABI und die Wheel-Kompatibilität von Erweiterungsmodulen kristallisieren sich als größte technische Streitpunkte heraus - Bestehende
abi3-Wheels passen möglicherweise nicht unverändert zu No-GIL-CPython 3.13; diskutiert werden die Einführung vonabi4, Änderungen an der eingeschränkten C-API und die Umstellung von Referenzzählungs-Makros auf Funktionsaufrufe - Es gibt Bedenken, dass
pipfälschlicherweise Wheels für GIL-Builds bzw. No-GIL-Builds auswählen könnte; eine stille Fehlinstallation wäre gefährlicher als ein einfacher Installationsfehler - Für die Bezeichnung des No-GIL-Builds wurde statt
nogilfree-threading vorgeschlagen, doch dabei müssen auch ausführbare Dateinamen, Shebangs, parallele Installationen und das Packaging durch Distributionen geklärt werden
PEP 703 und der aktuelle Stand von No-GIL-CPython
- Der Python Steering Council hat Ende Juli seine Absicht erklärt, PEP 703 zu genehmigen; dieses PEP sieht vor, den Global Interpreter Lock (GIL) in CPython optional zu machen
- Die detaillierten Genehmigungsbedingungen sind noch nicht endgültig festgelegt, doch die zugehörigen Implementierungsdiskussionen und die Vorbereitung des Ökosystems laufen bereits
- Langfristig wird ein Weg zu einer einzigen CPython-Version ohne GIL angenommen; vorerst befindet man sich jedoch in einer Phase, in der das No-GIL-Verhalten mit einem per
--disable-gilgebauten Interpreter erprobt wird - CPython 3.13 ist für Oktober 2024 geplant, und der No-GIL-Build dieser Version hat experimentellen Charakter
stable ABI und Kompatibilität von Erweiterungsmodulen
- Sam Gross behandelte im Python Discussion Forum, wie PEP 703 und die stable ABI von CPython zusammenpassen
- Die stable ABI dient dazu, dass Erweiterungsmodule mit demselben binären Wheel über mehrere CPython-Versionen hinweg funktionieren, sodass sie nicht für jedes neue CPython-Release neu gebaut werden müssen
- Erweiterungen, die für die stable ABI gebaut wurden, funktionieren in einem No-GIL-Build von CPython 3.13 möglicherweise nicht unverändert
- Um dieses Problem zu lösen, wurden einige Ergänzungen und Änderungen an der eingeschränkten C-API vorgeschlagen
- Erweiterungen, die nur die eingeschränkte C-API verwenden, können Binärdateien erzeugen, die die stable ABI nutzen
- Zu den Änderungsvorschlägen gehört der bereits bestehende Plan, einige Makros zum Erhöhen und Verringern der Objekt-Referenzzählung auf Funktionsaufrufe umzustellen
- Ziel ist es, Erweiterungs-Binaries zu ermöglichen, die sowohl mit GIL-Builds als auch mit No-GIL-Builds funktionieren
abi3, abi4 und das Problem der Wheel-Auswahl
- Victor Stinner sieht für einen Erfolg des No-GIL-Experiments eine einfache Lösung für Erweiterungen als nötig an, die mit beiden Interpreter-Typen funktionieren
- Da Erweiterungen, die unter CPython 3.12 oder älter für die stable ABI gebaut wurden, nicht mit No-GIL-Builds ab 3.13 kompatibel sind, kam der Vorschlag auf, eine neue ABI-Version namens abi4 einzuführen
- Die aktuelle stable ABI ist
abi3 - Die ABI-Nummer und die CPython-Hauptversionsnummer sind nicht zwingend miteinander verknüpft
- Die aktuelle stable ABI ist
- Gross meint, dass Erweiterungen, die No-GIL unterstützen wollen, die Belastung durch das Erstellen zweier binärer Wheels bis zu einem gewissen Grad akzeptieren können
- Größere Sorge bereitet ihm, dass das No-GIL-Projekt zu stark an Verbesserungen der C-API und der stable ABI gebunden wird
- Auch Alex Gaynor hat mehrere
abi3-Wheel-Pakete, hält es aber nicht für eine übermäßig große Belastung, einmal zwei Wheels zu erstellen- Wichtig sei jedoch, ob bestehende und künftige
pip-Versionen das jeweils passende Wheel auswählen
- Wichtig sei jedoch, ob bestehende und künftige
- Brett Cannon ist der Ansicht, dass die aktuelle
pip-Logik die beiden Versionen nicht unterscheiden kann; ohne eine Änderung wieabi4würden bestehende und altepip-Versionen daher nicht korrekt funktionieren
Sorge vor stillem Fehlverhalten von pip
- Gross meint, dass man sich beim experimentellen
--disable-gil-Build von CPython 3.13 nicht allzu große Sorgen um die Unterstützung alterpip-Versionen machen müsse- Als Grund nannte er, dass alte
pip-Versionen bei neuen Python-Versionen häufig kaputtgehen - Als Beispiel führte er an, dass
pip==23.1.1und älter unter CPython 3.13 wegen des fehlendenpkgutil.ImpImporterbrechen
- Als Grund nannte er, dass alte
pip-Maintainer Paul Moore sieht jedoch einen Unterschied zwischen explizitem Scheitern und stiller Installation eines falschen Pakets- Es gibt Nutzer, die alte
pip-Versionen verwenden - Ein expliziter Fehler und ein stiller Fehler wirken sich unterschiedlich auf Nutzer aus
- Es gibt Nutzer, die alte
- Moore befürchtet, dass Nutzer, die No-GIL- oder free-threaded Builds ausprobieren wollen, entmutigt werden könnten, wenn sie ABI-Kompatibilitätsprobleme debuggen müssen
- Auch Gaynor meint, dass eine Flut von Issues entstehen könnte, wenn
pipbei betroffenen Paketen stillschweigend falsch arbeitet
Parallele Installation und Namen ausführbarer Dateien
- Barry Warsaw fragte, ob es einen Plan gebe, GIL-Builds und No-GIL-Builds gemeinsam auf demselben System zu installieren
- Gross antwortete, diese Situation entspreche der Installation verschiedener Python-Versionen
- Cannon hält auch die Möglichkeit für denkbar, zwei Binaries in ein einziges „fat“ Wheel zu packen
- Allerdings müssten die Binärdateien im Wheel unterschiedliche Namen haben
- Die Diskussion über Namen ausführbarer Dateien wurde in einem separaten Thread fortgesetzt
- Paul Moore meint, Nutzer müssten den No-GIL-Modus leicht testen und einfach zwischen GIL und No-GIL wählen können
- Wenn dieser Prozess unter Windows, macOS, Linux usw. schwierig sei, könne sich das negativ auf das No-GIL-Projekt auswirken
- Nur wenn Nutzer es leicht ausprobieren können, entstehe Nachfrage nach No-GIL-Builds, und damit auch Druck auf Paket-Maintainer, No-GIL-kompatible Wheels bereitzustellen
Namensdebatte um nogil und free-threading
- Barry Scott sieht den Namen der ausführbaren Datei als wichtig an, weil in Shebang-Zeilen angegeben werden muss, welcher Interpreter aufgerufen werden soll
- Als Beispiele nannte er Namen wie
python-nogil3undpython-nogil3.13
- Als Beispiele nannte er Namen wie
- Gregory P. Smith äußerte seine persönliche Meinung, dass der No-GIL-Build von CPython 3.13 eine experimentelle Funktion sei und Distributionen ihn daher nicht in den standardmäßigen
$PATHaufnehmen sollten- Er hält es auch für unerwünscht, dass lange Namen ausführbarer Dateien in Shebangs verbleiben und lange weiterleben
- Er schlug vor, die Entscheidung über Installationsnamen auf 3.14 oder später zu verschieben
- Fedora-Entwickler Petr Viktorin wies darauf hin, dass Distributionen wahrscheinlich einen No-GIL-Interpreter paketieren wollen, damit Nutzer experimentieren können
- Moore möchte einen free-threaded Build in einer Form wie
#!/usr/bin/env python3.13-nogilangeben können- Dahinter steht der Wunsch, keine langen und wenig intuitiven Pfade hardcoden zu müssen
- In einem von Steve Dower gestarteten Thread zum Windows-Installer sagte Smith, der Steering Council wolle die Bezeichnung
nogilvermeiden- Als Gründe nannte er, dass sie den meisten Non-Core-Entwicklern nicht gut vermittelt werde, dass man nicht wissen müsse, was der GIL ist, und dass sie eine negative Formulierung enthalte
- Als Alternative wurde der Begriff free-threading vorgeschlagen
- Gross hält auch
free-threadingfür Außenstehende nicht leicht verständlich und nicht für einen weithin gebräuchlichen Begriff - In der tatsächlichen Diskussion gab es eine starke Präferenz für kurze Namen, und
nogilwar in dieser Hinsicht der stärkste Kandidat - Die konkret übernommene Änderung war, das ABI-Tag des No-GIL-Builds von
nauftzu änderntsteht für threading
abi4-Vorschlag und verbleibende Arbeit
- Gross und Viktorin diskutierten Problempunkte der vorgeschlagenen API-Änderungen, und dieses Feedback führte zum Vorschlag einer neuen ABI namens abi4
- Gross erstellte einen Prototyp der neuen ABI
- Viktorin stimmt dem Ansatz im Großen und Ganzen zu, meint aber, dass die Details noch weiter ausgearbeitet werden müssen
- Stinner hält ein PEP zu abi4 für notwendig, und Viktorin versteht dies als Pre-PEP-Diskussion
- Es gibt Verwirrung darüber, welche Kompatibilitätsgarantien die Kombination aus Version der eingeschränkten C-API und
abi3bietet; dieser Punkt beeinflusst auch die Richtung vonabi4 - Die entsprechenden Untersuchungen laufen weiter, und beim Core Developer Sprint Mitte Oktober könnte es persönliche Gespräche dazu geben
Endgültige Genehmigungsformulierung und langfristige Auswirkungen
- Die Arbeit an No-GIL- bzw. free-threaded CPython geht weiter, aber die endgültige Genehmigung von PEP 703 steht noch aus
- Die Verzögerung ist etwas länger geworden, doch PEP 703 und seine Folgewirkungen werden die Entwicklung von CPython und das Ökosystem in den kommenden fünf Jahren oder länger voraussichtlich stark beeinflussen
- Der Steering Council möchte die Genehmigungskriterien klarer fassen
- Thomas Wouters erklärte, er verfeinere die genaue Genehmigungsformulierung und wolle mehrere Entscheidungen klarstellen
- Ein Teil der Arbeit könnte auch beim Core Developer Sprint erfolgen
1 Kommentare
Meinungen auf Hacker News
Wenn man sich heutige Computer ansieht, bekommt man den Eindruck, dass explizite Parallelität ein grundlegenderes Element der Informatik sein könnte, als es in Lehrbüchern gerade en vogue ist.
Vielleicht sind wir inzwischen an dem Punkt, an dem man immer explizit parallelen Code schreiben muss.
Zum Beispiel werden
for-Schleifen durch Operationen wieforeach,mapundfilterersetzt. Solche Ausdrücke teilen dem Compiler/Interpreter die Absicht mit, eine bestimmte Operation auf alle Elemente einer Datenstruktur anzuwenden, und überlassen dem Compiler/Runtime, ob und wie parallelisiert wird.Beim Ausführen von Webdiensten ist jede einzelne Anfrage schnell genug, und der eigentliche Nutzen von Parallelität liegt darin, viele Anfragen nebeneinander zu verarbeiten. Hier passt No-GIL gut hinein.
Wenn es innerhalb einer einzelnen Anfrage viele Unteranfragen gibt, wird das meist mit asynchronem Code gelöst; häufig aber weniger wegen der Performance-Vorteile von Async, sondern weil das Erzeugen von Threads teuer ist oder Thread-Pools umständlich sind. Async ist gut für den Durchsatz, aber schlecht für die Latenz; und wenn man Service-Anfragen parallelisiert, macht man sich normalerweise mehr Sorgen um die Latenz. Async hat sich vor allem wegen der Bedienbarkeit durchgesetzt.
Eine andere Form von Parallelität sieht man bei großen Offline-Jobs. Dinge wie MapReduce oder Presto; sie sehen im Großen und Ganzen wie Divide-and-Conquer-Probleme aus. GPU-Modelltraining ist ähnlich.
Was nicht passiert ist, sind lokale, hochgradig parallele Algorithmen. Bei Webdiensten sind die Datenmengen klein, sodass der Latenzgewinn gering ist, die Implementierung komplex wird und die Koordinationskosten zwischen Threads steigen. Eine kleine Ausnahme sind vektorisierte Algorithmen, aber diese laufen auf einem Kern und haben keinen Koordinations-Overhead; auch Online-Inferenz ist wiederum sehr stark vektorisiert.
Mit der Zeit wird beides besser. So wie mehr Sprachen und Libraries standardmäßig sicherer werden, ist inzwischen auch mehr standardmäßig parallel. Es ist noch ein weiter Weg, aber ich finde es gut, dass wir es nicht zu früh gemacht haben. In den letzten zehn Jahren ist die Technik deutlich besser geworden.
Man kann zum Beispiel vergleichen, was sich mit Rayon in Rust sicher machen lässt, mit dem, was man früher unsicher mit OpenMP in C++ gemacht hat.
Weiter außen gibt es auch diese Dinge, an denen ich arbeite: https://legion.stanford.edu/, https://regent-lang.org/, https://github.com/nv-legate/cunumeric
Da es ein Implementierungsdetail ist, sollte man es abstrahieren, wenn es sich dadurch leichter nutzen lässt.
Zum Vergleich: Ein Mutex liegt bei etwa 25 Nanosekunden und wird bei Contention langsamer, aber ein Mutex ist Punkt-zu-Punkt-Synchronisation.
Das Gute am Disruptor ist, dass mehrere Threads dieselbe Nachricht ohne großen Zusatzaufwand empfangen können.
https://github.com/LMAX-Exchange/disruptor/wiki/Performance-...
https://gist.github.com/rmacy/2879257
Ich träume von einer Sprache, die Smalltalk ähnelt, aber single-threaded bleibt, bis Parallelisierung sinnvoll ist.
Ich suche nach Parallelitätsproblemen, die nichts mit Big Data zu tun haben. Parallelität ist eher so, als würde man mehr Autos auf die Straße bringen, statt ein Auto schneller zu machen. Aber ich bin noch auf der Suche danach, was Desktop- oder Mobile-Nutzer lokal tun müssen, wofür sie die mathematische Leistung ihres Computers ausnutzen.
Als Parallelitätsideen denke ich auch über Itanium- und VLIW-Architekturen nach.
Man könnte
-ngverwenden. Als Bedeutung: no-gil oder next-generation.Es gab neue Compiler-Flags, neue Linker-Flags, das Linken anderer Libraries und sogar die Verwendung komplett anderer Compiler-Befehle. AIX war da besonders so.
Beim Shebang-Problem sollte man sich wohl besser auf bestehende Python-Konventionen stützen:
from __future__ import nogilAn diesem Punkt könnte man den Interpreter hotswappen.
from __future__ importist keine Runtime-Anweisung, sondern eine besondere Anweisung, die ein Flag markiert.https://docs.python.org/3/reference/simple_stmts.html#future...
Future-Anweisungen gelten pro Modul, und GIL/no-GIL passt nicht leicht in dieses Modell.
Jedes Mal, wenn ich diesen Vorschlag sehe, frage ich mich, wie sichergestellt werden soll, dass Programme weiterhin korrekt funktionieren. Ein großer Teil des bestehenden multithreaded Python-Codes ist unsicher geschrieben
Ein besonderes Problem sind Data Races, die ich immer wieder in Codebasen verschiedener Unternehmen und in Open-Source-Projekten gesehen habe. Solche Programme gehen nur deshalb nicht kaputt, weil sie implizit darauf vertrauen, dass der GIL jeweils nur einem Thread die Ausführung erlaubt
Wenn der GIL verschwindet, werden solche Programme kaputtgehen. Python ist eine dynamisch typisierte Sprache, daher bezweifle ich sehr, dass es einen statischen Analyzer geben wird, der solche Probleme in bestehenden Python-Programmen finden kann
Wahrscheinlicher sind subtile Bugs, die zur Laufzeit nichtdeterministisch auftreten. Ein Crash wäre fast noch wünschenswert, aber diese Art von Bugs führt eher zu falschem Verhalten
Vielleicht ist dieser Vorschlag ohne GIL aber auch gar nicht für die meisten Programme gedacht. Möglicherweise ist es ein hochspezialisiertes Werkzeug für sehr wenige Situationen, in denen Programmierer wissen, dass es keinen GIL gibt, und entsprechend schreiben können
Der GIL bedeutet nur, dass immer nur ein Thread Python-Bytecode ausführen kann. Auch ein Interpreter mit GIL kann zwischen Bytecodes den Thread wechseln, und viele Python-Operationen benötigen mehrere Bytecodes. Dazu gehören auch eingebaute Methoden eingebauter Typen, die viele für „atomar“ halten
Deshalb bietet Python trotz des aktuellen GIL Dinge wie Locks, Mutexes und Semaphore an
Threads, die um den GIL konkurrieren, können sich schon jetzt zu ungünstigen Zeitpunkten gegenseitig den GIL wegnehmen und Chaos verursachen
Wenn ein Programm nur dann ohne GIL läuft, wenn alle Abhängigkeiten es erlauben, dürfte genug Zeit bleiben, solche Bugs zu beheben
Damit wird dieses Problem in großem Maßstab wahrscheinlich erst gegen 2030 angegangen. Ich sehe auch kaum Produktionsumgebungen, die ihre Runtime sofort auf das neueste Release heben
Ich will nicht hart klingen, aber der Steering Council hat gesagt, dass er keine weitere Migration wie von 2 auf 3 will, also werden die Leute nicht leichtfertig updaten. Vieles von dem, was derzeit online ist, könnte beim Kopieren und Einfügen gefährlich sein
In realem Python-Code gibt es sehr viele Threading-Bugs
Hat OCaml nicht eine ähnliche Entwicklung durchlaufen? Ich frage mich, ob es zwischen den beiden Projekten Vergleichspunkte gibt
Dadurch erzeugt die bestehende Thread-API Threads innerhalb der aktuellen Domain und kann Code isolieren, der erwartet, einen Lock zu halten. Neuer Code kann stattdessen eine neue Domain erstellen, die mit einem Thread startet. Man kann beides auch bewusst zusammen verwenden, als eine Form von Scheduling
Python versucht, den Lock global und außerhalb der Kontrolle von Bibliotheksautoren vollständig optional zu machen. Allerdings scheint Pythons Lock nur die Runtime selbst garantiert zu schützen; die meisten Codes, die sich auf diesen Lock verlassen, haben also wahrscheinlich ohnehin Bugs, weshalb auch Pythons Plan machbar wirkt
Eine Gemeinsamkeit wäre wohl, dass man in der gesamten Runtime-Codebasis unerwarteten gemeinsamen Zustand finden und beheben und das C-ABI überarbeiten muss
Jetzt hat Python die Chance, Tcl bei der Multithreading-Performance einzuholen: https://www.hammerdb.com/blog/uncategorized/why-tcl-is-700-f...
Ich würde Python-Code lieber nach Mojo portieren, um Multithreading, SIMD und andere Geschwindigkeitsgewinne zu bekommen
Du willst Python-Code nicht nach nogil Python migrieren? Dann gibt’s eben Downvotes
Wenn man Namen vergeben müsste, wären Dinge wie
python4,python3-gilfoil,python3-gilfreemöglichDer aktuelle Fokus auf Python ohne GIL fühlt sich ziemlich merkwürdig an. Das Faster-CPython-Team hatte sich das ehrgeizige Ziel gesetzt, die CPython-Performance mit jedem Release um 50 % zu steigern.
In 3.11 gab es tatsächlich Verbesserungen, aber von 50 % war das weit entfernt, und in vielen unserer Tests war 3.12 ähnlich schnell oder sogar langsamer. Echtes Multithreading wäre großartig, aber ich würde mir viel stärker wünschen, dass zuerst die Single-Thread-Performance besser wird.
Natürlich erkenne ich an, dass unsere Bedürfnisse nicht für alle stehen, und ich bin dankbar für all die Arbeit, die Python zu einer großartigen Sprache macht. Trotzdem frage ich mich, was mir entgeht.
Derzeit läuft die Nutzung mehrerer Kerne über multiprocessing, und das hat viele Einschränkungen. Ich verstehe, dass mehrere Interpreter zusammen mit Dingen wie Coroutines kommen könnten, aber eine echte Multithreading-Option wäre mir trotzdem lieber.
In nogil-Python können zum Beispiel mehrere Threads C-Code aufrufen und dabei gemeinsam genutzten Zustand verwenden, der als Python-Objekte zugänglich ist. Das ist für Machine Learning ziemlich zentral, und tatsächlich stammt die aktuelle Form dieses PEP vom PyTorch-Team.
Single-Thread-Performance ist ebenfalls wichtig, aber für kritische Abschnitte gab es bereits ziemlich gute Workarounds wie numba, Cython und Mojo.
Auch die Reihenfolge ist wichtig. Wenn nogil eingeführt wird, könnte ein erheblicher Teil der Faster-CPython-Arbeit komplett hinfällig werden, daher mussten sich die Teams abstimmen.
In einer idealen Welt gäbe es sowohl einen nogil-Modus als auch Verbesserungen der Single-Thread-Performance. Guido hat auch angedeutet, dass ein ausgefeilter JIT in Betracht gezogen wird.
Python macht es sehr bequem, Low-Level-Abstraktionen in einer höher angesiedelten Sprache zu handhaben. Deshalb habe ich mich als langjähriger Python-Entwickler wegen des GIL nie besonders gestresst gefühlt.
Wenn man sich nur für eines von beiden entscheiden müsste, stimme ich zu, dass schnellerer Single-Thread-Code für die meisten Anwendungsfälle einfach besser passt. Aber es gibt auch keinen Grund, warum man nicht beides haben sollte.
Im Rückblick ist es offensichtlich, aber wenn die Python-Seite gewusst hätte, wie langwierig und schmerzhaft der Übergang von 2 auf 3 werden würde, hätte sie vermutlich auch die Interna des Interpreters sehr viel umfassender überarbeitet.
Trotz einer 12-jährigen Migration ist die Single-Thread-Performance immer noch miserabel, und bis zu echtem Multithreading stehen noch einige schmerzhafte Übergänge bevor.
Ich weiß, dass man gegenüber Open-Source-Entwicklung wohlwollend sein sollte, aber ich frage mich, ab welchem Punkt man es eine sehr schlecht verwaltete Sprache nennen darf.
Die schlechtesten Teile von Python sind die, die schwer zu ändern sind, weil Python so populär ist und das Ökosystem so groß ist. Deshalb wird jede Art von Veränderung durch Abwärtskompatibilität erschwert.
Es gibt eine Tendenz, Python zu schnell zu verteidigen. Es ist wichtig, es unvoreingenommen und objektiv zu betrachten.
Projekte, die Performance und Python-Syntax wollen, könnten dann dorthin wechseln. Das aktuelle Python scheint zwischen mehreren Zielen zu straucheln und keines davon richtig zu erreichen.
Perl 5/6 wurde als Beispiel angeführt. Selbst als klar wurde, dass niemand migriert, dauerte es noch etwa fünf weitere Jahre bis zu Versuchen, es einfacher zu machen.