O que é um bloqueio de intérprete global e por que é um problema?
Muito barulho foi feito para remover o GIL do Python, e eu gostaria de entender por que isso é tão importante. Eu nunca escrevi um compilador nem um intérprete, então não seja frugal com detalhes, provavelmente precisarei que eles entendam.
python
python-internals
gil
e-satis
fonte
fonte
Respostas:
O GIL do Python destina-se a serializar o acesso a intérpretes internos de diferentes threads. Em sistemas com vários núcleos, isso significa que vários threads não podem efetivamente usar vários núcleos. (Se o GIL não levou a esse problema, a maioria das pessoas não se importaria com o GIL - ele só está sendo levantado como um problema devido à crescente prevalência de sistemas com vários núcleos.) Se você quiser entendê-lo em detalhes, você pode ver este vídeo ou ver este conjunto de slides . Pode ser muita informação, mas você pediu detalhes :-)
Observe que o GIL do Python é realmente apenas um problema para o CPython, a implementação de referência. Jython e IronPython não têm um GIL. Como desenvolvedor Python, você geralmente não encontra o GIL, a menos que esteja escrevendo uma extensão C. Os gravadores de extensão C precisam liberar o GIL quando suas extensões bloqueiam a E / S, para que outros threads no processo Python tenham a chance de executar.
fonte
regex
,lxml
,numpy
módulos. Cython permite liberar GIL no código personalizado, por exemplo,b2a_bin(data)
Suponha que você tenha vários threads que realmente não tocam nos dados um do outro. Aqueles devem executar o mais independentemente possível. Se você tem um "bloqueio global" que precisa adquirir para (por exemplo) chamar uma função, isso pode acabar como um gargalo. Você pode acabar não obtendo muitos benefícios por ter vários threads em primeiro lugar.
Para colocá-lo em uma analogia do mundo real: imagine 100 desenvolvedores trabalhando em uma empresa com apenas uma caneca de café. A maioria dos desenvolvedores gastava seu tempo esperando café em vez de codificar.
Nada disso é específico do Python - não sei os detalhes sobre o que o Python precisava de um GIL para começar. No entanto, espero que tenha lhe dado uma idéia melhor do conceito geral.
fonte
Vamos primeiro entender o que o python GIL fornece:
Qualquer operação / instrução é executada no intérprete. O GIL garante que o intérprete seja mantido por um único thread em um determinado instante de tempo . E seu programa python com vários threads funciona em um único intérprete. A qualquer momento específico, esse intérprete é mantido por um único thread. Isso significa que apenas o segmento que está segurando o intérprete está sendo executado a qualquer instante .
Agora, por que isso é um problema:
Sua máquina pode ter vários núcleos / processadores. E múltiplos núcleos permitem que vários threads sejam executados simultaneamente, ou seja, vários threads podem ser executados a qualquer instante específico. . Porém, como o intérprete é mantido por um único encadeamento, outros encadeamentos não estão fazendo nada, apesar de terem acesso a um núcleo. Portanto, você não está obtendo nenhuma vantagem fornecida por vários núcleos porque, a qualquer instante, apenas um único núcleo, que é o núcleo que está sendo usado pelo encadeamento que está mantendo o intérprete, está sendo usado. Portanto, seu programa levará tanto tempo para ser executado como se fosse um único programa encadeado.
No entanto, operações potencialmente bloqueadoras ou de longa execução, como E / S, processamento de imagens e processamento de números NumPy, ocorrem fora do GIL. Tomado daqui . Portanto, para essas operações, uma operação multithread ainda será mais rápida que uma única operação encadeada, apesar da presença do GIL. Portanto, o GIL nem sempre é um gargalo.
Edit: GIL é um detalhe de implementação do CPython. IronPython e Jython não têm GIL, portanto, um programa verdadeiramente multithread deve ser possível neles, embora eu nunca tenha usado PyPy e Jython e não tenha certeza disso.
fonte
Python não permite multi-threading no sentido mais verdadeiro da palavra. Ele possui um pacote multiencadeado, mas se você deseja multiencadear para acelerar seu código, geralmente não é uma boa ideia usá-lo. Python tem uma construção chamada Global Interpreter Lock (GIL).
https://www.youtube.com/watch?v=ph374fJqFPE
O GIL garante que apenas um dos seus 'threads' possa ser executado a qualquer momento. Um segmento adquire o GIL, faz um pouco de trabalho e passa o GIL para o próximo segmento. Isso acontece muito rapidamente e, para o olho humano, pode parecer que seus threads estão executando paralelamente, mas na verdade eles estão apenas se revezando usando o mesmo núcleo da CPU. Toda essa passagem do GIL adiciona sobrecarga à execução. Isso significa que, se você deseja que seu código seja executado mais rapidamente, o uso do pacote threading geralmente não é uma boa ideia.
Existem razões para usar o pacote de threading do Python. Se você deseja executar algumas coisas simultaneamente, e a eficiência não é uma preocupação, é totalmente adequado e conveniente. Ou, se você estiver executando um código que precisa esperar por algo (como algumas IO), pode fazer muito sentido. Mas a biblioteca de encadeamentos não permitirá que você use núcleos extras da CPU.
A multithreading pode ser terceirizada para o sistema operacional (executando o multiprocessamento), algum aplicativo externo que chama seu código Python (por exemplo, Spark ou Hadoop) ou algum código que seu código Python chama (por exemplo: você pode ter seu Python código chama uma função C que faz o material multiencadeado caro).
fonte
Sempre que dois threads têm acesso à mesma variável, você tem um problema. No C ++, por exemplo, a maneira de evitar o problema é definir algum bloqueio mutex para impedir que dois threads entrem, digamos, no setter de um objeto ao mesmo tempo.
Multithreading é possível em python, mas dois threads não podem ser executados ao mesmo tempo em uma granularidade mais fina que uma instrução python. O encadeamento em execução está recebendo um bloqueio global chamado GIL.
Isso significa que, se você começar a escrever um código multithread para tirar proveito do seu processador multicore, seu desempenho não melhorará. A solução alternativa usual consiste em executar vários processos.
Observe que é possível liberar o GIL se você estiver dentro de um método que você escreveu em C, por exemplo.
O uso de um GIL não é inerente ao Python, mas a alguns de seus intérpretes, incluindo o CPython mais comum. (#editado, ver comentário)
O problema GIL ainda é válido no Python 3000.
fonte
Documentação do Python 3.7
Gostaria também de destacar a seguinte citação da documentação do Python
threading
:Isso vincula à entrada
global interpreter lock
do Glossário, na qual explica que o GIL implica que o paralelismo encadeado em Python não é adequado para tarefas vinculadas à CPU :Esta citação também implica que os dictos e, portanto, a atribuição de variáveis também são seguros para threads como um detalhe de implementação do CPython:
A seguir, os documentos do
multiprocessing
pacote explicam como ele supera o GIL ao gerar processo, enquanto expõe uma interface semelhante à dethreading
:E os documentos para
concurrent.futures.ProcessPoolExecutor
explicar que ele usamultiprocessing
como back-end:que deve ser contrastado com a outra classe base
ThreadPoolExecutor
que usa threads em vez de processosda qual concluímos que
ThreadPoolExecutor
é adequado apenas para tarefas vinculadas de E / S, enquantoProcessPoolExecutor
também pode lidar com tarefas vinculadas à CPU.A pergunta a seguir pergunta por que o GIL existe em primeiro lugar: Por que o bloqueio global de intérpretes?
Experiências de processo versus encadeamento
No Multiprocessing vs Threading Python , fiz uma análise experimental do processo vs threads no Python.
Visualização rápida dos resultados:
fonte
Por que Python (CPython e outros) usa o GIL
Em http://wiki.python.org/moin/GlobalInterpreterLock
No CPython, o bloqueio global de intérpretes, ou GIL, é um mutex que impede que vários threads nativos executem bytecodes do Python de uma só vez. Esse bloqueio é necessário principalmente porque o gerenciamento de memória do CPython não é seguro para threads.
Como removê-lo de Python?
Como Lua, talvez o Python possa iniciar várias VMs, mas o python não faz isso, acho que deve haver outros motivos.
Em Numpy ou em alguma outra biblioteca estendida python, às vezes, liberar o GIL para outros threads poderia aumentar a eficiência de todo o programa.
fonte
Quero compartilhar um exemplo do livro multithreading for Visual Effects. Então, aqui está uma situação clássica de cadeado
Agora considere os eventos na sequência que resultam em um bloqueio.
fonte