¿Por qué Global Interpreter Lock?

89

¿Cuál es exactamente la función del bloqueo de intérprete global de Python? ¿Otros lenguajes que se compilan para bytecode emplean un mecanismo similar?

Federico A. Ramponi
fuente
6
También debería preguntar "¿Importa?"
S.Lott
2
Estoy de acuerdo, considero que no es un problema ahora que en 2.6 se agregó el módulo de multiprocesamiento para permitirle programar usando múltiples procesos de una manera similar a un hilo. docs.python.org/library/multiprocessing.html
monkut
¿Qué es el Gil ?: stackoverflow.com/questions/1294382/… Relacionado con programadores: softwareengineering.stackexchange.com/questions/186889/…
Ciro Santilli 郝海东 冠状 病 六四 事件 法轮功

Respuestas:

69

En general, para cualquier problema de seguridad de subprocesos, deberá proteger sus estructuras de datos internas con bloqueos. Esto se puede hacer con varios niveles de granularidad.

  • Puede usar un bloqueo de grano fino, donde cada estructura separada tiene su propio bloqueo.

  • Puede utilizar un bloqueo de grano grueso donde un bloqueo protege todo (el enfoque GIL).

Hay varios pros y contras de cada método. El bloqueo detallado permite un mayor paralelismo: dos subprocesos se pueden ejecutar en paralelo cuando no comparten ningún recurso. Sin embargo, hay una sobrecarga administrativa mucho mayor. Para cada línea de código, es posible que deba adquirir y liberar varios bloqueos.

El enfoque de grano grueso es el opuesto. No se pueden ejecutar dos subprocesos al mismo tiempo, pero un subproceso individual se ejecutará más rápido porque no hace tanta contabilidad. En última instancia, todo se reduce a un compromiso entre la velocidad de un solo subproceso y el paralelismo.

Ha habido algunos intentos de eliminar el GIL en Python, pero la sobrecarga adicional para las máquinas de un solo subproceso era generalmente demasiado grande. Algunos casos pueden ser más lentos incluso en máquinas multiprocesador debido a la contención de bloqueo.

¿Otros lenguajes que se compilan para código de bytes emplean un mecanismo similar?

Varía y probablemente no debería considerarse una propiedad del lenguaje tanto como una propiedad de implementación. Por ejemplo, existen implementaciones de Python como Jython e IronPython que utilizan el enfoque de subprocesamiento de su VM subyacente, en lugar de un enfoque GIL. Además, la próxima versión de Ruby parece estar en movimiento. hacia la introducción de un GIL.

Brian
fuente
1
¿Puede explicar esto: 'No se pueden ejecutar dos subprocesos al mismo tiempo'? Recientemente escribí un servidor web simple en Python con multiproceso. Para cada nueva solicitud del cliente, los servidores generan un nuevo hilo y esos hilos continúan ejecutándose. Entonces, habrá varios subprocesos ejecutándose al mismo tiempo, ¿verdad? ¿O lo he entendido mal?
avi
1
Los subprocesos de @avi AFAIK python no pueden ejecutarse simultáneamente, pero eso no significa que un subproceso deba bloquear al otro. GIL solo significa que solo un hilo puede interpretar el código de Python a la vez, no significa que la administración de hilos y la asignación de recursos no funcionen.
Benproductions1
2
^ entonces, en cualquier momento, solo un hilo estará sirviendo contenido al cliente ... así que no tiene sentido usar el multihilo para mejorar el rendimiento. ¿Correcto?
avi
Y, por supuesto, Java está compilado en código de bytes y permite un bloqueo de grano muy fino.
Warren Dew
3
@avi, un proceso vinculado a IO como un servidor web aún puede beneficiarse de los hilos de Python. Dos o más subprocesos pueden hacer IO simultáneamente. Simplemente no se pueden interpretar (CPU) simultáneamente.
Saish
33

Lo siguiente es del Manual de referencia oficial de la API de Python / C :

El intérprete de Python no es completamente seguro para subprocesos. Para admitir programas de Python de subprocesos múltiples, existe un bloqueo global que debe mantener el subproceso actual antes de que pueda acceder de forma segura a los objetos de Python. Sin el bloqueo, incluso las operaciones más simples podrían causar problemas en un programa de subprocesos múltiples: por ejemplo, cuando dos subprocesos incrementan simultáneamente el recuento de referencias del mismo objeto, el recuento de referencias podría terminar incrementándose solo una vez en lugar de dos.

Por lo tanto, existe la regla de que solo el subproceso que ha adquirido el bloqueo de intérprete global puede operar en objetos de Python o llamar a funciones de API de Python / C. Para admitir programas de Python de múltiples subprocesos, el intérprete libera y vuelve a adquirir el bloqueo con regularidad; de forma predeterminada, cada 100 instrucciones de código de bytes (esto se puede cambiar con sys.setcheckinterval ()). El bloqueo también se libera y se vuelve a adquirir alrededor del bloqueo potencial de las operaciones de E / S como leer o escribir un archivo, de modo que otros subprocesos puedan ejecutarse mientras el subproceso que solicita la E / S espera a que se complete la operación de E / S.

Creo que resume bastante bien la cuestión.

Eli Bendersky
fuente
1
Yo también lo leí, pero no puedo entender por qué Python es diferente en este aspecto de, digamos, java (¿lo es?)
Federico A. Ramponi
@EliBendersky Los subprocesos de Python se implementan como pthreads y son manejados por el sistema operativo ( dabeaz.com/python/UnderstandingGIL.pdf ) mientras que los subprocesos de Java son subprocesos de nivel de aplicación cuya programación es manejada por la JVM
gokul_uf
19

El bloqueo de intérprete global es un gran bloqueo de tipo mutex que evita que los contadores de referencia se manguen. Si está escribiendo código Python puro, todo esto sucede detrás de escena, pero si incrusta Python en C, es posible que tenga que tomar / liberar explícitamente el bloqueo.

Este mecanismo no está relacionado con la compilación de Python en código de bytes. No es necesario para Java. De hecho, ni siquiera es necesario para Jython (python compilado en jvm).

ver también esta pregunta

David Nehme
fuente
4
"Este mecanismo no está relacionado con la compilación de Python en bytecode": Precisamente, es un artefacto de la implementación de CPython. Otras implementaciones (como Jython que ha mencionado) pueden estar libres de esta restricción en virtud de su implementación segura para subprocesos
Eli Bendersky
11

Python, como perl 5, no fue diseñado desde cero para ser seguro para subprocesos. Los subprocesos se injertaron después del hecho, por lo que el bloqueo del intérprete global se usa para mantener la exclusión mutua cuando solo un subproceso está ejecutando código en un momento dado en las entrañas del intérprete.

Los subprocesos de Python individuales son multitarea cooperativamente por el propio intérprete al ciclar el bloqueo de vez en cuando.

Es necesario agarrar el candado usted mismo cuando está hablando con Python desde C cuando otros subprocesos de Python están activos para 'participar' en este protocolo y asegurarse de que no ocurra nada inseguro a sus espaldas.

Otros sistemas que tienen una herencia de un solo subproceso que luego evolucionaron a sistemas de múltiples subprocesos a menudo tienen algún mecanismo de este tipo. Por ejemplo, el kernel de Linux tiene el "Big Kernel Lock" de sus primeros días de SMP. Gradualmente, con el tiempo, a medida que el rendimiento de subprocesos múltiples se convierte en un problema, existe una tendencia a tratar de romper este tipo de bloqueos en piezas más pequeñas o reemplazarlos con algoritmos sin bloqueos y estructuras de datos donde sea posible para maximizar el rendimiento.

Edward KMETT
fuente
+1 por mencionar el hecho de que el bloqueo de grano grueso se usa de lo que la mayoría piensa, especialmente el BKL a menudo olvidado (yo uso reiserfs, la única razón real por la que lo sé).
new123456
3
Linux tenía BKL, desde la versión 2.6.39, BKL se ha eliminado por completo.
AVI
5
Por supuesto. Eso sí, eso fue ~ 3 años después de que respondí la pregunta. =)
Edward KMETT
7

Con respecto a su segunda pregunta, no todos los lenguajes de scripting usan esto, pero solo los hace menos poderosos. Por ejemplo, los hilos en Ruby son verdes. y no nativos.

En Python, los subprocesos son nativos y GIL solo evita que se ejecuten en diferentes núcleos.

En Perl, los hilos son aún peores. Simplemente copian todo el intérprete y están lejos de ser tan utilizables como en Python.

Eli Bendersky
fuente
2

Quizás este artículo del BDFL ayude.

Jeremy Cantrell
fuente