11.1 Global Interpreter Lock
Global Interpreter Lock (GIL) — é um mecanismo no interpretador CPython que garante que apenas um thread está executando bytecode Python a qualquer momento. GIL impede a execução paralela de threads, o que pode impactar negativamente o desempenho de programas multithread, especialmente em processadores multicore.
Razões para a existência do GIL
Simplificação do gerenciamento de memória: GIL simplifica o gerenciamento de memória e a coleta de lixo, tornando o Python mais fácil de implementar.
Segurança de threads: GIL previne condições de corrida, tornando a execução do código segura para threads sem a necessidade de usar locks.
Problemas causados pelo GIL
Desempenho limitado: Programas multithread que executam tarefas intensivas em computação não conseguem aproveitar completamente as vantagens dos processadores multicore devido às limitações do GIL.
Distorsão de desempenho: Programas que usam threads intensivamente para executar tarefas podem enfrentar degradação de desempenho devido à troca de contexto entre threads.
Atualmente, existem 4 principais maneiras de "contornar o GIL":
11.2 Uso de Multiprocessamento (multiprocessing)
Módulo multiprocessing permite criar processos que são executados em paralelo e não estão limitados pelo GIL, já que cada processo tem seu próprio interpretador Python e memória.
Exemplo:
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 Programação Assíncrona
Programação assíncrona usando asyncio permite executar tarefas em paralelo sem bloquear o thread principal. Embora isso não contorne o GIL diretamente, permite usar eficientemente o tempo de espera para executar outras tarefas.
Exemplo:
import asyncio
async def main():
await asyncio.sleep(1)
print('Hello')
asyncio.run(main())
11.4 Uso de Bibliotecas com Gestão Própria de Threads
Algumas bibliotecas, como NumPy e SciPy, são escritas em C e usam seus próprios mecanismos de gestão de threads, o que permite contornar o GIL e usar eficientemente processadores multicore para cálculos.
Na verdade, essa é uma das principais razões para o sucesso do Python, por mais lento que ele possa ser. Todos os cálculos complexos são reescritos em linguagens C/C++ e executados em todos os núcleos do processador ou até mesmo diretamente nos núcleos da placa de vídeo. E nas placas de vídeo modernas, há milhares de núcleos.
Assim, não importa mais quão rápido ou lento é a linguagem, se todos os cálculos que demandam muitos recursos são realizados por bibliotecas externas ou até mesmo transferidos para data centers remotos.
11.5 Execução de Cálculos Fora do Interpretador Python
Uso de extensões em C/C++ ou outras linguagens que podem executar cálculos fora do interpretador Python e depois retornar o resultado. Isso permite evitar o bloqueio do GIL durante a execução de cálculos intensivos.
Exemplo de uso com Cython:
# example.pyx
def compute(int n):
cdef int i, result = 0
for i in range(n):
result += i
return result
Compilação e uso:
cythonize -i example.pyx
import example
print(example.compute(1000000))
GO TO FULL VERSION