Python ha recibido mucha atención últimamente. La versión 3.13, prevista para octubre de este año, iniciará el enorme trabajo de eliminar el GIL. Ya está disponible una versión preliminar para los usuarios curiosos que quieran probar un Python (casi) sin GIL.
Todo este revuelo me hizo profundizar en mi propio lenguaje, ArkScript, ya que también tenía un Global VM Lock en el pasado (agregado en la versión 3.0.12, en 2020, eliminado en 3.1.3 en 2022), para compara cosas y me obliga a profundizar en el cómo y el por qué de Python GIL.
Un bloqueo de intérprete global (GIL) es un mecanismo utilizado en intérpretes de lenguajes informáticos para sincronizar la ejecución de subprocesos de modo que solo un subproceso nativo (por proceso) pueda ejecutar operaciones básicas (como asignación de memoria y recuento de referencias) a la vez. tiempo.
Wikipedia: bloqueo global del intérprete
Simultaneidad es cuando dos o más tareas pueden iniciarse, ejecutarse y completarse en períodos de tiempo superpuestos, pero eso no significa que ambas se ejecutarán simultáneamente.
Paralelismo es cuando las tareas se ejecutan literalmente al mismo tiempo, por ejemplo, en un procesador multinúcleo.
Para obtener una explicación detallada, consulte esta respuesta de Stack Overflow.
El GIL puede aumentar la velocidad de los programas de un solo subproceso porque no es necesario adquirir y liberar bloqueos en todas las estructuras de datos: todo el intérprete está bloqueado, por lo que usted está seguro de forma predeterminada.
Sin embargo, dado que hay un GIL por intérprete, eso limita el paralelismo: ¡necesita generar un intérprete completamente nuevo en un proceso separado (usando el módulo de multiprocesamiento en lugar de subprocesos) para usar más de un núcleo! Esto tiene un costo mayor que simplemente generar un nuevo hilo porque ahora tiene que preocuparse por la comunicación entre procesos, lo que agrega una sobrecarga no despreciable (consulte GeekPython: GIL se vuelve opcional en Python 3.13 para obtener puntos de referencia).
En el caso de Python, se debe a que la implementación principal, CPython, no tiene administración de memoria segura para subprocesos. Sin GIL, el siguiente escenario generaría una condición de carrera:
Si el hilo 1 se ejecuta primero, el recuento será 11 (recuento * 2 = 10, luego recuento 1 = 11).
Si el hilo 2 se ejecuta primero, el recuento será 12 (recuento 1 = 6, luego recuento * 2 = 12).
El orden de ejecución importa, pero puede suceder algo peor: si ambos subprocesos leen el conteo al mismo tiempo, uno borrará el resultado del otro y el conteo será 10 o 6.
En general, tener un GIL hace que la implementación (CPython) sea más fácil y rápida en casos generales:
También facilita el empaquetado de bibliotecas C, porque tienes garantizada la seguridad de los subprocesos gracias a GIL.
La desventaja es que tu código es asincrónico como en concurrente, pero no paralelo.
[!NOTA]
¡Python 3.13 está eliminando el GIL!El PEP 703 agregó una configuración de compilación --disable-gil para que al instalar Python 3.13, puedas beneficiarte de mejoras de rendimiento en programas multiproceso.
En Python, las funciones deben tomar un color: son "normales" o "asíncronas". ¿Qué significa esto en la práctica?
>>> def foo(call_me): ... print(call_me()) ... >>> async def a_bar(): ... return 5 ... >>> def bar(): ... return 6 ... >>> foo(a_bar):2: RuntimeWarning: coroutine 'a_bar' was never awaited RuntimeWarning: Enable tracemalloc to get the object allocation traceback >>> foo(bar) 6
Debido a que una función asincrónica no devuelve un valor inmediatamente, sino que invoca una corrutina, no podemos usarlas en todas partes como devoluciones de llamada, a menos que la función que estamos llamando esté diseñada para aceptar devoluciones de llamada asíncronas.
Obtenemos una jerarquía de funciones, porque las funciones "normales" deben hacerse asíncronas para usar la palabra clave await, necesaria para llamar a funciones asíncronas:
can call normal -----------> normal can call async - -----------> normal | .-----------> async
Aparte de confiar en la persona que llama, no hay forma de saber si una devolución de llamada es asíncrona o no (a menos que intentes llamarla primero dentro de un bloque try/except para comprobar si hay una excepción, pero eso es feo).
Al principio, ArkScript usaba un bloqueo global de VM (similar al GIL de Python), porque el módulo http.arkm (usado para crear servidores HTTP) era multiproceso y causaba problemas con la VM de ArkScript al alterar su estado mediante la modificación de variables. y llamar a funciones en múltiples subprocesos.
Luego, en 2021, comencé a trabajar en un nuevo modelo para manejar el estado de la VM para que pudiéramos paralelizarlo fácilmente y escribí un artículo al respecto. Posteriormente se implementó a fines de 2021 y se eliminó el bloqueo global de VM.
ArkScript no asigna un color a las funciones asíncronas, porque no existen en el lenguaje: o tienes una función o un cierre, y ambos pueden llamarse entre sí sin ninguna sintaxis adicional (un cierre es un objeto pobre, en este idioma: una función que mantiene un estado mutable).
Cualquier función se puede hacer asincrónica en el sitio de llamada (en lugar de declaración):
(let foo (fun (a b c) ( a b c))) (print (foo 1 2 3)) # 6 (let future (async foo 1 2 3)) (print future) # UserType (print (await future)) # 6 (print (await future)) # nil
Usando el async incorporado, estamos generando un std::future bajo el capó (aprovechando std::async y threads) para ejecutar nuestra función dado un conjunto de argumentos. Luego podemos llamar a await (otra función incorporada) y obtener un resultado cuando queramos, lo que bloqueará el subproceso actual de la VM hasta que la función regrese.
Por lo tanto, es posible esperar desde cualquier función y desde cualquier hilo.
Todo esto es posible porque tenemos una única VM que opera en un estado contenido dentro de un Ark::internal::ExecutionContext, que está vinculado a un solo hilo. ¡La VM se comparte entre los subprocesos, no los contextos!
.---> thread 0, context 0 | ^ VM thread 1, context 1
Al crear un futuro usando async, somos:
Esto prohíbe cualquier tipo de sincronización entre subprocesos ya que ArkScript no expone referencias ni ningún tipo de bloqueo que pueda compartirse (esto se hizo por razones de simplicidad, ya que el lenguaje pretende ser algo minimalista pero aún utilizable).
Sin embargo, este enfoque no es mejor (ni peor) que el de Python, ya que creamos un nuevo subproceso por llamada y la cantidad de subprocesos por CPU es limitada, lo cual es un poco costoso. Afortunadamente, no veo eso como un problema que deba abordarse, ya que nunca se deben crear cientos o miles de subprocesos simultáneamente ni llamar a cientos o miles de funciones asíncronas de Python simultáneamente: ambos resultarían en una enorme desaceleración de su programa.
En el primer caso, esto ralentizaría su proceso (incluso la computadora), ya que el sistema operativo hace malabarismos para darle tiempo a cada hilo; en el segundo caso, es el programador de Python el que tendría que hacer malabares entre todas sus rutinas.
[!NOTA]
Fuera de la caja, ArkScript no proporciona mecanismos para la sincronización de subprocesos, pero incluso si pasamos un tipo de usuario (que es un contenedor encima de objetos C borrados de tipo ) a una función, el objeto subyacente no es No se copió.
Con un poco de codificación cuidadosa, se podría crear un bloqueo utilizando la construcción UserType, que permitiría la sincronización entre subprocesos.(let lock (module:createLock)) (let foo (fun (lock i) { (lock true) (print (str:format "hello {}" i)) (lock false) })) (async foo lock 1) (async foo lock 2)
ArkScript y Python usan dos tipos muy diferentes de async/await: el primero requiere el uso de async en el sitio de la llamada y genera un nuevo hilo con su propio contexto, mientras que el segundo requiere que el programador marque las funciones como asíncronas para podrá usar await, y esas funciones asíncronas son corrutinas que se ejecutan en el mismo hilo que el intérprete.
Originalmente de leexp.lt
Descargo de responsabilidad: Todos los recursos proporcionados provienen en parte de Internet. Si existe alguna infracción de sus derechos de autor u otros derechos e intereses, explique los motivos detallados y proporcione pruebas de los derechos de autor o derechos e intereses y luego envíelos al correo electrónico: [email protected]. Lo manejaremos por usted lo antes posible.
Copyright© 2022 湘ICP备2022001581号-3