5 points par GN⁺ 2025-05-17 | 1 commentaires | Partager sur WhatsApp
  • Python free-threaded est conçu pour exploiter efficacement le matériel multicœur
  • Dans CPython 3.14, la sécurité des threads et les performances des modules clés ont été nettement améliorées
  • De nombreux packages majeurs ne prennent toujours pas en charge les builds free-threaded
  • Chacun peut contribuer à ses progrès grâce à des retours en conditions réelles et à la contribution communautaire

Vue d’ensemble

La semaine dernière, CPython 3.14.0b1 a été publié, et cette semaine PyCon 2025 débute à Pittsburgh
Ces deux événements marquent des étapes importantes pour le déploiement et la stabilisation de Python free-threaded
Cet article revient sur l’année écoulée et explique comment l’équipe de Quansight a joué un rôle clé dans l’adoption expérimentale du build free-threaded au sein de workflows de production réels avec des dépendances complexes

Sens et nécessité de Python free-threaded

  • La prise en charge de Python free-threaded permet de mobiliser l’ensemble des ressources de calcul du matériel moderne, où CPU et GPU multicœurs sont devenus la norme
  • Avec l’approche traditionnelle du GIL (Global Interpreter Lock), exploiter pleinement les algorithmes parallèles nécessitait des contournements et un tuning spécifique
  • On utilisait généralement multiprocessing plutôt que le module threading, mais cela implique un coût élevé de création de processus et des copies de données inefficaces
  • Lorsqu’un package Python inclut du code natif, un build free-threaded n’est pas automatiquement compatible ; un audit du code est donc indispensable pour garantir la sécurité des threads
  • La suppression du GIL a exigé de profonds changements structurels dans l’interpréteur CPython, et a aussi mis en lumière des problèmes structurels dans les packages existants

Principales avancées

  • Avec l’équipe Python Runtime de Meta, la prise en charge de Python free-threaded a été apportée à divers packages et projets, notamment :
    • des outils de packaging et de workflow comme meson, meson-python, setup-python GitHub Actions, packaging, pip et setuptools
    • des générateurs de bindings comme Cython, pybind11, f2py et PyO3
    • des packages clés de l’écosystème PyData comme NumPy, SciPy, PyArrow, Matplotlib, pandas, scikit-learn et scikit-image
    • des dépendances majeures parmi les plus téléchargées sur PyPI comme Pillow, PyYAML, yarl, multidict et frozenlist
  • Des packages populaires encore non compatibles (CFFI, cryptography, PyNaCl, aiohttp, SQLAlchemy, grpcio, etc.) ainsi que des bibliothèques de machine learning (safetensors, tokenizers, etc.) sont également en cours de prise en charge progressive
  • Les développeurs core de CPython de l’équipe Quansight ont intégré dans la version 3.14 les améliorations suivantes :
    • le module warnings fonctionne désormais de manière thread-safe par défaut dans les builds free-threaded
    • amélioration de graves problèmes de sécurité des threads dans asyncio et meilleure scalabilité en parallèle
    • amélioration complète de la sécurité des threads dans le module ctypes
    • amélioration des performances du garbage collector free-threaded
    • optimisation du schéma de deferred reference counting et de l’interpréteur à spécialisation adaptative
    • nombreuses corrections de bugs et renforcements de la sécurité des threads
  • Un guide complet1 pour la prise en charge de Python free-threaded a également été rédigé, afin de fournir une documentation concrète utile à davantage de packages à l’avenir

État de l’écosystème Python free-threaded

  • Il y a un an (au moment de la sortie de 3.13.0b1), l’installation de la plupart des packages Python sur un build free-threaded était massivement cassée
  • Les échecs de build venaient moins de problèmes fondamentaux que d’options par défaut non prises en charge ou de petites hypothèses devenues fausses
  • Au cours de l’année écoulée, de nombreux problèmes ont été résolus avec l’aide de la communauté, et Cython 3.1.0 a constitué un tournant majeur avec sa prise en charge officielle
  • Il reste encore des packages contenant du code compilé mais ne proposant pas de wheels free-threaded
    Leur progression peut être suivie dans des tableaux de suivi manuels et automatiques2

Défis actuels

  • À ce stade, les builds Python free-threaded ont surtout besoin d’expérimentations et de retours dans des workflows réels
  • Il existe un fort potentiel d’amélioration des performances, notamment dans les workflows où le coût du multiprocessing est élevé, mais un audit fin de la stabilité des threads reste indispensable pour chaque package
  • De nombreuses bibliothèques fournissent des structures de données mutables sans documenter correctement leur sécurité des threads, ni la garantir réellement
  • Quand les packages sont volumineux et fortement hérités du legacy, il manque souvent des personnes capables d’en comprendre entièrement le code, ce qui ralentit la prise en charge
  • À l’échelle de la communauté, des efforts sont nécessaires pour assurer la pérennité de la maintenance des packages essentiels

Comment contribuer

  • Il est possible de consulter le guide de contribution officiel
  • Le suivi des problèmes à l’échelle de l’écosystème et les principaux documents de compatibilité sont gérés dans le dépôt free-threaded-compatibility5
  • Il est aussi possible de participer aux discussions et aux contributions sur le Discord communautaire animé par Quansight-Labs6

Présentations prévues à la PyCon

  • L’auteur de l’article et son coéquipier Lysandros Nikolaou doivent intervenir lors de PyCon 2025
  • Ils prévoient de partager des cas concrets de portage et des retours d’expérience pratiques, avec également une vidéo de la présentation disponible sur YouTube
  • Ils estiment que le build free-threaded représente l’avenir du langage Python et se disent très enthousiastes à l’idée de contribuer à sa concrétisation
  • Ils espèrent que les efforts d’aujourd’hui marqueront un tournant pour l’avenir des packages variés et massifs qu’utilisent chaque jour des millions de développeurs

1 commentaires

 
GN⁺ 2025-05-17
Avis Hacker News
  • Beaucoup de gens utilisent le multiprocessing, avec la remarque que le coût de création des processus est élevé

    • La fonctionnalité SharedMemory existe, et il est difficile de comprendre pourquoi elle n’est pas utilisée plus souvent

    • Il est souligné que l’expérience avec ShareableList a été bonne

    • Sous Unix, la création d’un processus prend moins de 1 ms

      • Mais le démarrage d’un processus interpréteur PYTHON peut prendre de 30 ms à 300 ms selon le nombre d’import
      • Comme l’écart est d’un à deux ordres de grandeur, les chiffres précis sont importants
      • CGI est une exception sur ce point, et ce n’est pas un problème avec C, Rust ou Go
      • Il est aussi mentionné que sqlite.org utilise un modèle avec un processus distinct par requête
    • ShareableList ne permet de partager que des scalaires atomiques et des bytes ou chaînes de caractères

      • Les objets structurés de Python entraînent un coût de sérialisation, comme avec pickle dump, ainsi qu’un coût mémoire lié aux copies par processus
    • Retour d’expérience très positif sur le partage de tableaux numpy

      • Le partage explicite n’est pas une charge importante comparé à la difficulté de déboguer les problèmes causés par un partage accidentel entre threads
      • Beaucoup de gens surestiment à quel point les threads sont meilleurs que le multiprocessing
      • Il y a une inquiétude quant à une augmentation des segfault aléatoires à déboguer si le GIL disparaît
      • Il est mentionné que peu de gens se sont vraiment plaints du fait que JavaScript ne prenne pas en charge le threading basé sur mémoire partagée
      • Une interprétation est que JavaScript est suffisamment rapide pour en avoir moins besoin
      • Il est souhaité que davantage d’efforts soient consacrés à l’amélioration des performances de base de Python
    • Comme les processus peuvent mourir indépendamment, il est difficile de récupérer si un processus meurt alors qu’il modifie une structure de données en mémoire partagée tout en tenant un verrou

      • Exemple cité : les structures de mémoire partagée de Postgres et la nécessité d’arrêter tous les processus backend
      • Avec les threads, qui meurent ensemble, ce type de problème est moins visible
    • La mémoire partagée ne fonctionne que sur du matériel dédié

      • Dans des environnements comme AWS Fargate, il n’y a pas de mémoire partagée, ce qui impose de passer par le réseau ou le système de fichiers et augmente la latence
      • La duplication de processus via fork pose un autre problème
      • D’après l’expérience réelle, les green threads et le modèle actor ont été bien plus efficaces
  • Question sur le fait de savoir si la suppression du GIL a d’autres effets sur le code Python multithread que le parallélisme

    • Le maintien du GIL ne viendrait pas du fait que le multithread en dépend, mais du fait que sa suppression complique l’implémentation de l’interpréteur et des extensions C, tout en ralentissant le code mono-thread

    • On se demande si Free-threaded Python garde la même garantie historique, à savoir qu’une préemption peut survenir à n’importe quelle frontière de bytecode

    • Ou bien s’il faut écrire le code différemment, par exemple en utilisant davantage de verrous

    • Free-threaded Python conserve globalement les mêmes garanties

      • Mais sans free-threading, les gens utilisent moins le threading, donc les bugs liés à la préemption aux frontières apparaissent rarement en pratique
      • L’introduction du free-threading expose davantage de bugs
      • Le code utilisateur devient plus simple car il n’est plus nécessaire de recourir à des contournements multiprocess ; cela est jugé suffisant pour justifier la complexité accrue de l’interpréteur
      • Le problème de complexité des extensions C serait plus grave avec les sub-interpreters qu’avec le free-threading ; l’équipe numpy a clairement indiqué ne pas pouvoir prendre en charge les sub-interpreters
      • numpy prend déjà en charge le free-threading, et corrige encore les bugs restants
      • Un léger ralentissement mono-thread (quelques pourcents à un chiffre) est considéré comme un compromis acceptable
    • L’utilisation de plusieurs cœurs est possible, mais avec une baisse des performances par thread et la nécessité de retravailler les bibliothèques

      • Lors d’expériences avec PyTorch, il a été observé une utilisation CPU multipliée par 10 pour seulement la moitié du débit de traitement
      • Des améliorations sont espérées avec le temps, et c’est une évolution bienvenue après 20 ans d’attente
    • Les situations de concurrence (race conditions) peuvent devenir plus fréquentes, donc il faudra faire plus attention en écrivant du Python multithread pour garantir la fiabilité

  • Information selon laquelle Microsoft a dissous l’équipe Faster Python

    • L’équipe n’aurait pas été maintenue à cause de résultats prévus insuffisants pour 2025

    • Il reste à voir si des améliorations de performance continueront à être ajoutées à CPython, ou si d’autres entreprises prendront le relais du financement

    • Facebook (Meta) semblerait encore assurer une partie du soutien

    • Il est rappelé que le calendrier a énormément dérapé par rapport aux promesses de Microsoft

      • Plus récemment, ils seraient sans doute au courant de graves problèmes politiques et de gouvernance ; selon cet avis, des employés compétents n’auraient pas envie de contribuer gratuitement pour ensuite voir le groupe être dénigré
      • Critique d’une organisation CPython qui promet trop, distribue le travail aux plus dociles et écarte les opposants compétents
      • Selon ce point de vue, ces problèmes n’existaient pas auparavant et sont auto-infligés aujourd’hui
    • C’est regrettable, mais cela confirmerait l’idée de départ selon laquelle il ne fallait pas faire confiance aux engagements de long terme de Microsoft

    • Une rumeur récente affirme aussi que Google aurait licencié toute son équipe de développement Python

      • La question est posée de savoir si les deux cas ont une cause d’époque ou un dénominateur commun
    • C’est jugé très triste, avec une allusion disant qu’après embrace & extend, il ne reste plus qu’une seule étape

  • Question de savoir si l’on est le seul à s’inquiéter de la disparition du GIL en Python

    • Dans n’importe quel langage, le code multithread complexe est difficile à juger fiable, et cela semble encore plus inquiétant avec la nature dynamique de Python

    • La peur du changement n’est pas isolée, même si elle peut être irrationnelle

      • Le GIL est vu comme de la pure dette technique, donc sa suppression serait nécessaire dans l’intérêt de la communauté
      • Aujourd’hui, en Python, on utilise le plus souvent les E/S non bloquantes et async plutôt que les threads
      • Si on n’utilise pas les threads, la suppression du GIL ne change rien ; les bibliothèques C restent elles aussi sûres en mono-thread
      • Il faut surtout être prudent si l’on utilise réellement des threads
      • Jusqu’ici, du code Python threadé naïf se comportait comme du mono-thread à cause du GIL ; désormais il pourrait être un peu plus rapide, mais aussi générer davantage de bugs
      • Le conseil est soit de ne pas utiliser de threads, soit d’apprendre à les utiliser correctement
      • De meilleures abstractions arriveront probablement, et l’on espère des discussions dans la communauté autour de la structured concurrency notamment
    • asyncio est utilisé activement

      • Cela permet d’écrire du Python concurrent agréable en restant mono-thread, un peu à la manière de Node.js
      • Cette approche est recommandée pour le web et le réseau
    • On s’attend à une organisation comparable à celle du domaine ML/IA, où des spécialistes construiront d’abord des bibliothèques complexes pour ensuite les mettre à disposition des utilisateurs ordinaires

      • Les cas où le GIL constitue un goulot d’étranglement sérieux sont devenus plus nombreux
      • C’est pour cette raison qu’il a fallu apprendre Go : un langage avec un vrai support des threads, situé plus bas niveau que Python mais plus haut niveau que C/C++
      • Le modèle du compilateur est aussi un facteur de fond important, au-delà du threading
    • Il est rappelé, peut-être à tort pour ne pas inquiéter inutilement, que les LLM ont été entraînés sur du code Python écrit avec l’hypothèse de l’existence du GIL pendant des décennies

    • La présence ou l’absence du GIL ne concerne en pratique que ceux qui veulent exploiter plusieurs cœurs

      • Si l’on ne se préoccupait déjà ni du threading ni du multiprocessing, il n’y a pas de changement concret
      • Les problèmes de race conditions existent avec ou sans GIL
  • Une personne utilise souvent Python sans être experte, et lance parfois simplement plusieurs fonctions en parallèle avec concurrent.futures

    • Elle demande ce qu’un tel utilisateur devra changer à l’avenir

    • Les threads n’étant plus liés au GIL, l’ensemble sera plus rapide

      • Tant que les verrous sur les objets partagés sont correctement gérés, il n’y a pas d’inquiétude supplémentaire à avoir
  • Partage d’une impression issue de 20 ans d’expérience en développement professionnel avec Python

    • Les threads ne sont réellement nécessaires que lorsqu’il est impossible d’éviter le passage de messages

      • L’écosystème Python fournit déjà des solutions de contournement pour toutes ces situations
      • À cause des nombreux pièges liés à la gestion de plusieurs threads (verrous, etc.), cela pourrait n’être nécessaire à l’avenir que dans certaines bibliothèques ou certains domaines
      • Pour tirer autant de performance que possible de Python pur, on peut aussi s’appuyer sur des bibliothèques fondées sur du code natif (ex. Pypy, numba)
      • La véritable révolution de performance de Python est finalement la programmation async ; il est vivement recommandé de l’apprendre
    • Une autre personne dit utiliser Python depuis aussi longtemps et être d’accord, tout en le formulant un peu différemment

      • Les threads Python étant tellement médiocres, diverses solutions de contournement ont été développées pour les éviter
      • En voulant accélérer d’un facteur 2 une tâche CPU-bound avec des threads, elle s’est heurtée au problème du GIL puis est passée au multiprocessing ; cela a entraîné des coûts de sérialisation des structures de données, avec des expériences inefficaces du type deux fois plus de cœurs pour seulement 1,5 fois plus de vitesse
      • Il y a beaucoup d’environnements où un bon support des threads serait utile ; comme il n’existait pas jusqu’ici, il a fallu recourir à toutes sortes d’approches alternatives
      • Si le contexte s’y prête, async est très fortement recommandé (glyph, tu avais raison !)
  • C’est une image générée par IA, mais il est étrange que le serpent ait deux queues

    • Avis de soutien humoristique suggérant de ne pas s’y attarder ; il est plaisamment dit que lorsqu’un article sur Python contient une image de serpent, c’est généralement un signal qui ne mérite pas vraiment d’attention

    • Proposition humoristique du nom Confusoborus

  • Remarque selon laquelle le serpent de l’image d’en-tête semble avoir deux queues

    • Blague disant qu’il a sans doute créé un deuxième thread dans le même processus
  • Question sur le fait de savoir s’il existe d’autres contraintes, en dehors du support des bibliothèques, pour faire tourner des workers WSGI et Celery dans un seul processus

    • Il n’y a pas de contrainte particulière, mais cette manière de faire n’est pas une fonctionnalité de premier ordre du langage
      • Explication selon laquelle le GIL est un problème de dette technique
      • Il existe aussi d’autres raisons que le parallélisme pour lesquelles la suppression du GIL est nécessaire
  • C’est vu comme un immense travail de fond pour l’ère des performances à venir