¿Qué es un bloqueo global de intérprete y por qué es un problema?
Se ha hecho mucho ruido al eliminar el GIL de Python, y me gustaría entender por qué eso es tan importante. Nunca he escrito un compilador ni un intérprete, así que no seas frugal con los detalles, probablemente los necesite para entender.
python
python-internals
gil
e-satis
fuente
fuente
Respuestas:
Python GIL está destinado a serializar el acceso a intérpretes internos de diferentes hilos. En sistemas de múltiples núcleos, significa que múltiples hilos no pueden hacer uso efectivo de múltiples núcleos. (Si el GIL no condujo a este problema, la mayoría de las personas no se preocuparían por el GIL; solo se plantea como un problema debido a la creciente prevalencia de los sistemas de múltiples núcleos). Si desea comprenderlo en detalle, puedes ver este video o mirar este conjunto de diapositivas . Puede ser demasiada información, pero luego solicitó detalles :-)
Tenga en cuenta que Python de GIL solo es realmente un problema para CPython, la implementación de referencia. Jython y IronPython no tienen un GIL. Como desarrollador de Python, generalmente no te encuentras con el GIL a menos que estés escribiendo una extensión C. Los creadores de extensiones C necesitan liberar el GIL cuando sus extensiones bloquean las E / S, para que otros hilos en el proceso Python tengan la oportunidad de ejecutarse.
fuente
regex
,lxml
,numpy
módulos. Cython permite liberar GIL en código personalizado, por ejemplo,b2a_bin(data)
Supongamos que tiene múltiples hilos que realmente no tocan los datos del otro. Esos deberían ejecutarse de la manera más independiente posible. Si tiene un "bloqueo global" que necesita adquirir para (por ejemplo) llamar a una función, eso puede terminar como un cuello de botella. Puede terminar sin obtener muchos beneficios al tener múltiples hilos en primer lugar.
Para ponerlo en una analogía del mundo real: imagine a 100 desarrolladores trabajando en una empresa con una sola taza de café. La mayoría de los desarrolladores pasarían su tiempo esperando café en lugar de codificar.
Nada de esto es específico de Python: en primer lugar, no sé los detalles de para qué Python necesitaba un GIL. Sin embargo, con suerte le dará una mejor idea del concepto general.
fuente
Primero entendamos lo que proporciona Python GIL:
Cualquier operación / instrucción se ejecuta en el intérprete. GIL asegura que el intérprete esté en manos de un solo hilo en un instante de tiempo particular . Y su programa Python con múltiples hilos funciona en un solo intérprete. En cualquier instante particular de tiempo, este intérprete está en manos de un solo hilo. Significa que solo el hilo que contiene el intérprete se está ejecutando en cualquier momento .
Ahora, ¿por qué es eso un problema?
Su máquina podría tener múltiples núcleos / procesadores. Y múltiples núcleos permiten que múltiples hilos se ejecuten simultáneamente, es decir, múltiples hilos podrían ejecutarse en cualquier instante de tiempo en particular. . Pero dado que el intérprete está en manos de un solo hilo, otros hilos no están haciendo nada a pesar de que tienen acceso a un núcleo. Por lo tanto, no está obteniendo ninguna ventaja proporcionada por múltiples núcleos porque en un instante solo se está utilizando un solo núcleo, que es el núcleo utilizado por el hilo que actualmente contiene el intérprete. Por lo tanto, su programa tardará tanto en ejecutarse como si fuera un solo programa de subprocesos.
Sin embargo, las operaciones potencialmente de bloqueo o de larga duración, como E / S, procesamiento de imágenes y procesamiento de números NumPy, ocurren fuera del GIL. Tomado de aquí . Entonces, para tales operaciones, una operación multiproceso aún será más rápida que una operación de un solo subproceso a pesar de la presencia de GIL. Entonces, GIL no siempre es un cuello de botella.
Editar: GIL es un detalle de implementación de CPython. IronPython y Jython no tienen GIL, por lo que un programa verdaderamente multiproceso debería ser posible en ellos, pensé que nunca había usado PyPy y Jython y no estoy seguro de esto.
fuente
Python no permite subprocesos múltiples en el verdadero sentido de la palabra. Tiene un paquete de subprocesos múltiples, pero si desea varios subprocesos para acelerar su código, generalmente no es una buena idea usarlo. Python tiene una construcción llamada Global Interpreter Lock (GIL).
https://www.youtube.com/watch?v=ph374fJqFPE
El GIL se asegura de que solo uno de sus 'hilos' pueda ejecutarse a la vez. Un hilo adquiere el GIL, hace un poco de trabajo, luego pasa el GIL al siguiente hilo. Esto sucede muy rápido, por lo que para el ojo humano puede parecer que sus subprocesos se ejecutan en paralelo, pero en realidad solo se turnan para usar el mismo núcleo de CPU. Todo este paso de GIL agrega gastos generales a la ejecución. Esto significa que si desea que su código se ejecute más rápido, usar el paquete de subprocesos a menudo no es una buena idea.
Hay razones para usar el paquete de subprocesos de Python. Si desea ejecutar algunas cosas simultáneamente, y la eficiencia no es una preocupación, entonces está totalmente bien y conveniente. O si está ejecutando código que necesita esperar algo (como algunos IO), entonces podría tener mucho sentido. Pero la biblioteca de subprocesos no le permitirá usar núcleos de CPU adicionales.
El subprocesamiento múltiple se puede externalizar al sistema operativo (mediante el procesamiento múltiple), alguna aplicación externa que llame a su código de Python (por ejemplo, Spark o Hadoop), o algún código al que llame su código de Python (por ejemplo: podría tener su Python llamada de código a una función C que hace el costoso material de subprocesos múltiples).
fuente
Cada vez que dos hilos tienen acceso a la misma variable, tiene un problema. En C ++, por ejemplo, la forma de evitar el problema es definir un bloqueo de mutex para evitar que dos hilos entren, digamos, al mismo tiempo al establecedor de un objeto.
El subprocesamiento múltiple es posible en python, pero no se pueden ejecutar dos subprocesos al mismo tiempo con una granularidad más fina que una instrucción de python. El hilo en ejecución está obteniendo un bloqueo global llamado GIL.
Esto significa que si comienza a escribir un código multiproceso para aprovechar su procesador multinúcleo, su rendimiento no mejorará. La solución habitual consiste en ir multiproceso.
Tenga en cuenta que es posible liberar el GIL si está dentro de un método que escribió en C, por ejemplo.
El uso de un GIL no es inherente a Python sino a algunos de sus intérpretes, incluido el CPython más común. (#editado, ver comentario)
El problema de GIL todavía es válido en Python 3000.
fuente
Documentación de Python 3.7
También me gustaría destacar la siguiente cita de la documentación de Python
threading
:Esto enlaza con la entrada
global interpreter lock
del Glosario para la cual se explica que GIL implica que el paralelismo roscado en Python no es adecuado para tareas vinculadas a la CPU :Esta cita también implica que los dictados y, por lo tanto, la asignación de variables también son seguros para subprocesos como un detalle de implementación de CPython:
A continuación, los documentos del
multiprocessing
paquete. explican cómo supera el GIL al generar el proceso al tiempo que expone una interfaz similar a la dethreading
:Y los documentos
concurrent.futures.ProcessPoolExecutor
explican que se usamultiprocessing
como backend:que debería contrastarse con la otra clase base
ThreadPoolExecutor
que usa hilos en lugar de procesosde lo cual concluimos que
ThreadPoolExecutor
solo es adecuado para tareas vinculadas de E / S, mientrasProcessPoolExecutor
que también puede manejar tareas vinculadas a la CPU.La siguiente pregunta pregunta por qué existe el GIL en primer lugar: ¿Por qué el Global Interpreter Lock?
Experimentos de proceso vs hilo
En Multiprocessing vs Threading Python he hecho un análisis experimental de proceso vs threads en Python.
Vista previa rápida de los resultados:
fuente
Por qué Python (CPython y otros) usa el GIL
De http://wiki.python.org/moin/GlobalInterpreterLock
En CPython, el bloqueo global del intérprete, o GIL, es un mutex que evita que múltiples hilos nativos ejecuten códigos de bytes Python a la vez. Este bloqueo es necesario principalmente porque la administración de memoria de CPython no es segura para subprocesos.
¿Cómo eliminarlo de Python?
Al igual que Lua, quizás Python podría iniciar varias máquinas virtuales, pero python no hace eso, supongo que debería haber otras razones.
En Numpy o alguna otra biblioteca extendida de Python, a veces, liberar el GIL a otros subprocesos podría aumentar la eficiencia de todo el programa.
fuente
Quiero compartir un ejemplo del libro multihilo para efectos visuales. Así que aquí hay una situación clásica de bloqueo muerto
Ahora considere los eventos en la secuencia que resultan en un punto muerto.
fuente