11.1 Global Interpreter Lock
Global Interpreter Lock (GIL)
— es un mecanismo en
el intérprete de CPython, que asegura que solo un hilo
ejecuta bytecode de Python en cualquier momento. GIL
previene
la ejecución paralela de hilos, lo que puede impactar negativamente
en el rendimiento de programas multihilo, especialmente en procesadores
multinúcleo.
Razones para la existencia del GIL
Simplificación de la gestión de memoria: GIL
simplifica
la gestión de memoria y la recolección de basura, haciendo Python más fácil
de implementar.
Seguridad en hilos: GIL
previene estados
de carrera, haciendo la ejecución del código segura en hilos sin necesidad
de usar bloqueos.
Problemas causados por el GIL
Rendimiento limitado: Programas multihilo
que ejecutan tareas computacionalmente intensivas no pueden
aprovechar completamente las ventajas de procesadores multinúcleo debido a
las limitaciones del GIL
.
Distorsión de rendimiento: Programas que usan intensivamente hilos para ejecutar tareas pueden enfrentarse con degradación del rendimiento debido al cambio de contexto entre hilos.
Actualmente existen 4 formas principales de "eludir el GIL":
11.2 Uso de multiprocesamiento (multiprocessing)
El módulo multiprocessing
permite crear
procesos que se ejecutan en paralelo y no están limitados por el GIL
, ya que
cada proceso tiene su propio intérprete de Python y
memoria.
Ejemplo:
import multiprocessing
def worker(num):
print(f'Worker: {num}')
def main():
processes = []
for i in range(5):
p = multiprocessing.Process(target=worker, args=(i,))
processes.append(p)
p.start()
for p in processes:
p.join()
main()
11.3 Programación asíncrona
La programación asíncrona utilizando
asyncio
permite ejecutar tareas en paralelo, sin
bloquear el hilo principal. Aunque esto no elude el GIL
en el sentido estricto,
permite utilizar el tiempo de espera de forma eficiente para ejecutar
otras tareas.
Ejemplo:
import asyncio
async def main():
await asyncio.sleep(1)
print('Hello')
asyncio.run(main())
11.4 Uso de bibliotecas con gestión propia de hilos
Algunas bibliotecas, como NumPy y SciPy, están escritas en C y utilizan sus propios mecanismos de gestión de hilos, lo que les permite eludir el GIL y utilizar de manera eficiente los procesadores multinúcleo para cálculos.
De hecho, esta es una de las principales razones del éxito de Python, por más lento que sea. Todos los cálculos complejos están reescritos en lenguajes C/C++ y se ejecutan en todos los núcleos de la CPU o incluso directamente en los núcleos de la GPU. Y en las GPU modernas hay miles de núcleos.
Resulta que ya no importa cuán rápido o lento sea el lenguaje, si todos los cálculos que consumen recursos se ejecutan por bibliotecas externas o incluso se llevan a centros de datos remotos.
11.5 Ejecución de cálculos fuera del intérprete de Python
El uso de extensiones en C/C++ u otros lenguajes que pueden ejecutar cálculos fuera del intérprete de Python y luego devolver el resultado. Esto permite evitar el bloqueo del GIL durante la ejecución de cálculos intensivos.
Ejemplo de uso de Cython:
# example.pyx
def compute(int n):
cdef int i, result = 0
for i in range(n):
result += i
return result
Compilación y uso:
cythonize -i example.pyx
import example
print(example.compute(1000000))
GO TO FULL VERSION