- Le Python steering council a indiqué son intention d’approuver la PEP 703, qui rend le GIL optionnel sur plusieurs versions, mais les conditions finales sont encore en cours d’ajustement
- Le build
--disable-gilde CPython 3.13 sera disponible à titre expérimental, et la compatibilité avec la stable ABI et les wheels de modules d’extension apparaît comme le principal enjeu technique - Les wheels
abi3existantes pourraient ne pas être directement compatibles avec CPython 3.13 sans GIL ; l’introduction d’abi4, des modifications de l’API C limitée et la conversion des macros de comptage de références en appels de fonctions sont discutées - On craint que
pipchoisisse la mauvaise wheel entre les builds avec GIL et sans GIL ; une installation silencieusement incorrecte serait plus dangereuse qu’un simple échec d’installation - Pour nommer le build sans GIL, free-threading a été proposé à la place de
nogil, mais il faut aussi résoudre les questions de nom de l’exécutable, de shebang, d’installation en parallèle et de packaging par les distributions
Position actuelle de la PEP 703 et de CPython sans GIL
- Le Python steering council a annoncé fin juillet son intention d’approuver la PEP 703, qui vise à rendre le verrou global de l’interpréteur (GIL) optionnel dans CPython
- Les conditions détaillées d’approbation ne sont pas encore finalisées, mais les discussions d’implémentation et la préparation de l’écosystème sont déjà en cours
- À long terme, la trajectoire envisagée mène à une version unique de CPython sans GIL, mais, pour l’instant, il s’agit de tester le fonctionnement sans GIL dans un interpréteur compilé avec l’option
--disable-gil - CPython 3.13 est prévu pour octobre 2024, et le build sans GIL de cette version aura un caractère expérimental
Stable ABI et compatibilité des modules d’extension
- Sam Gross a abordé sur le forum de discussion Python la façon dont la PEP 703 s’articule avec la stable ABI de CPython
- La stable ABI sert à permettre aux modules d’extension de fonctionner sur plusieurs versions de CPython avec la même wheel binaire, sans devoir être recompilés à chaque nouvelle version de CPython
- Les extensions compilées pour la stable ABI pourraient ne pas fonctionner telles quelles avec le build sans GIL de CPython 3.13
- Pour résoudre cela, plusieurs ajouts et modifications de l’API C limitée sont proposés
- Les extensions qui n’utilisent que l’API C limitée peuvent produire des binaires utilisant la stable ABI
- Les changements envisagés incluent le plan déjà prévu de convertir certaines macros qui incrémentent ou décrémentent le compteur de références des objets en appels de fonctions
- L’objectif est de permettre des binaires d’extension capables de fonctionner à la fois avec les builds avec GIL et sans GIL
abi3, abi4 et le problème du choix des wheels
- Victor Stinner estime que, pour que l’expérimentation sans GIL réussisse, il faut une solution simple pour les extensions fonctionnant avec les deux types d’interpréteurs
- Les extensions compilées pour la stable ABI avec CPython 3.12 ou antérieur ne seront pas compatibles avec les builds sans GIL à partir de 3.13 ; l’idée de créer une nouvelle version d’ABI, abi4, a donc émergé
- La stable ABI actuelle est
abi3 - Le numéro d’ABI et le numéro de version majeure de CPython ne sont pas nécessairement liés
- La stable ABI actuelle est
- Gross considère que les extensions souhaitant prendre en charge le mode sans GIL peuvent, dans une certaine mesure, accepter la charge de produire deux wheels binaires
- Il s’inquiète davantage d’une situation où le projet sans GIL serait trop fortement contraint par les améliorations de l’API C et de la stable ABI
- Alex Gaynor maintient lui aussi plusieurs paquets de wheels
abi3, mais estime que produire une fois deux wheels n’est pas une charge excessive- Toutefois, il est important que les versions actuelles et futures de
pipchoisissent la bonne wheel parmi les deux
- Toutefois, il est important que les versions actuelles et futures de
- Brett Cannon estime que la logique actuelle de
pipne distingue pas les deux versions ; sans changement commeabi4, les versions existantes et anciennes depipne fonctionneront donc pas correctement
Risque de mauvais comportement silencieux de pip
- Gross estime qu’il n’est pas nécessaire de trop s’inquiéter de la prise en charge des anciens
pipdans le build expérimental--disable-gilde CPython 3.13- La raison avancée est qu’il est courant qu’un ancien
pipcasse avec une nouvelle version de Python - Il cite comme exemple le fait que
pip==23.1.1et les versions antérieures cassent avec CPython 3.13 en raison de l’absence depkgutil.ImpImporter
- La raison avancée est qu’il est courant qu’un ancien
- Paul Moore, mainteneur de
pip, considère qu’une rupture explicite et l’installation silencieuse d’un paquet incorrect sont deux problèmes différents- Certains utilisateurs emploient encore d’anciennes versions de
pip - Un échec explicite et une erreur silencieuse n’ont pas le même impact pour l’utilisateur
- Certains utilisateurs emploient encore d’anciennes versions de
- Moore craint que les utilisateurs voulant tester des builds sans GIL ou free-threaded ne soient découragés s’ils doivent déboguer des problèmes de compatibilité ABI
- Gaynor estime lui aussi que, si
pipse comporte silencieusement de manière incorrecte pour les paquets concernés, les tickets pourraient affluer
Installation en parallèle et nom de l’exécutable
- Barry Warsaw a demandé s’il existait un plan pour installer côte à côte, sur un même système, les builds avec GIL et sans GIL
- Gross a répondu que cette situation était comparable à l’installation de différentes versions de Python
- Cannon estime qu’une solution consistant à placer deux binaires dans une seule wheel « fat » serait également possible
- Toutefois, les noms des binaires à l’intérieur de la wheel devraient être différents
- La discussion sur le nom de l’exécutable s’est poursuivie dans un fil séparé
- Paul Moore considère que les utilisateurs doivent pouvoir tester facilement le mode sans GIL et choisir facilement entre GIL et sans GIL
- Si ce processus est difficile sous Windows, macOS, Linux, etc., cela pourrait avoir un impact négatif sur le projet sans GIL
- Il faut que les utilisateurs puissent l’essayer facilement pour qu’une demande de builds sans GIL apparaisse, ce qui créera aussi une pression sur les mainteneurs de paquets afin qu’ils fournissent des wheels compatibles sans GIL
Débat sur les noms nogil et free-threading
- Barry Scott estime que le nom de l’exécutable est important, car la ligne shebang doit indiquer quel interpréteur appeler
- Il propose par exemple des noms comme
python-nogil3oupython-nogil3.13
- Il propose par exemple des noms comme
- Gregory P. Smith a exprimé l’avis personnel que, le build sans GIL de CPython 3.13 étant une fonctionnalité expérimentale, les distributions ne devraient pas le placer dans le
$PATHpar défaut- Il considère également qu’il n’est pas souhaitable que des noms d’exécutables longs restent durablement dans les shebangs
- Il suggère de repousser la décision sur le nom d’installation à la version 3.14 ou ultérieure
- Petr Viktorin, développeur Fedora, souligne qu’il est probable que les distributions veuillent packager l’interpréteur sans GIL pour permettre aux utilisateurs d’expérimenter
- Moore indique vouloir pouvoir désigner un build free-threaded sous une forme comme
#!/usr/bin/env python3.13-nogil- Il s’agit d’éviter de coder en dur un chemin long et peu intuitif
- Dans un fil sur l’installateur Windows lancé par Steve Dower, Smith a indiqué que le steering council souhaitait éviter le nom
nogil- Les raisons sont qu’il ne parle pas clairement à la plupart des développeurs non core, qu’il ne devrait pas être nécessaire de savoir ce qu’est le GIL, et qu’il contient une formulation négative
- Le terme free-threading a été proposé comme alternative
- Gross estime que
free-threadingn’est pas non plus facile à comprendre pour les personnes extérieures et que ce n’est pas un terme largement utilisé - Dans les discussions concrètes, la préférence allait nettement à un nom court, et
nogilétait le candidat le plus solide sur ce point - Le changement effectivement reflété consiste à remplacer le tag ABI des builds sans GIL de
nparttsignifie threading
Proposition abi4 et travaux restants
- Gross et Viktorin ont discuté des points problématiques de la proposition de modification de l’API, et ces retours ont conduit à la proposition d’une nouvelle ABI, abi4
- Gross a créé un prototype de la nouvelle ABI
- Viktorin est globalement d’accord avec l’approche, mais estime que les détails doivent encore être clarifiés
- Stinner estime qu’une PEP sur abi4 est nécessaire, et Viktorin considère qu’il s’agit d’une discussion pré-PEP
- Il existe une confusion autour des garanties de compatibilité offertes par la combinaison entre les versions de l’API C limitée et
abi3, ce qui influe aussi sur l’orientation d’abi4 - Les investigations liées se poursuivent, et une discussion en personne pourrait avoir lieu lors du core developer sprint de mi-octobre
Formulation finale de l’approbation et impact à long terme
- Les travaux sur CPython sans GIL ou free-threaded se poursuivent, mais l’approbation finale de la PEP 703 est encore en attente
- Le retard s’est quelque peu prolongé, mais la PEP 703 et ses effets pourraient avoir un impact majeur sur le développement de CPython et son écosystème pendant les cinq prochaines années ou plus
- Le steering council cherche à clarifier les critères d’approbation
- Thomas Wouters a indiqué qu’il peaufinait la formulation exacte de l’approbation et cherchait à clarifier plusieurs décisions
- Une partie du travail pourrait également avancer lors du core developer sprint
1 commentaires
Avis sur Hacker News
Quand on regarde les ordinateurs modernes, on se dit que la parallélisation explicite pourrait devenir un élément plus fondamental de l’informatique que ce que les manuels laissent penser
C’est peut-être le moment où il faut désormais toujours écrire du code parallèle de façon explicite
Par exemple, les boucles
forsont remplacées par des opérations commeforeach,map,filter. Ces expressions indiquent au compilateur/interpréteur l’intention d’appliquer une opération à tous les éléments d’une structure de données, et laissent au compilateur/runtime le soin de décider si et comment paralléliserDans l’exécution de services web, chaque requête est suffisamment rapide en soi, et le vrai gain de la parallélisation consiste à traiter de nombreuses requêtes en parallèle. C’est là que le No-GIL s’insère bien
Lorsqu’il y a beaucoup de sous-requêtes dans une même requête, on les traite souvent avec du code asynchrone, mais c’est fréquemment moins pour le gain de performance de l’asynchrone que parce que créer des threads coûte cher ou que les pools de threads sont pénibles à gérer. L’asynchrone est bon pour le débit mais mauvais pour la latence, et quand on parallélise des requêtes de service, c’est en général la latence qui préoccupe davantage. L’asynchrone l’a surtout emporté pour des raisons d’ergonomie
Une autre forme de parallélisation apparaît dans les gros traitements offline. C’est le cas de MapReduce ou de Presto, et cela ressemble généralement à des problèmes de type diviser pour régner. L’entraînement de modèles sur GPU est assez similaire
Ce qui ne s’est pas produit, ce sont les algorithmes locaux hautement parallèles. Dans les services web, la taille des données est petite, donc le gain en latence est faible, l’implémentation est complexe et le coût de coordination entre threads devient élevé. Une petite exception concerne les algorithmes vectorisés, mais ils s’exécutent sur un seul cœur, sans surcharge de coordination, et l’inférence en ligne est elle aussi à nouveau très fortement vectorisée
Avec le temps, les deux s’améliorent. De même que davantage de langages et de bibliothèques deviennent sûrs par défaut, de plus en plus de choses sont désormais parallèles par défaut. Il reste encore du chemin, mais c’est peut-être une bonne chose que cela ne soit pas arrivé trop tôt. La technologie s’est beaucoup améliorée ces dix dernières années
On peut par exemple comparer ce qu’on peut faire en toute sécurité avec Rayon en Rust et ce qu’on faisait de manière non sûre avec OpenMP en C++
Plus à l’extérieur, il y a aussi ce genre de choses sur lesquelles je travaille : https://legion.stanford.edu/, https://regent-lang.org/, https://github.com/nv-legate/cunumeric
Comme il s’agit d’un détail d’implémentation, s’il est possible de l’abstraire pour la rendre plus facile à exploiter, il faut le faire
À titre de comparaison, un mutex tourne autour de 25 nanosecondes, et davantage en cas de contention, mais un mutex reste une synchronisation point à point
Ce qui est bien avec Disruptor, c’est que plusieurs threads peuvent recevoir le même message sans gros effort supplémentaire
https://github.com/LMAX-Exchange/disruptor/wiki/Performance-...
https://gist.github.com/rmacy/2879257
Je rêve d’un langage un peu comme Smalltalk, mais qui reste mono-thread tant que la parallélisation n’a pas de sens
Je cherche des problèmes de parallélisation qui ne relèvent pas du big data. La parallélisation ressemble plus au fait de mettre plus de voitures sur la route qu’à augmenter la vitesse d’une voiture. Mais je cherche encore ce que les utilisateurs desktop ou mobile ont réellement besoin de faire en local pour exploiter la puissance de calcul mathématique de leur machine
Je réfléchis aussi à des idées de parallélisation comme Itanium et les architectures VLIW
Il suffit d’utiliser
-ng, au sens de no-gil ou next-generationIl y avait de nouveaux flags de compilation, de nouveaux flags d’édition de liens, des liens vers des bibliothèques différentes, voire l’utilisation d’une commande de compilation complètement différente. AIX était particulièrement comme ça
Pour le problème du shebang, il vaudrait mieux s’appuyer sur les conventions Python existantes :
from __future__ import nogilIl suffirait ensuite de faire un hot swap de l’interpréteur à ce moment-là
from __future__ importn’est pas une instruction d’exécution, mais une instruction spéciale qui représente un drapeauhttps://docs.python.org/3/reference/simple_stmts.html#future...
Les future statements fonctionnent module par module, et GIL/no-GIL ne s’intègre pas facilement à ce modèle
Chaque fois que je vois cette proposition, je me demande comment on peut garantir que les programmes continueront à fonctionner correctement. Une grande partie du code Python multithread existant est écrite de manière non sûre
Le problème, en particulier, ce sont les data races, que j’ai vues à répétition dans les bases de code de plusieurs entreprises et dans des projets open source
Si ces programmes ne cassent pas, c’est uniquement parce qu’ils reposent implicitement sur le fait que le GIL n’autorise l’exécution que d’un seul thread à la fois
Si le GIL disparaît, ces programmes casseront. Python étant un langage à typage dynamique, je doute fortement qu’il puisse exister un analyseur statique capable de repérer ce genre de problème dans les programmes Python existants
Le plus probable, ce sont plutôt des bugs subtils qui apparaîtront de manière non déterministe à l’exécution. Un crash serait presque préférable, mais ce type de bug a de fortes chances d’aboutir à un comportement erroné
Peut-être aussi que cette proposition sans GIL n’est pas destinée à la majorité des programmes. Ce sera peut-être un outil ultra-spécialisé, réservé à de très rares cas où le programmeur sait qu’il n’y a pas de GIL et écrit son code en conséquence
Le GIL signifie simplement qu’un seul thread peut exécuter du bytecode Python à la fois. Même avec un interpréteur doté du GIL, on peut changer de thread entre des bytecodes, et de nombreuses opérations Python nécessitent plusieurs bytecodes. Cela inclut aussi des méthodes intégrées de types intégrés que beaucoup de gens considèrent comme « atomiques »
C’est pour cela que Python fournit déjà des verrous, mutex, sémaphores, etc., malgré la présence actuelle du GIL
Des threads qui se disputent le GIL peuvent déjà se le reprendre mutuellement au pire moment et semer le chaos
Si un programme ne s’exécute sans GIL que lorsque toutes ses dépendances l’autorisent, il y aura largement le temps de corriger ce genre de bugs
Donc le moment où ce problème devra être traité à grande échelle n’arrivera probablement pas avant qu’on approche de 2030. On ne voit déjà pas beaucoup d’environnements de production qui passent immédiatement à la dernière release du runtime
Je ne veux pas paraître brutal, mais le Steering Council a dit qu’il ne voulait pas d’une nouvelle migration du type 2 vers 3, donc les gens ne mettront pas à jour à la légère. Une grande partie de ce qu’on trouve aujourd’hui en ligne pourrait devenir dangereuse à copier-coller
En pratique, le code Python contient énormément de bugs de threading
OCaml n’a-t-il pas connu une évolution similaire ? Je me demande s’il y a des comparaisons pertinentes à faire entre les deux projets
Ainsi, l’API de threads existante crée des threads dans le domain courant et permet d’isoler le code qui s’attend à prendre le verrou. Le nouveau code peut à la place créer un nouveau domain démarrant avec un seul thread. On peut aussi utiliser volontairement les deux ensemble comme forme d’ordonnancement
Python, lui, cherche à rendre le verrou global entièrement optionnel, de manière globale et hors du contrôle des auteurs de bibliothèques. Cela dit, le verrou de Python semble être garanti comme ne protégeant que le runtime lui-même, donc la plupart du code qui en dépend a probablement déjà des bugs, ce qui rend aussi le plan de Python plausible
S’il y a un point commun, ce serait surtout le fait de devoir repérer et corriger des états partagés inattendus dans toute la base de code du runtime, et de devoir réviser l’ABI C
Python a maintenant une chance de rattraper Tcl en performances multithread : https://www.hammerdb.com/blog/uncategorized/why-tcl-is-700-f...
Je préférerais encore porter du code Python vers Mojo pour obtenir du multithreading, du SIMD et d’autres gains de performance
Tu ne veux pas porter ton code Python vers un Python nogil ? Alors on te downvote, en gros
Si on devait lui donner un nom, on pourrait avoir
python4,python3-gilfoil,python3-gilfree, etc.Je trouve assez étrange l’élan actuel qui consiste à se concentrer sur Python sans GIL. L’équipe Faster CPython s’était fixé l’objectif ambitieux d’augmenter de 50 % les performances de CPython à chaque release
Il y a bien eu de vraies améliorations en 3.11, mais on était encore très loin des 50 %, et dans une grande partie de nos tests, la 3.12 était similaire ou plus lente. Le vrai multithreading serait formidable, mais je préférerais de loin voir d’abord s’améliorer les performances en mono-thread
Bien sûr, je reconnais que nos besoins ne représentent pas forcément tout le monde, et je remercie pour tout le travail accompli pour faire de Python un excellent langage. Cela dit, je me demande quand même ce qui m’échappe
Aujourd’hui, l’utilisation de plusieurs cœurs passe par
multiprocessing, avec beaucoup de limites. Je comprends que plusieurs interpréteurs puissent arriver avec des choses du genre goroutines, mais je préfère malgré tout une vraie option de multithreadingDans nogil Python, par exemple, plusieurs threads peuvent appeler du code C avec un état partagé accessible via des objets Python. C’est assez central pour le machine learning, et dans les faits, la forme actuelle de cette PEP vient de l’équipe PyTorch
Les performances en mono-thread comptent aussi, mais pour les sections critiques, il existait déjà des solutions de contournement plutôt correctes comme numba, Cython et Mojo
L’ordre a aussi son importance. Si nogil est introduit, une bonne partie du travail de Faster CPython pourrait être entièrement abandonnée, donc les équipes ont dû se coordonner
Dans un monde idéal, on aurait à la fois un mode nogil et des améliorations des performances en mono-thread. Guido a aussi laissé entendre qu’il envisageait un JIT sophistiqué
Python rend très pratique la manipulation d’abstractions bas niveau dans un langage de plus haut niveau. C’est pourquoi, en tant que développeur Python de longue date, je n’ai jamais été particulièrement stressé par le GIL
S’il fallait n’en choisir qu’un, je suis d’accord pour dire que, pour la plupart des cas d’usage, du code mono-thread simplement plus rapide conviendrait mieux. Mais il n’y a pas non plus de raison de ne pas avoir les deux
Avec le recul, c’est évident, mais si l’équipe Python avait su à quel point la transition de la 2 vers la 3 serait longue et douloureuse, elle aurait sans doute beaucoup plus remanié l’intérieur de l’interpréteur dès le départ
Après une transition qui a duré 12 ans, les performances en mono-thread restent médiocres, et il reste encore plusieurs transitions douloureuses avant d’arriver à un vrai multithreading
Je sais qu’il faut faire preuve de bienveillance envers le développement open source, mais à partir d’un certain point, je me demande s’il n’est pas permis de parler d’un langage très mal géré
Les pires aspects de Python sont ceux qu’il est difficile de changer précisément parce que Python est trop populaire et que son écosystème est trop vaste. Du coup, tout type de changement devient plus difficile à cause de la compatibilité descendante
multiprocessingreste toujours médiocreIl y a une tendance à défendre Python trop vite. Il est important de l’examiner objectivement, sans biais
Les projets qui veulent des performances et la syntaxe Python pourraient aller de ce côté-là. En l’état, Python semble se débattre entre plusieurs objectifs sans vraiment en atteindre aucun correctement
Perl 5/6 était cité en exemple. Même une fois qu’il est devenu évident que personne ne migrerait, il a encore fallu environ 5 ans avant qu’on essaie de rendre la transition plus facile