Este tutorial será focado em um dos tópicos importantes do Python, GIL. Também abordaremos como o GIL impacta o desempenho dos programas Python com a implementação do código. Antes de mergulhar neste tópico, vamos ter uma ideia básica do GIL.
GIL ou bloqueio global de intérprete
Python Global Interpreter Lock ou GIL é uma parte importante da programação multithreading. É um tipo de bloqueio de processo usado ao trabalhar com vários processos. Dá o controle a apenas um thread. Geralmente, Python usa um único thread para executar um único processo. Obtemos o mesmo resultado de desempenho dos processos single-threaded e multi-threaded usando o GIL. Ele restringe a obtenção de multithreading em Python porque evita os threads e funciona como um único thread.
Nota - Python não suporta multithreading porque os pacotes de threading não nos permitem usar vários núcleos de CPU.
Por que os desenvolvedores Python usam GIL?
Python fornece o recurso exclusivo de contador de referência, que é usado para gerenciamento de memória. O contador de referências conta o número total de referências feitas internamente em Python para atribuir um valor a um objeto de dados. Quando as contagens de referência chegam a zero, a memória atribuída ao objeto é liberada. Vejamos o exemplo abaixo.
Exemplo -
boa sorte
import sys a = [] b = a sys.getrefcount(a)
A principal preocupação com a variável de contagem de referência é que ela pode ser afetada quando dois ou três threads tentam aumentar ou diminuir seu valor simultaneamente. É conhecido como condição de corrida. Se essa condição ocorrer, poderá ocorrer vazamento de memória que nunca será liberada. Pode travar ou apresentar bugs no programa Python.
GIL nos ajuda a remover tal situação usando bloqueios para todas as estruturas de dados compartilhadas entre threads, para que não sejam alteradas de forma inconsistente. Python fornece uma maneira fácil de implementar o GIL, pois lida com gerenciamento de memória thread-safe. GIL exige a oferta de um único bloqueio para um thread para processamento em Python. Ele aumenta o desempenho de um programa de thread único, pois apenas um bloqueio precisa ser manipulado. Também ajuda a criar qualquer programa vinculado à CPU e evita a condição de deadlocks.
int para string java
O impacto em programas Python multithread
Há uma diferença entre os limites da CPU em seu desempenho e os limites de E/S para um programa Python típico ou qualquer programa de computador. Os programas vinculados à CPU geralmente levam a CPU ao seu limite. Esses programas são geralmente usados para cálculos matemáticos, como multiplicações de matrizes, queima, processamento de imagens, etc.
Programas vinculados a E/S são aqueles programas que gastam tempo para obter entradas/saídas que podem ser geradas pelo usuário, arquivo, banco de dados, rede, etc. Esses programas precisam esperar um tempo significativo até que a fonte forneça a entrada. Por outro lado, a fonte também possui seu próprio tempo de processamento. Por exemplo - um usuário está pensando no que inserir como entrada.
Vamos entender o exemplo a seguir.
Exemplo -
import time from threading import Thread COUNT = 100000000 def countdown(num): while num>0: num -= 1 start_time = time.time() countdown(COUNT) end_time = time.time() print('Time taken in seconds -', end_time - start_time)
Saída:
Time taken in seconds - 7.422671556472778
Agora modificamos o código acima executando os dois threads.
Exemplo - 2:
import time from threading import Thread COUNT = 100000000 def countdown(num): while num>0: num -= 1 thread1 = Thread(target=countdown, args=(COUNT//2,)) thread2 = Thread(target=countdown, args=(COUNT//2,)) start_time = time.time() thread1.start() thread2.start() thread1.join() thread2.join() end_time = time.time() print('Time taken in seconds -', end_time - start_time)
Saída:
Time taken in seconds - 6.90830135345459
Como podemos ver que ambos os códigos demoraram o mesmo tempo para serem concluídos. GIL impediu que os threads vinculados à CPU fossem executados em paralelo no segundo código.
inkscape x gimp
Por que o GIL ainda não foi removido?
Muitos programadores reclamam disso, mas o Python não consegue trazer mudanças tão significativas quanto a remoção do GIL. Outra razão é que o GIL não foi melhorado até agora. Se mudar no Python 3, criará alguns problemas sérios. Em vez de remover o GIL, o conceito do GIL pode melhorar. De acordo com Guido van Rossom -
'Eu aceitaria um conjunto de patches no Py3k apenas se o desempenho de um programa de thread único (e de um programa multithread, mas vinculado a E/S) não diminuir'.
Existem também muitos métodos disponíveis que resolvem o mesmo problema resolvido pelo GIL, mas são difíceis de implementar.
Como lidar com o GIL do Python
Usar multiprocessamento é a maneira mais adequada de impedir o programa de GIL. Python oferece vários intérpretes para cada processo a ser executado, portanto, nesse cenário, o thread único é fornecido para cada processo em multiprocessamento. Vamos entender o exemplo a seguir.
Exemplo -
comprimento da string bash
from multiprocessing import Pool import time COUNT = 50000000 def countdown(num): while num>0: num -= 1 if __name__ == '__main__': pool = Pool(processes=2) start_time = time.time() r1 = pool.apply_async(countdown, [COUNT//2]) r2 = pool.apply_async(countdown, [COUNT//2]) pool.close() pool.join() end_time = time.time() print('Time taken in seconds -', end_time - start_time)
Saída:
Time taken in seconds - 3.3707828521728516
Pode parecer que um desempenho decente aumentou, mas o gerenciamento de processos tem suas próprias despesas gerais e vários processos são mais pesados que vários threads.
Conclusão
Neste tutorial, discutimos o GIL e como podemos usá-lo. Ele dá o controle para um único thread ser executado de cada vez. Este tutorial também abordou por que o GIL é importante para programadores Python.