- El steering council de Python expresó su intención de aprobar la PEP 703 para hacer que el GIL sea opcional a lo largo de varias versiones, y las condiciones finales aún se están afinando
- La compilación
--disable-gilde CPython 3.13 se prepara como experimental, y la stable ABI y la compatibilidad de wheels de módulos de extensión aparecen como los principales retos técnicos - Como los wheels
abi3actuales podrían no encajar tal cual en CPython 3.13 sin GIL, se está discutiendo introducirabi4, cambios en la C API limitada y convertir macros de conteo de referencias en llamadas a función - Existe preocupación de que
pippueda elegir mal los wheels para compilaciones con GIL y sin GIL, y una instalación incorrecta silenciosa sería más peligrosa que un simple fallo de instalación - Para el nombre de la compilación sin GIL se propuso free-threading en lugar de
nogil, pero también hay que resolver en conjunto el nombre del ejecutable, el shebang, la instalación en paralelo y el empaquetado en distribuciones
Dónde está hoy PEP 703 y el CPython sin GIL
- El steering council de Python expresó a fines de julio su intención de aprobar la PEP 703, que propone hacer opcional el bloqueo global del intérprete (GIL) en CPython
- Las condiciones detalladas de esa aprobación todavía no están definidas, pero la discusión sobre la implementación y la preparación del ecosistema ya están en marcha
- A largo plazo se contempla un camino hacia una sola versión de CPython sin GIL, pero por ahora la etapa es probar el funcionamiento sin GIL en un intérprete compilado con la opción
--disable-gil - CPython 3.13 está previsto para octubre de 2024, y la compilación sin GIL de esa versión tendrá carácter experimental
stable ABI y compatibilidad de módulos de extensión
- Sam Gross trató en el foro de discusión de Python cómo encajan la PEP 703 y la stable ABI de CPython
- La stable ABI permite que los módulos de extensión funcionen con el mismo wheel binario en varias versiones de CPython, evitando tener que recompilar en cada nueva versión
- Las extensiones compiladas para la stable ABI podrían no funcionar tal cual en la compilación sin GIL de CPython 3.13
- Para resolverlo, se propusieron algunas adiciones y cambios en la C API limitada
- Las extensiones que usan solo la C API limitada pueden crear binarios que usen la stable ABI
- Entre los cambios propuestos está un plan ya existente para convertir algunas macros que incrementan o decrementan el conteo de referencias de objetos en llamadas a función
- El objetivo es hacer posible binarios de extensiones que funcionen tanto en compilaciones con GIL como sin GIL
abi3, abi4 y el problema de selección de wheels
- Victor Stinner considera que, para que el experimento sin GIL tenga éxito, hace falta una solución simple para extensiones que funcionen en ambos tipos de intérprete
- Como las extensiones compiladas para la stable ABI en CPython 3.12 o anterior no son compatibles con compilaciones sin GIL a partir de 3.13, surgió la idea de crear una nueva versión ABI: abi4
- La stable ABI actual es
abi3 - El número de ABI no necesariamente está ligado al número de versión principal de CPython
- La stable ABI actual es
- Gross considera aceptable que las extensiones que quieran soportar no-GIL tengan que asumir en cierta medida la carga de crear dos wheels binarios
- Le preocupa más que el proyecto no-GIL termine demasiado atado a mejoras en la C API y la stable ABI
- Alex Gaynor, que también mantiene varios paquetes con wheels
abi3, cree que generar dos wheels una vez no es una carga excesiva- Pero sí es importante que
pip, actual y futuro, elija el wheel correcto entre ambos
- Pero sí es importante que
- Brett Cannon cree que la lógica actual de
pipno distingue entre esas dos variantes, así que sin un cambio comoabi4, las versiones actuales y antiguas depipno se comportarán correctamente
Preocupación por fallos silenciosos en pip
- Gross considera que no hace falta preocuparse demasiado por el soporte de
pipviejo en la compilación experimental--disable-gilde CPython 3.13- Señala que es común que
pipantiguo se rompa con nuevas versiones de Python - Puso como ejemplo el caso en que
pip==23.1.1o anterior falla en CPython 3.13 por la ausencia depkgutil.ImpImporter
- Señala que es común que
- El mantenedor de
pip, Paul Moore, considera que romperse de forma explícita y instalar silenciosamente el paquete equivocado son problemas distintos- Hay usuarios que usan versiones antiguas de
pip - El impacto para el usuario no es el mismo entre un fallo explícito y un error silencioso
- Hay usuarios que usan versiones antiguas de
- Moore teme que si quienes quieran probar compilaciones no-GIL o free-threaded tienen que depurar problemas de compatibilidad ABI, eso les quite las ganas de seguir probando
- Gaynor también cree que si
pipfalla en silencio en paquetes afectados, podrían llegar una avalancha de reportes
Instalación en paralelo y nombre del ejecutable
- Barry Warsaw preguntó si hay planes para instalar lado a lado compilaciones con GIL y sin GIL en el mismo sistema
- Gross respondió que la situación es comparable a instalar distintas versiones de Python
- Cannon considera posible incluir ambos binarios en un solo wheel “fat”
- Pero los nombres de los binarios dentro del wheel tendrían que ser distintos
- La discusión sobre el nombre del ejecutable continuó en otro hilo
- Paul Moore considera que los usuarios deberían poder probar fácilmente el modo sin GIL y elegir con facilidad entre GIL y no-GIL
- Si ese proceso es difícil en Windows, macOS o Linux, podría afectar negativamente al proyecto no-GIL
- Cuanto más fácil sea probarlo, mayor será la demanda de compilaciones sin GIL y también la presión sobre mantenedores de paquetes para ofrecer wheels compatibles
Debate de nombres: nogil vs free-threading
- Barry Scott considera importante el nombre del ejecutable porque en la línea shebang hay que indicar qué intérprete invocar
- Dio como ejemplo nombres como
python-nogil3ypython-nogil3.13
- Dio como ejemplo nombres como
- Gregory P. Smith expresó la opinión personal de que, como la compilación sin GIL de CPython 3.13 es una función experimental, las distribuciones no deberían ponerla en el
$PATHpredeterminado- Tampoco le parece deseable que un nombre largo de ejecutable quede fijado en shebangs durante mucho tiempo
- Propuso posponer la decisión del nombre de instalación hasta después de 3.14
- El desarrollador de Fedora Petr Viktorin señaló que es bastante probable que las distribuciones quieran empaquetar un intérprete sin GIL para que los usuarios experimenten
- Moore considera deseable poder indicar una compilación free-threaded con algo como
#!/usr/bin/env python3.13-nogil- La necesidad surge de evitar rutas largas y poco intuitivas hardcodeadas
- En un hilo sobre el instalador de Windows iniciado por Steve Dower, Smith explicó que el steering council quiere evitar el nombre
nogil- La razón es que para la mayoría de desarrolladores fuera del core no comunica bien la idea, no deberían necesitar saber qué es el GIL y además usa una formulación negativa
- Como alternativa se propuso el término free-threading
- Gross cree que
free-threadingtampoco es fácil de entender para quienes están fuera del tema y que no es un término de uso amplio - En la discusión real hubo una clara preferencia por nombres cortos, y en ese aspecto
nogilera el candidato más fuerte - Un cambio concreto ya reflejado fue modificar la etiqueta ABI de la compilación no-GIL de
nat- La
tsignifica threading
- La
La propuesta de abi4 y el trabajo pendiente
- Gross y Viktorin discutieron los puntos problemáticos de la propuesta de cambios en la API, y ese feedback llevó a la propuesta de una nueva ABI: abi4
- Gross creó un prototipo de la nueva ABI
- Viktorin coincide en términos generales con el enfoque, aunque cree que todavía hay que ordenar mejor varios detalles
- Stinner considera que hará falta una PEP para abi4, y Viktorin lo toma como una discusión pre-PEP
- Hay confusión sobre las garantías de compatibilidad que ofrece la combinación de versión de la C API limitada con
abi3, y eso también afecta la dirección deabi4 - La investigación relacionada sigue en curso, y es posible que haya discusión presencial en el sprint de core developers de mediados de octubre
Redacción final de la aprobación e impacto a largo plazo
- El trabajo en CPython no-GIL o free-threaded sigue avanzando, pero la aprobación final de la PEP 703 todavía está pendiente
- La demora ya se alargó un poco, pero la PEP 703 y sus efectos podrían influir fuertemente en el desarrollo y el ecosistema de CPython durante más de cinco años
- El steering council quiere dejar claros los criterios de aprobación
- Thomas Wouters señaló que está puliendo la redacción exacta de la aprobación y tratando de aclarar varias decisiones
- Parte de ese trabajo también podría avanzar durante el sprint de core developers
1 comentarios
Opiniones de Hacker News
Al ver las computadoras modernas, me da la impresión de que el paralelismo explícito podría ser un elemento más fundamental de la informática de lo que está de moda en los libros de texto.
Quizá ya estemos en una etapa en la que siempre tengamos que escribir código paralelo explícitamente.
Por ejemplo, los bucles
forestán siendo reemplazados por operaciones comoforeach,mapyfilter. Estas expresiones le indican al compilador/intérprete la intención de aplicar una operación a todos los elementos de una estructura de datos, y dejan en manos del compilador/runtime si paralelizarlo y cómo hacerlo.En la ejecución de servicios web, cada solicitud es lo bastante rápida, y el verdadero beneficio del paralelismo está en procesar muchas solicitudes en paralelo. Ahí encaja No-GIL.
Cuando dentro de una sola solicitud hay muchas subsolicitudes, normalmente se maneja con código asíncrono, pero muchas veces eso se debe más a que crear hilos es caro o a que los pools de hilos son engorrosos que a las ventajas de rendimiento de lo asíncrono. Lo asíncrono es bueno para el throughput, pero malo para la latencia, y si se paralelizan solicitudes de un servicio, normalmente preocupa más la latencia. Lo asíncrono ganó sobre todo por usabilidad.
Otro tipo de paralelismo aparece en trabajos offline a gran escala. Cosas como MapReduce o Presto, que en general se parecen a problemas de divide y vencerás. El entrenamiento de modelos en GPU es similar.
Lo que no ocurrió fueron los algoritmos locales altamente paralelos. En los servicios web, el tamaño de los datos es pequeño, la ganancia de latencia es baja, la implementación es compleja y el costo de coordinación entre hilos aumenta. Una pequeña excepción son los algoritmos vectorizados, pero se ejecutan en un solo núcleo y no tienen overhead de coordinación; la inferencia online también vuelve a estar muy fuertemente vectorizada.
Con el tiempo, ambos están mejorando. Así como más lenguajes y bibliotecas se vuelven seguros por defecto, ahora más cosas son paralelas por defecto. Aún falta camino, pero creo que fue bueno no haberlo hecho demasiado pronto, porque la tecnología mejoró mucho en los últimos 10 años.
Por ejemplo, se puede comparar lo que se puede hacer de forma segura con Rayon de Rust con lo que se hacía de forma insegura con OpenMP en C++.
Más hacia afuera también están estas cosas en las que trabajo: https://legion.stanford.edu/, https://regent-lang.org/, https://github.com/nv-legate/cunumeric
Como es un detalle de implementación, si se puede abstraer para que sea más fácil de aprovechar, deberíamos hacerlo.
En comparación, un mutex ronda los 25 nanosegundos y aumenta si hay contención, pero un mutex es sincronización punto a punto.
Lo bueno de Disruptor es que varios hilos pueden recibir el mismo mensaje sin mucho esfuerzo adicional.
https://github.com/LMAX-Exchange/disruptor/wiki/Performance-...
https://gist.github.com/rmacy/2879257
Sueño con un lenguaje parecido a Smalltalk, pero que se mantenga en un solo hilo hasta que paralelizar tenga sentido.
Estoy buscando problemas de paralelismo que no sean de big data. El paralelismo se parece más a poner más autos en la carretera que a aumentar la velocidad de un auto. Pero todavía estoy tratando de encontrar qué cosas deberían hacer localmente los usuarios de escritorio o móviles aprovechando la potencia matemática de sus computadoras.
También estoy pensando en Itanium y las arquitecturas VLIW como ideas de paralelismo.
Se puede usar
-ng, con el sentido de no-gil o next-generation.Había nuevos flags de compilador, nuevos flags de linker, enlace con bibliotecas distintas e incluso el uso de comandos de compilador totalmente diferentes. AIX era especialmente así.
Para el problema del shebang, quizá convenga apoyarse en la convención existente de Python:
from __future__ import nogil.En ese momento se podría hacer hot-swap del intérprete.
from __future__ importno es una sentencia de runtime, sino una sentencia especial que representa flags.https://docs.python.org/3/reference/simple_stmts.html#future...
Las sentencias future son por módulo, y GIL/no-GIL no encaja fácilmente en ese modelo.
Cada vez que veo esta propuesta, me pregunto cómo van a garantizar que los programas sigan funcionando correctamente. Gran parte del código Python multihilo existente está escrito de forma insegura
En particular, el problema son las carreras de datos, algo que he visto repetidamente en bases de código de varias empresas y en proyectos de código abierto. Esos programas no se rompen solo porque dependen implícitamente de que el GIL permite ejecutar a un solo hilo a la vez
Si desaparece el GIL, esos programas se van a romper. Como Python es un lenguaje de tipado dinámico, dudo mucho que pueda existir un analizador estático que encuentre estos problemas en programas Python existentes
Lo más probable son bugs sutiles que aparecen de forma no determinista en runtime. Ojalá al menos provocaran crashes, pero esta clase de bugs probablemente termine en comportamientos incorrectos
Quizá esta propuesta sin GIL no esté pensada para usarse en la mayoría de los programas. Tal vez sea una herramienta ultraespecializada para un conjunto muy reducido de situaciones en las que el programador sabe que no hay GIL y puede escribir el código en consecuencia
El GIL solo significa que un único hilo puede ejecutar bytecode de Python a la vez. Un intérprete con GIL también puede cambiar de hilo entre bytecodes, y muchas operaciones de Python requieren varios bytecodes. Eso incluye métodos incorporados de tipos incorporados que mucha gente considera “atómicos”
Por eso Python ofrece actualmente locks, mutexes, semáforos y cosas por el estilo, aun teniendo GIL
Los hilos que compiten por el GIL ya pueden quitárselo unos a otros en malos momentos y generar caos
Si el programa solo se ejecuta sin GIL cuando todas sus dependencias lo permiten, debería haber tiempo suficiente para corregir esos bugs
Entonces, probablemente no se empiece a tratar este problema a gran escala hasta cerca de 2030. Tampoco se ven muchas producciones que actualicen de inmediato el runtime que usan a la versión más reciente
No quiero sonar duro, pero el Steering Council dijo que no quiere otra migración de 2 a 3, así que la gente no va a actualizar a la ligera. Gran parte de lo que hay hoy en línea podría ser peligroso para copiar y pegar
En el código Python real hay muchísimos bugs de threading
¿OCaml no pasó por una evolución parecida? Me pregunto si hay puntos comparables entre ambos proyectos
Así, la API de hilos existente crea hilos dentro del domain actual y permite aislar el código que espera poder tomar un lock. El código nuevo, en cambio, puede crear un nuevo domain que empieza con un solo hilo. También se pueden usar ambos intencionalmente como forma de scheduling
Python está intentando hacer que el lock sea completamente opcional de forma global, fuera del control de los autores de librerías. Dicho eso, parece que en Python se garantiza que el lock solo protege al propio runtime, así que la mayoría del código que depende de ese lock probablemente ya tenga bugs de todos modos; por eso el plan de Python también parece viable
Si hay algo en común, sería encontrar y corregir estado compartido inesperado en toda la base de código del runtime, y tener que revisar la ABI de C
Ahora Python también tiene oportunidad de alcanzar a Tcl en rendimiento multihilo: https://www.hammerdb.com/blog/uncategorized/why-tcl-is-700-f...
Preferiría portar código Python a Mojo para obtener multithreading, SIMD y otras mejoras de velocidad
¿No quieres mover código Python a Python nogil? Entonces te downvotean, algo así
Si hubiera que ponerle nombre, podrían ser cosas como
python4,python3-gilfoil,python3-gilfreeEl enfoque actual en Python sin GIL se siente bastante extraño. El equipo de Faster CPython se puso la ambiciosa meta de mejorar el rendimiento de CPython en un 50% en cada release
En 3.11 sí hubo mejoras reales, pero quedaron muy lejos del 50%, y en bastantes de nuestras pruebas 3.12 fue similar o incluso más lento. El multithreading real sería genial, pero antes preferiría mucho más que mejorara el rendimiento single-thread
Por supuesto, reconozco que nuestras necesidades quizá no representen a todos, y agradezco todo el trabajo que se ha hecho para convertir a Python en un gran lenguaje. Aun así, me pregunto qué me estoy perdiendo
Actualmente, el uso de múltiples cores se hace mediante multiprocessing, que tiene muchas limitaciones. Entiendo que varios intérpretes podrían llegar junto con algo como corrutinas, pero aun así me gusta más la opción de multithreading real
En nogil Python, por ejemplo, varios threads pueden llamar código C con estado compartido accesible como objetos de Python. Esto es bastante central para machine learning y, de hecho, la forma actual de este PEP vino del equipo de PyTorch
El rendimiento single-thread también es importante, pero para las secciones críticas ya existían alternativas bastante decentes como numba, Cython y Mojo
El orden también importa. Si se introduce nogil, una parte considerable del trabajo de faster CPython podría terminar descartándose por completo, así que los equipos tenían que coordinarse
En un mundo ideal, habría tanto modo nogil como mejoras de rendimiento single-thread. Guido también insinuó que está considerando un JIT sofisticado
Python hace que sea muy cómodo manejar abstracciones de bajo nivel desde un lenguaje de más alto nivel. Por eso, como desarrollador de Python de muchos años, el GIL nunca me estresó demasiado
Si hubiera que elegir solo uno de los dos, estoy de acuerdo en que para la mayoría de los casos de uso encaja mejor simplemente tener código single-thread más rápido. Pero tampoco hay razón para no tener ambos
Con hindsight es claro, pero si el lado de Python hubiera sabido lo larga y dolorosa que sería la transición de 2 a 3, creo que también habría rehecho mucho más las partes internas del intérprete
Incluso después de una transición de 12 años, el rendimiento single-thread sigue siendo pésimo, y todavía quedan varias transiciones dolorosas para llegar al multithreading real
Sé que hay que ser amable con el desarrollo open source, pero me pregunto a partir de qué punto se puede llamar a esto un lenguaje muy mal gestionado
Las peores partes de Python son las que son difíciles de cambiar porque Python es demasiado popular y su ecosistema es demasiado grande. Por eso, todo tipo de cambio se vuelve más difícil por la compatibilidad hacia atrás
Hay una tendencia a defender Python demasiado rápido. Es importante verlo objetivamente, sin sesgos
Los proyectos que quieran rendimiento y sintaxis de Python podrían irse hacia allá. El Python actual parece estar tambaleándose entre varios objetivos sin lograr bien ninguno
Perl 5/6 se citaba como ejemplo. Incluso cuando quedó claro que nadie iba a hacer la transición, pasaron unos 5 años más hasta que se intentó hacerla más fácil