3 puntos por GN⁺ 2025-12-26 | 1 comentarios | Compartir por WhatsApp
  • El intérprete con tail calling de CPython muestra un rendimiento aproximadamente un 15% superior al método anterior en entornos Windows x86-64
  • También se confirmó una mejora de rendimiento de alrededor del 5% en macOS AArch64 (XCode Clang), y en Windows se aprovecha una función experimental de MSVC 2026
  • En los benchmarks de pyperformance, la mayoría de las pruebas mostraron mejoras de velocidad, y algunas mejoraron hasta un 78%
  • Se analiza que las principales causas de la mejora son el reinicio de las heurísticas de optimización del compilador y la mejora del inlining
  • Cuando se lance oficialmente Python 3.15, se aplicará por defecto en compilaciones basadas en Visual Studio 2026

Mejora de rendimiento del intérprete con tail calling

  • Se midió que el intérprete con tail calling de CPython es aproximadamente un 15% más rápido que el intérprete switch-case en Windows x86-64
    • Según pyperformance, la media geométrica mejora entre un 15% y un 16%
    • Algunos benchmarks mejoraron la velocidad hasta un 78%, mientras que unos pocos fueron un 60% más lentos
  • En macOS AArch64 (XCode Clang) se confirmó una mejora de rendimiento de alrededor del 5%
  • Estos resultados son válidos bajo el supuesto de que no haya cambios durante el ciclo de desarrollo de Python 3.15

Comparación de la estructura del intérprete

  • Las formas de implementación de intérpretes en C se dividen en tres tipos: switch-case, computed goto y tail-call threaded
    • switch-case: manejo de bifurcaciones por instrucción
    • computed goto: extensión de GCC/Clang que salta directamente a la dirección de bifurcación
    • tail-call threaded: separa cada manejador de bytecode en una función y hace tail call a la siguiente función
  • En el pasado, los compiladores de C no garantizaban la optimización de tail call, por lo que existía riesgo de desbordamiento de pila
  • Los atributos __attribute__((musttail)) de Clang y [[msvc::musttail]] de MSVC permiten forzar el tail call

Resultados de compilación con MSVC 2026 para Windows

  • En una compilación de CPython usando funciones experimentales de MSVC, la mayoría de los benchmarks mejoraron su velocidad
    • Resultados de ejemplo:
      • spectralnorm: 1.48x
      • nbody: 1.35x
      • bm_django_template: 1.18x
      • xdsl: 1.14x
  • Se reflejó oficialmente en el documento “What’s New” de Python 3.15
    • En compilaciones con Visual Studio 2026 (MSVC 18), ya se puede usar el intérprete con tail calling
    • Las bibliotecas puras de Python son aproximadamente un 15% más rápidas, y los scripts pequeños hasta un 40%

Causas de la mejora de rendimiento

  • El tail calling reinicia las heurísticas de optimización del compilador, induciendo una generación de código más eficiente
  • El bucle tradicional del intérprete de CPython estaba compuesto por una sola función de unas 12,000 líneas, lo que hacía frecuentes los fallos en la optimización de inlining
    • Hubo muchos casos en los que el compilador rechazó hacer inlining para evitar aumentar el tamaño del código
  • Con el enfoque de tail calling, las funciones se separan y las funciones simples pueden procesarse con inline
    • Por ejemplo, una función sencilla como PyStackRef_CLOSE_SPECIALIZED puede ser inlineada
  • También se reportó el mismo fenómeno en compilaciones con PGO (optimización guiada por perfil)

Cómo compilar y usarlo

  • Por ahora, solo es posible compilar desde el código fuente
    • En un entorno Visual Studio 2026, se compila con el siguiente comando
      $env:PlatformToolset = "v145"
      ./PCbuild/build.bat --tail-call-interp -c Release -p x64 --pgo
      
  • En el futuro, cuando el desarrollo de Python 3.15 se estabilice, se distribuirán binarios oficiales

1 comentarios

 
GN⁺ 2025-12-26
Comentarios en Hacker News
  • Comparte el fragmento de código clave que le hubiera gustado ver incluido en la entrada del blog
    Es un ejemplo que muestra la diferencia en la definición de los atributos musttail y preserve_none entre MSVC y Clang
    Estos atributos deben colocarse en el declarador de la función, y no funcionan en la posición de especificador de función
    Enlace al código relacionado
    Da la impresión de que Microsoft solo informa de este tipo de funciones no públicas a los proyectos que considera importantes
    • Yo estaba equivocado. [[msvc::musttail]] en realidad era un atributo documentado oficialmente
      Va a corregir la entrada del blog para reflejarlo
      Comentario relacionado en HN
    • Plantea la duda de si “¿se los habrán dicho porque era importante, o porque les convenía a ellos?”
  • Recuerda el caso anterior en Python 3.14, cuando se reportó una mejora de rendimiento incorrecta debido a un bug de LLVM 19, y espera que esta vez no ocurra algo parecido
    Después de leer el texto, considera que está bien porque el enfoque priorizó la transparencia y la retroalimentación rápida
    Sería aún mejor con validación de compiladores cruzados o una auditoría independiente, pero cree que se puede confiar gracias a la transparencia total del autor
    • El autor recuerda que el error de aquella vez fue más bien un accidente afortunado
      Como se publicó temprano, Nelson encontró el bug de Clang 19 y eso permitió corregirlo antes del lanzamiento oficial
      Esta vez tiene más confianza porque hay dos mejoras: la lógica de dispatch y el inlining
      MSVC puede convertir un intérprete basado en switch-case en threaded code bajo ciertas condiciones, pero CPython es demasiado complejo para que esa optimización aplique
      En cambio, el enfoque con tail call le da más control al autor del código C
      Referencias relacionadas: condiciones de threaded code en MSVC, issue sobre forceinline
    • La ventaja del nuevo diseño es que depende menos de los caprichos de las optimizaciones del compilador
      Antes, optimizaciones como tail duplication variaban según el criterio del compilador, pero ahora el intérprete puede expresar directamente la forma de código máquina que quiere
      Enlace a una discusión anterior
  • Comparte una charla sobre problemas de compiladores del pasado, el video de la presentación en EuroPython 2025
  • Está creando una app GUI para Windows en Python por primera vez en mucho tiempo
    Eligió Python porque el ecosistema de VS es demasiado pesado frente a C#/MAUI
    Tkinter le resultó incómodo, y Qt tenía una curva de aprendizaje alta, así que usa la combinación wxGlade + wxPython
    Solo necesita una única dependencia instalable con pip y le gusta la sensación Pythonic
    Le alegran las mejoras en el runtime de Windows
    • Yo prefiero la combinación Python + Qt/PySide
      Si haces la UI rápido con QtCreator y luego conectas la lógica en Python, la velocidad de desarrollo es muy alta
    • También recomienda pyfltk. En GNU/Linux le funcionó bastante bien
    • Si la GUI es importante, LINQPad también podría valer la pena. Está en un punto intermedio entre scripting y trabajo pesado
    • Recomienda los bindings de ImGui para Python
      A diferencia de Tkinter o Qt, no usa retained mode sino immediate mode, así que es especialmente útil para herramientas internas
      Proyecto imgui_bundle
  • Se pregunta si “¿esto no sería una tarea de optimización de baja dificultad?”, y cuestiona por qué el bucle del intérprete todavía no está completamente optimizado
    Dice que pensaba que ya estaría escrito en ensamblador para las principales ISA
    • Más bien, cree que esta actualización demuestra que el bucle ya está extremadamente optimizado
      [[msvc::musttail]] es un atributo muy reciente, añadido en MSVC 14.50 (lanzado el mes pasado), y el equipo de CPython lo aprovechó en cuestión de semanas para mejorar el rendimiento
      Documentación de MSVC musttail
    • Python desde el principio priorizó la simplicidad por encima de la velocidad
      Como Guido priorizó la simplicidad del código, la adopción de un JIT llegó tarde, y después aparecieron intentos como PEP 744 (JIT Compilation)
    • No hay que tener expectativas excesivas sobre el código abierto
      La optimización en ensamblador es una pesadilla de mantenimiento, y el verdadero cuello de botella de Python es el sistema de empaquetado
    • De todos modos, menciona que quienes son sensibles al rendimiento no ejecutan Python en Windows
  • Plantea la pregunta de “¿por qué el intérprete de Python es mucho más lento que V8?”
    • JavaScript usa compilación JIT, pero CPython no
      PyPy es rápido gracias al JIT, pero no es compatible con extensiones en C
      El modelo de hilos de Python también dificulta las optimizaciones
      En cambio, JS es monohilo y más simple
      Como Python puede evitar parte del problema con extensiones en C, ha habido menos presión para optimizar el propio CPython
    • Considera que la gran diferencia está en el personal y la calidad de Google
      Además, CPython no puede romper compatibilidad por su enorme ecosistema de extensiones en C
      En cambio, V8 pudo cambiar libremente su estructura interna
    • Python es mucho más dinámico; incluso el acceso a atributos pasa por el protocolo descriptor
      Además, debe mantener un ABI C estable, lo que dificulta que un JIT analice el código con libertad
    • Python es un lenguaje mucho más dinámico que JS, y los bindings FFI limitan los cambios internos
      Menciona el caso de PyPy, que sufrió bastante para ajustarse a esas restricciones
    • JS fue optimizado con enormes recursos porque Google quería dominar la web, mientras que Python ha dedicado más recursos a la evolución del lenguaje
      Además, JS solo necesita soportar JS puro, mientras que Python tiene que mantener su ecosistema de extensiones externas, lo que impone muchas restricciones a la optimización
  • Menciona que el nuevo gráfico de benchmarks le pareció interesante y pregunta con qué herramienta fue generado
    Comparte que ha medido rendimiento de librerías JS con mitata
    PR de optimización de Immer JS
  • Comenta que Matt Godbolt mencionó que un intérprete basado en tail-call encaja mejor con el branch predictor del CPU
  • Dice que hay dos errores tipográficos en la primera oración del texto y pregunta si quizá fueron errores intencionales para que pareciera generado por IA
    • El autor responde: “gracias, ya lo corregí”
    • Otro usuario bromea con que, “si quieres que no parezca escrito por IA, solo escríbelo tú mismo
      Satiriza rasgos típicos de textos de IA, como párrafos cortos, tono demasiado positivo y poca profundidad
  • Pregunta: “si el equipo de Python considera útil el tail call, ¿podría llegar a incorporarse soporte para tail call en el propio lenguaje?”