GIL em Python

GIL significa Global Interpreter Lock (Trava Global do Interpretador).
É um mutex que protege o acesso a objetos internos do Python.
Primeiramente, o GIL permite que apenas uma thread execute bytecode por vez.
Por exemplo, mesmo com 8 núcleos de CPU, apenas um executa Python puro.
Isso limita severamente o paralelismo para código CPU-intensivo.
Além disso, o GIL simplifica a gerência de memória do Python.
A voz passiva é usada aqui: “objetos são protegidos contra corrupção pelo GIL”.
Quando o GIL é um problema? Em tarefas que consomem muita CPU.
Por exemplo, cálculos matemáticos, loops pesados ou processamento de imagens.
Por outro lado, o GIL não atrapalha operações de I/O.
Leitura de arquivos, requisições de rede ou acesso a banco são liberados.
Portanto, Python com threads é excelente para I/O-bound.
Vamos explorar na prática esses conceitos.
Três subtítulos mostram quando o GIL atrapalha ou ajuda.

Gil prejudicando: cpu-bound com threads

Quando o código é intensivo em CPU, threads não ajudam.
O GIL força as threads a executarem uma por vez.
Portanto, o ganho de performance é zero ou negativo.
Por exemplo, calcular números primos em várias threads não acelera.
A voz passiva é aplicada: “o tempo total é similar ao sequencial”.
Veja um exemplo prático dessa limitação:

O resultado mostra aceleração próxima de 1.0.
Ou seja, threads não trouxeram ganho para CPU.
Para esses casos, use multiprocessing ou bibliotecas em C (NumPy).

Gil ajudando: i/o-bound com threads

Para operações de I/O, o GIL é liberado durante a espera.
Enquanto uma thread aguarda dados da rede, outra executa.
Portanto, threads trazem ganhos enormes para I/O-bound.
Por exemplo, baixar 10 arquivos simultaneamente é muito mais rápido.
A voz passiva é aplicada: “as operações de I/O são realizadas fora do GIL”.
Exemplo demonstrando a eficiência para I/O:

Threads reduzem o tempo total de 5 segundos para cerca de 1 segundo.
Isso é o poder da concorrência para operações de I/O.
A fórmula do ganho teórico é:
\(G = \frac{T_{\text{sequencial}}}{T_{\text{thread}}} \approx N_{\text{tarefas}}\)
Quando as tarefas são independentes e bloqueantes.

Alternativas para contornar o gil

Para CPU-bound, use multiprocessing em vez de threading.
Cada processo tem seu próprio GIL e memória separada.
Assim, você aproveita múltiplos núcleos de verdade.
Outra alternativa é usar bibliotecas em C como NumPy.
Elas liberam o GIL durante operações pesadas.
Para I/O-bound, threading e asyncio são excelentes.
A escolha certa depende do seu problema específico.
A voz passiva é aplicada: “decisões informadas são tomadas após medição”.
Exemplo de multiprocessing para contornar o GIL:

Multiprocessing mostra aceleração real próxima ao número de núcleos.
Threads para CPU-bound mostram aceleração próxima de 1.0.
Entenda o GIL e escolha a ferramenta certa.
Para I/O: threading ou asyncio.
Para CPU: multiprocessing, NumPy ou Cython.
O GIL não é um monstro, apenas uma característica.
Aprenda a conviver com ele e seja feliz em Python.

Deixe um comentário