3 points par GN⁺ 2025-12-26 | 1 commentaires | Partager sur WhatsApp
  • L’interpréteur à appels de fin (tail-calling) de CPython affiche sur Windows x86-64 des performances environ 15 % supérieures à la méthode précédente
  • Un gain de performances d’environ 5 % a aussi été confirmé sur macOS AArch64 (XCode Clang), tandis que Windows exploite une fonctionnalité expérimentale de MSVC 2026
  • Dans les benchmarks pyperformance, la plupart des tests montrent une amélioration de vitesse, certains allant jusqu’à 78 %
  • La principale raison de ce gain de performances serait une réinitialisation des heuristiques d’optimisation du compilateur et une amélioration de l’inlining
  • Lors de la sortie officielle de Python 3.15, cela devrait être activé par défaut dans les builds basés sur Visual Studio 2026

Amélioration des performances de l’interpréteur à appels de fin

  • Il a été mesuré que l’interpréteur à appels de fin de CPython est environ 15 % plus rapide sous Windows x86-64 que l’interpréteur switch-case traditionnel
    • Selon pyperformance, la moyenne géométrique progresse de 15 à 16 %
    • Certains benchmarks gagnent jusqu’à 78 % en vitesse, tandis qu’un petit nombre deviennent 60 % plus lents
  • Sur macOS AArch64 (XCode Clang), un gain de performances d’environ 5 % a été constaté
  • Ces résultats restent valables à condition qu’aucun changement n’intervienne pendant le cycle de développement de Python 3.15

Comparaison des structures d’interpréteur

  • Les implémentations d’interpréteur en C se répartissent en trois approches : switch-case, computed goto et tail-call threaded
    • switch-case : traitement des branchements pour chaque instruction
    • computed goto : extension GCC/Clang permettant de sauter directement à l’adresse de branchement
    • tail-call threaded : séparation de chaque gestionnaire de bytecode dans une fonction, puis appel de fin vers la fonction suivante
  • Par le passé, les compilateurs C ne garantissaient pas l’optimisation des appels de fin, d’où un risque de débordement de pile
  • Les attributs __attribute__((musttail)) de Clang et [[msvc::musttail]] de MSVC permettent désormais de forcer les appels de fin

Résultats de build avec MSVC 2026 pour Windows

  • Dans un build CPython utilisant une fonctionnalité expérimentale de MSVC, la plupart des benchmarks s’améliorent
    • Exemples de résultats :
      • spectralnorm : 1,48x
      • nbody : 1,35x
      • bm_django_template : 1,18x
      • xdsl : 1,14x
  • Cela a été officiellement intégré au document « What’s New » de Python 3.15
    • L’interpréteur à appels de fin peut être utilisé dans les builds Visual Studio 2026 (MSVC 18)
    • Les bibliothèques Python pures gagnent environ 15 % en vitesse, et les petits scripts jusqu’à 40 %

Causes du gain de performances

  • Les appels de fin réinitialisent les heuristiques d’optimisation du compilateur, ce qui favorise une génération de code plus efficace
  • La boucle d’interpréteur CPython actuelle se compose d’une fonction unique d’environ 12 000 lignes, ce qui entraîne fréquemment un échec de l’optimisation par inlining
    • Le compilateur refuse souvent l’inlining pour éviter d’augmenter la taille du code
  • Avec l’approche par appels de fin, les fonctions sont séparées, ce qui permet d’inliner les fonctions simples
    • Par exemple, une fonction simple comme PyStackRef_CLOSE_SPECIALIZED peut être inlinée
  • Le même phénomène a également été observé dans les builds PGO (optimisation guidée par profil)

Méthode de build et d’utilisation

  • Pour l’instant, seul le build depuis les sources est possible
    • Dans un environnement Visual Studio 2026, build avec la commande suivante
      $env:PlatformToolset = "v145"
      ./PCbuild/build.bat --tail-call-interp -c Release -p x64 --pgo
      
  • Une distribution binaire officielle est prévue une fois le développement de Python 3.15 stabilisé

1 commentaires

 
GN⁺ 2025-12-26
Commentaires Hacker News
  • Partage le morceau de code clé qui aurait dû figurer dans le billet de blog
    C’est un exemple montrant la différence de définition des attributs musttail et preserve_none entre MSVC et Clang
    Ces attributs doivent être placés sur le déclarateur de fonction et ne fonctionnent pas à l’emplacement des spécificateurs de fonction
    Lien vers le code concerné
    Cela laissait entendre que Microsoft ne révèle ce genre de fonctionnalités non publiques qu’aux projets qu’elle juge importants
    • Je me suis trompé. [[msvc::musttail]] était en fait un attribut officiellement documenté
      Je vais corriger le billet de blog en conséquence
      Commentaire HN lié
    • Soulève la question suivante : « Est-ce qu’ils l’ont signalé parce que c’est important, ou parce que ça leur profite ? »
  • Cela rappelle le cas de Python 3.14, où un bug de LLVM 19 avait conduit à annoncer à tort un gain de performances ; espère que ce n’est pas le cas cette fois
    Après lecture, juge l’approche correcte car elle privilégie la transparence et des retours rapides
    Une validation par compilateurs croisés ou un audit indépendant serait encore mieux, mais estime cela fiable grâce à la transparence totale de l’auteur
    • L’auteur se souvient que cette erreur avait finalement été un heureux accident
      Comme cela a été publié tôt, Nelson a trouvé le bug de Clang 19, ce qui a permis de le corriger avant la sortie officielle
      Cette fois, il est plus confiant car il y a deux améliorations : la logique de dispatch et l’inlining
      MSVC peut, dans certaines conditions, transformer un interpréteur switch-case en threaded code, mais CPython est trop complexe pour que cette optimisation s’applique
      À la place, l’approche par tail call donne davantage de contrôle à l’auteur du code C
      Références associées : conditions du threaded code sous MSVC, problème lié à forceinline
    • L’avantage de la nouvelle conception est qu’elle dépend moins des caprices des optimisations du compilateur
      Avant, des optimisations comme tail duplication variaient selon les décisions du compilateur, alors que désormais l’interpréteur peut exprimer directement la forme de code machine souhaitée
      Lien vers la discussion précédente
  • Mentionne qu’il existe une présentation sur les anciens problèmes de compilateur et partage la vidéo de présentation EuroPython 2025
  • Il développe pour la première fois depuis longtemps une application GUI Windows en Python
    A choisi Python parce que l’écosystème VS est bien trop lourd comparé à C#/MAUI
    A trouvé Tkinter peu pratique et Qt trop coûteux à apprendre, et utilise donc la combinaison wxGlade + wxPython
    N’a besoin que d’une dépendance unique installable via pip, et apprécie le ressenti Pythonic
    Se réjouit des améliorations du runtime Windows
    • Je préfère la combinaison Python + Qt/PySide
      Avec QtCreator pour construire rapidement l’UI et Python pour y brancher la logique, la vitesse de développement est très élevée
    • Recommande aussi pyfltk. C’était plutôt correct sous GNU/Linux
    • Si la GUI est importante, LINQPad peut aussi valoir le détour. C’est un point intermédiaire entre le scripting et les applications plus lourdes
    • Recommande les bindings ImGui pour Python
      Contrairement à Tkinter ou Qt, qui sont en retained mode, cela fonctionne en immediate mode, ce qui est particulièrement utile pour l’outillage interne
      Projet imgui_bundle
  • Demande si, à ce niveau, ce n’est pas une tâche d’optimisation assez facile ; s’étonne que la boucle de l’interpréteur ne soit pas encore entièrement optimisée
    Dit qu’il pensait qu’elle serait écrite en assembleur pour les principales ISA
    • Au contraire, estime que cette mise à jour montre que la boucle est déjà extrêmement optimisée
      [[msvc::musttail]] est un attribut tout récent ajouté dans MSVC 14.50, sorti le mois dernier, et l’équipe CPython l’a exploité en quelques semaines pour obtenir un gain de performances
      Documentation MSVC sur musttail
    • Python vise avant tout la simplicité plutôt que la vitesse
      Guido a privilégié la simplicité du code, ce qui a retardé l’arrivée d’un JIT, puis des tentatives comme la PEP 744 (JIT Compilation) sont apparues
    • Il ne faut pas avoir des attentes excessives vis-à-vis de l’open source
      Les optimisations en assembleur sont un cauchemar de maintenance, et le vrai goulot d’étranglement de Python, c’est son système de packaging
    • De toute façon, les gens sensibles aux performances n’exécutent pas Python sous Windows, dit-il
  • Pose la question : « Pourquoi l’interpréteur Python est-il tellement plus lent que V8 ? »
    • JavaScript utilise la compilation JIT, contrairement à CPython
      PyPy est plus rapide grâce au JIT, mais n’est pas compatible avec les extensions C
      Le modèle de threading de Python complique aussi l’optimisation
      À l’inverse, JS est monothread, donc plus simple
      Python pouvait contourner cela via des extensions C, ce qui a réduit la pression pour optimiser CPython lui-même
    • Pense que les effectifs et le niveau de qualité chez Google font une grande différence
      De plus, CPython ne peut pas casser la compatibilité à cause de son vaste écosystème d’extensions C
      À l’inverse, V8 a pu modifier librement sa structure interne
    • Python est bien plus dynamique, jusqu’à faire passer le simple accès aux attributs par le protocole descriptor
      Il doit aussi conserver une ABI C stable, ce qui rend l’analyse de code plus difficile pour un JIT
    • Python est un langage bien plus dynamique que JS, et les bindings FFI limitent les changements internes
      Mentionne les difficultés rencontrées par PyPy pour respecter ces contraintes
    • JS a été optimisé avec d’énormes ressources investies par Google pour dominer le Web, tandis que Python a consacré davantage de ressources à l’évolution du langage
      De plus, JS n’a qu’à prendre en charge du pur JS, alors que Python doit préserver tout un écosystème d’extensions externes, ce qui limite les optimisations
  • Trouve le nouveau graphique de benchmark intéressant et demande avec quel outil il a été généré
    Partage qu’il a déjà mesuré les performances de bibliothèques JS avec mitata
    PR d’optimisation d’Immer JS
    • Ce graphique est un violin plot
      Explication Wikipedia, exemple Matplotlib
      Il visualise la distribution de manière symétrique, mais le lissage peut déformer la distribution réelle et certains lui reprochent de gaspiller de l’espace
      Dans cette discussion HN, certains estimaient qu’un half-violin plot était préférable
      Image d’exemple
  • Indique que Matt Godbolt a mentionné que les interpréteurs fondés sur les tail calls s’accordent mieux avec le branch predictor du CPU
  • Fait remarquer qu’il y a deux fautes de frappe dans la première phrase du billet et demande si ce sont des erreurs volontaires pour donner l’air d’un texte généré par IA
    • L’auteur répond : « Merci, corrigé »
    • Un autre utilisateur plaisante en disant que pour ne pas ressembler à un texte IA, il suffit de l’écrire soi-même
      Il tourne en dérision les traits typiques des textes IA : paragraphes courts, ton excessivement positif, manque de profondeur, etc.
  • Pose la question : « Si l’équipe Python juge les tail calls utiles, est-ce que le langage lui-même finira par les prendre en charge ? »