¿Por qué se escribió Python con la GIL?

104

El bloqueo global de intérpretes (GIL) parece citarse a menudo como una de las principales razones por las que el subprocesamiento y similares es un poco complicado en Python, lo que plantea la pregunta "¿Por qué se hizo eso en primer lugar?"

Al no ser un programador, no tengo ni idea de por qué podría ser eso: ¿cuál era la lógica detrás de poner el GIL?

    
pregunta Fomite 13.02.2013 - 02:14
fuente

3 respuestas

100

Hay varias implementaciones de Python, por ejemplo, CPython, IronPython, RPython, etc.

Algunos de ellos tienen un GIL, otros no. Por ejemplo, CPython tiene la GIL:

De enlace

Las aplicaciones escritas en lenguajes de programación con un GIL pueden diseñarse para usar procesos separados para lograr un paralelismo completo, ya que cada proceso tiene su propio intérprete y, a su vez, tiene su propio GIL.

Beneficios de la GIL

  • Mayor velocidad de los programas de un solo hilo.
  • Fácil integración de bibliotecas de C que normalmente no son seguras para subprocesos.

Por qué Python (CPython y otros) usa la GIL

En CPython, el bloqueo global del intérprete, o GIL, es un mutex que impide que varios subprocesos nativos ejecuten códigos de bytes de Python a la vez. Este bloqueo es necesario principalmente porque la administración de memoria de CPython no es segura para subprocesos.

El GIL es controvertido porque evita que los programas de multiproceso de CPython aprovechen al máximo los sistemas multiprocesador en ciertas situaciones. Tenga en cuenta que las operaciones de bloqueo o de ejecución prolongada, como E / S, procesamiento de imágenes y procesamiento de números NumPy, ocurren fuera de la GIL. Por lo tanto, es solo en programas de multiproceso que pasan mucho tiempo dentro de la GIL, interpretando el código de byte de CPython, que la GIL se convierte en un cuello de botella.

Python tiene un GIL en lugar de un bloqueo de grano fino por varias razones:

  • Es más rápido en el caso de un solo hilo.

  • Es más rápido en el caso de subprocesos múltiples para programas enlazados de E / S.

  • Es más rápido en el caso de subprocesos múltiples para programas ligados a cpu que hacen su trabajo intensivo de cómputo en bibliotecas de C

  • Hace que las extensiones C sean más fáciles de escribir: no habrá ningún cambio de subprocesos de Python, excepto cuando permitas que esto suceda (es decir, entre las macros Py_BEGIN_ALLOW_THREADS y Py_END_ALLOW_THREADS).

  • Hace que el ajuste de las bibliotecas de C sea más fácil. No tienes que preocuparte por la seguridad de los hilos. Si la biblioteca no es segura para subprocesos, simplemente mantendrá la GIL bloqueada mientras la llama.

El GIL puede ser liberado por las extensiones C. La biblioteca estándar de Python libera el GIL alrededor de cada llamada de I / O de bloqueo. Por lo tanto, GIL no tiene ninguna consecuencia para el rendimiento de los servidores enlazados de E / S. Por lo tanto, puede crear servidores de red en Python usando procesos (bifurcación), subprocesos o i / o asíncrono, y GIL no se interpondrá en su camino.

Las bibliotecas numéricas en C o Fortran se pueden llamar de manera similar con el GIL liberado. Mientras su extensión C está esperando que se complete una FFT, el intérprete ejecutará otros subprocesos de Python. Por lo tanto, un GIL es más fácil y más rápido que el bloqueo de grano fino en este caso. Esto constituye el grueso del trabajo numérico. La extensión NumPy libera la GIL siempre que sea posible.

Los subprocesos suelen ser una mala forma de escribir la mayoría de los programas de servidor. Si la carga es baja, forking es más fácil. Si la carga es alta, la I / O asíncrona y la programación controlada por eventos (por ejemplo, utilizando el marco Twisted de Python) es mejor. La única excusa para usar hilos es la falta de os.fork en Windows.

La GIL es un problema si, y solo si, está haciendo un trabajo intensivo de CPU en Python puro. Aquí puede obtener un diseño más limpio utilizando procesos y paso de mensajes (por ejemplo, mpi4py). También hay un módulo de 'procesamiento' en Python Cheese Shop, que le da a los procesos la misma interfaz que los hilos (es decir, reemplazar los hilos. Hilo con procesamiento.Proceso).

Los subprocesos se pueden usar para mantener la capacidad de respuesta de una GUI independientemente de la GIL. Si la GIL afecta su rendimiento (consulte la discusión anterior), puede dejar que su hilo genere un proceso y esperar a que termine.

    
respondido por el Md Mahbubur Rahman 13.02.2013 - 04:57
fuente
39

Primero que nada: Python no tiene un GIL. Python es un lenguaje de programación. Un lenguaje de programación es un conjunto de reglas y restricciones matemáticas abstractas. No hay nada en la Especificación de lenguaje Python que diga que debe haber una GIL.

Hay muchas implementaciones diferentes de Python. Algunos tienen un GIL, otros no.

Una explicación simple para tener una GIL es que escribir código concurrente es difícil. Al colocar un candado gigante alrededor de su código, lo obliga a ejecutarse siempre en serie. Problema resuelto!

En CPython, en particular, un objetivo importante es facilitar la extensión del intérprete con los complementos escritos en C. Una vez más, escribir el código concurrente es difícil, por lo tanto, al garantizar que no habrá concurrencia, es más fácil Escribir extensiones para el intérprete. Además, muchas de esas extensiones son simplemente envoltorios delgados alrededor de bibliotecas existentes que pueden no haberse escrito teniendo en cuenta la concurrencia.

    
respondido por el Jörg W Mittag 13.02.2013 - 04:22
fuente
15

¿Cuál es el propósito de una GIL?

La documentación de CAPI tiene esto que decir sobre el tema:

  

El intérprete de Python no es completamente seguro para subprocesos. Para admitir los programas de Python de subprocesos múltiples, hay un bloqueo global, denominado bloqueo de intérprete global o GIL, que debe estar sujeto por el hilo actual antes de poder 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 múltiples subprocesos: por ejemplo, cuando dos subprocesos incrementan simultáneamente el recuento de referencias del mismo objeto, el recuento de referencias podría incrementarse solo una vez en lugar de dos veces.

En otras palabras, la GIL previene la corrupción del estado. Los programas de Python nunca deben producir un fallo de segmentación, ya que solo se permiten las operaciones de memoria segura. El GIL extiende esta garantía a los programas de subprocesos múltiples.

¿Cuáles son las alternativas?

Si el propósito de la GIL es proteger el estado de la corrupción, entonces una alternativa obvia es el bloqueo en un grano mucho más fino; tal vez en un nivel por objeto. El problema con esto es que, aunque se ha demostrado que aumenta el rendimiento de los programas de múltiples subprocesos, tiene más sobrecarga y, como resultado, los programas de un solo subproceso sufren.

    
respondido por el dan_waterworth 13.02.2013 - 07:37
fuente

Lea otras preguntas en las etiquetas