Por que Python foi escrito com o GIL?

O bloqueio do interpretador global (GIL) parece ser frequentemente citado como a principal razão pela qual threading e similares são um pouco complicados em Python – o que levanta a questão “Por que isso foi feito em primeiro lugar?”

Como não sou um programador, não tenho ideia de por que isso pode ser – qual foi a lógica por trás da inclusão do GIL?

Comentários

  • O artigo da Wikipedia afirma que ” o GIL pode ser uma barreira significativa ao paralelismo – um preço pago por ter o dinamismo da linguagem ” , e continua dizendo que ” As razões para empregar tal bloqueio incluem: maior velocidade de programas de thread único (sem necessidade de adquirir ou liberar bloqueios em todas as estruturas de dados separadamente) e fácil integração de bibliotecas C que geralmente são não seguro para thread. ”
  • @RobertHarvey, Dinamismo não tem nada a ver com isso. O problema é a mutação.
  • stackoverflow.com/questions/265687/…
  • Não pode ‘ ajudar a sentir que, assim como o Java ‘ a falta de números não assinados, pretendia evitar que pessoas que não ‘ Não sei o que eles ‘ estão fazendo atirando no próprio pé. Infelizmente, qualquer pessoa que saiba o que ‘ está fazendo obtém uma linguagem deficiente, o que é uma pena, porque o Python balança de muitas outras maneiras
  • @Basic deve haver alguma maneira padrão de lidar com matrizes de bytes em Java (não ‘ há muito tempo) para fazer criptografia matemática. Python (por exemplo) não ‘ tem números com sinais, mas eu não ‘ nem mesmo tentaria fazer operações bit a bit com ele porque há maneiras melhores.

Resposta

Existem várias implementações de Python, por exemplo, CPython, IronPython, RPython, etc.

Alguns deles têm um GIL, outros não. Por exemplo, CPython tem o GIL:

De http://en.wikipedia.org/wiki/Global_Interpreter_Lock

Os aplicativos escritos em linguagens de programação com um GIL podem ser projetados para usar processos separados para atingir o paralelismo completo, pois cada processo tem seu próprio interpretador e por sua vez, tem seu próprio GIL.

Benefícios do GIL

  • Maior velocidade de programas single-threaded.
  • Fácil integração de bibliotecas C que geralmente não são thread-safe.

Por que Python (CPython e outros) usa o GIL

No CPython, o bloqueio do interpretador global, ou GIL, é um mutex que impede que várias threads nativas executem bytecodes Python de uma vez. Este bloqueio é necessário principalmente porque o gerenciamento de memória do CPython não é seguro para thread.

O GIL é controverso porque impede que programas CPython com multiprocessamento tirem total proveito de sistemas multiprocessadores em certas situações. Observe que potencialmente bloqueando ou operações de longa duração, como E / S, processamento de imagem e processamento de números NumPy, acontecem fora do GIL. Portanto, é apenas em programas multithread que passam muito tempo dentro do GIL, interpretando bytecode CPython, que o GIL se torna um gargalo.

Python tem um GIL em oposição ao bloqueio refinado por vários motivos:

  • É mais rápido no caso de thread único.

  • É mais rápido no caso de multithread para programas vinculados a i / o.

  • É mais rápido no caso de multithread para programas vinculados à CPU que fazem seu trabalho de computação intensiva em bibliotecas C.

  • Isso torna Extensões C mais fáceis de escrever: não haverá troca de threads Python, exceto onde você permite que isso aconteça (ou seja, entre as macros Py_BEGIN_ALLOW_THREADS e Py_END_ALLOW_THREADS).

  • Facilita o empacotamento de bibliotecas C. Você não precisa se preocupar com thread-safety. Se a biblioteca não for thread-safe, você simplesmente mantém a GIL bloqueada enquanto a chama.

A GIL pode ser lançado por extensões C. A biblioteca padrão do Python libera o GIL em torno de cada chamada de i / o de bloqueio. Assim, o GIL não tem consequências para o desempenho dos servidores vinculados a E / S. Assim, você pode criar servidores de rede em Python usando processos (fork), threads ou E / S assíncrona, e o GIL não vai atrapalhar.

Bibliotecas numéricas em C ou Fortran podem ser chamadas de forma semelhante com o GIL lançado. Enquanto sua extensão C está esperando a conclusão de um FFT, o interpretador estará executando outros threads Python.Um GIL é, portanto, mais fácil e mais rápido do que um bloqueio de baixa granularidade neste caso também. Isso constitui a maior parte do trabalho numérico. A extensão NumPy libera o GIL sempre que possível.

Threads geralmente são uma maneira ruim de escrever a maioria dos programas de servidor. Se a carga for baixa, o garfo é mais fácil. Se a carga for alta, o i / o assíncrono e a programação orientada a eventos (por exemplo, usando a estrutura Twisted do Python) são melhores. A única desculpa para usar threads é a falta de os.fork no Windows.

O GIL é um problema se, e somente se, você estiver fazendo um trabalho intensivo de CPU em Python puro. Aqui você pode obter um design mais limpo usando processos e transmissão de mensagens (por exemplo, mpi4py). Há também um módulo de “processamento” no queijo Python shop, que fornece aos processos a mesma interface que os threads (ou seja, substitua o threading.Thread por processing.Process).

Os threads podem ser usados para manter a capacidade de resposta de uma GUI independentemente do GIL. Se o GIL prejudicar seu desempenho (cf. a discussão acima), você pode deixar seu thread gerar um processo e esperar que ele termine.

Comentários

  • Soa como uvas verdes para mim. Python pode ‘ t fazer threads corretamente, então você inventa razões pelas quais as threads são desnecessárias ou mesmo ruins. ” Se o carregamento é baixo, fo rking é mais fácil “, sério? E o GIL é ” mais rápido ” para todos esses casos apenas se você insistir em usar GC de contagem de referência.
  • s/RPython/PyPy/g. @MichaelBorgwardt Dar razões pro GIL é o ponto principal da pergunta, não ‘ é isso? Embora eu concorde que parte do conteúdo desta resposta (a saber, a discussão de alternativas) está fora de questão. E para o melhor ou para o pior, agora é quase impossível se livrar da recontagem – ela está profundamente arraigada em toda a API e base de código; é ‘ quase impossível se livrar dele sem reescrever metade do código e quebrar todo o código externo.
  • Don ‘ Não esqueça a biblioteca multiprocessing – padrão desde 2.6. É ‘ s pools de trabalho são uma abstração super-inteligente para alguns tipos simples de paralelismo.
  • @alcalde Somente se você não ‘ não sei o que você ‘ está fazendo e / ou não ‘ não deseja que seus tópicos funcionem cooperativamente / comunicar. Caso contrário, é ‘ uma dor real nas costas, especialmente considerando a sobrecarga de lançar um novo processo em alguns sistemas operacionais. Temos servidores com 32 núcleos, portanto, para utilizá-los totalmente em CPython, ‘ d preciso de 32 processos. Essa ‘ não é uma ” boa solução ” é ‘ um hack para contornar as ‘ s inadequações do CPython.
  • O fato de que os threads existem em plataformas diferentes do Windows deve ser prova suficiente de que a bifurcação não é ‘ t adequado em todas as situações.

Resposta

Primeiro desativado: Python não tem GIL. Python é uma linguagem de programação. Uma linguagem de programação é um conjunto de regras e restrições matemáticas abstratas. Não há nada na Especificação da linguagem Python que diga que deve haver um GIL.

Existem muitas implementações diferentes de Python. Algumas têm GIL, outras não.

Uma explicação simples para ter uma GIL é que escrever código concorrente é difícil. Ao colocar um cadeado gigante em torno de seu código, você o força a sempre ser executado em série. Problema resolvido!

No CPython, em particular, um objetivo importante é tornar mais fácil estender o interpretador com plug-ins escritos em C. Novamente, escrever código simultâneo é difícil, garantindo que não haverá simultaneidade, torna mais fácil escrever extensões para o interpretador. Além disso, muitas dessas extensões são apenas thin wrappers em torno de bibliotecas existentes que podem não ter sido escritas com a simultaneidade em mente.

Comentários

  • Isso ‘ s o mesmo argumento que Java ‘ s falta de tipos numéricos sem sinal – os desenvolvedores pensam que todo mundo é mais burro do que eles …
  • @Basic – acredite ou não, mesmo quando você ‘ não é muito, realmente burro, acontece que ter uma linguagem que simplifica suposições que significam que você não ‘ Pensar em certas coisas para fazê-las funcionar ainda é uma coisa útil.CPython é ótimo para certas coisas, incluindo aplicativos multithread simples (onde o programa é limitado por IO, o que muitos são, e portanto o GIL não ‘ importa), porque as decisões de design que tomaram o GIL, a melhor solução, também torna a programação desses aplicativos mais fácil, principalmente o fato de que ele suporta operações atômicas em coleções .
  • @Jules Sim, ‘ é muito útil até você precisar desses recursos. cpython ‘ s ” solução preferida ” de ” basta escrevê-lo em outra linguagem como c ++ ” então significa que você perderá todos os benefícios do Python individualmente. Se você ‘ está escrevendo metade do seu código em c ++, então por que começar com Python? Claro, para pequenos projetos de API / cola é ‘ rápido e fácil, e para ETL é ‘ incomparável, mas ‘ s não é adequado para nada que requeira levantamento de peso. O mesmo que usar Java para falar com o hardware … É ‘ quase cômico os obstáculos que você tem que pular.
  • @Basic One of Python ‘ se, portanto, estender as filosofias centrais ‘ do CPython é tornar a tecnologia ” amigável e fácil de usar “. A programação paralela sem bloqueio global não é isso. Considerando que há muitas implementações sem GIL, faz sentido fornecer pelo menos uma implementação que o tenha.
  • Você diz ” faz sentido pelo menos fornecer uma implementação que o tenha. ” goste ‘ é a conclusão óbvia, mas nenhuma outra linguagem eu ‘ estou ciente dos obstáculos aos seus desenvolvedores dessa maneira, então não pode ‘ ser tão óbvio.

Resposta

Qual é a finalidade de um GIL?

A documentação CAPI tem a dizer sobre o assunto:

O interpretador Python não é totalmente thread-safe . Para oferecer suporte a programas Python multithread, há um bloqueio global, chamado bloqueio de interpretador global ou GIL, que deve ser mantido pelo thread atual antes que ele possa acessar objetos Python com segurança. Sem o bloqueio, mesmo as operações mais simples podem causar problemas em um programa multi-threaded: por exemplo, quando dois threads incrementam simultaneamente a contagem de referência do mesmo objeto, a contagem de referência pode acabar sendo incrementada apenas uma vez em vez de duas vezes.

Em outras palavras, o GIL evita a corrupção de estado. Os programas Python nunca devem produzir uma falha de segmentação, porque somente operações de memória segura são permitidas. O GIL estende essa garantia para programas multithread.

Quais são as alternativas?

Se o propósito do GIL é proteger o estado da corrupção, então uma alternativa óbvia é se limitar a um grão muito mais refinado; talvez em um nível por objeto. O problema com isso é que, embora tenha sido demonstrado que aumenta o desempenho de programas multi-threaded, há mais sobrecarga e os programas single-threaded sofrem como resultado.

Comentários

  • Seria ótimo deixar um usuário executar um programa com uma opção de intérprete substituindo o gil por um bloqueio refinado e de alguma forma saber -de uma forma somente leitura- se o processo atual foi gerado com ou sem gil.
  • Apesar do GIL, consegui produzir uma falha de segmentação em um programa multithread devido ao uso descuidado do módulo pyodbc. Assim, ” nunca deve produzir uma falha de segmentação ” é uma falácia.

Deixe uma resposta

O seu endereço de email não será publicado. Campos obrigatórios marcados com *