Por que o Python foi escrito com o GIL?

104

O bloqueio global de intérpretes (GIL) parece ser frequentemente citado como uma das principais razões pelas quais o threading e outros semelhantes são um pouco complicados em Python - o que levanta a questão "Por que isso foi feito em primeiro lugar?"

Não sendo um programador, eu não tenho idéia do por que isso pode ser - qual era a lógica por trás de colocar o GIL?

    
por Fomite 13.02.2013 / 02:14
fonte

3 respostas

100

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

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

De link

As aplicações escritas em linguagens de programação com um GIL podem ser projetadas para usar processos separados para obter o paralelismo total, já que cada processo possui seu próprio intérprete e, por sua vez, possui seu próprio GIL.

Benefícios do GIL

  • Maior velocidade de programas de thread único.
  • Integração fácil de bibliotecas C que normalmente não são seguras para threads.

Por que o Python (CPython e outros) usa o GIL

No CPython, o bloqueio global de intérprete, ou GIL, é um mutex que impede que vários encadeamentos nativos executem bytecodes de Python de uma só vez. Esse bloqueio é necessário principalmente porque o gerenciamento de memória do CPython não é thread-safe.

O GIL é controverso porque impede que programas CPython multissegmentados aproveitem ao máximo os sistemas multiprocessadores em determinadas situações. Observe que operações potencialmente de bloqueio ou de longa execução, como E / S, processamento de imagens e processamento de números NumPy, acontecem fora do GIL. Portanto, é apenas nos programas multithread que passam muito tempo dentro do GIL, interpretando o bytecode CPython, que o GIL se torna um gargalo.

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

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

  • É mais rápido no caso multi-thread para programas vinculados a E / S.

  • É mais rápido no caso multi-thread para programas ligados a cpu que fazem seu trabalho intensivo de computação em bibliotecas C.

  • Isso torna as extensões C mais fáceis de serem escritas: não haverá alternância de encadeamentos Python, exceto quando você permitir que isso aconteça (por exemplo, entre as macros Py_BEGIN_ALLOW_THREADS e Py_END_ALLOW_THREADS).

  • Ele facilita o acúmulo de bibliotecas C. Você não precisa se preocupar com segurança de thread. Se a biblioteca não for thread-safe, você simplesmente mantém a GIL bloqueada enquanto a chama.

O GIL pode ser liberado por extensões C. A biblioteca padrão do Python libera o GIL em torno de cada chamada de bloqueio de i / o. Assim, o GIL não tem nenhuma consequência para o desempenho de servidores ligados a E / S. Assim, você pode criar servidores de rede no Python usando processos (bifurcação), threads ou i / o assíncrona, e o GIL não atrapalhará.

Bibliotecas numéricas em C ou Fortran também podem ser chamadas com o GIL liberado. Enquanto sua extensão C está esperando por um FFT para completar, o interpretador estará executando outros threads do Python. Uma GIL é, portanto, mais fácil e mais rápida que o bloqueio refinado também neste caso. Isso constitui a maior parte do trabalho numérico. A extensão NumPy libera o GIL sempre que possível.

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

A 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 passagem de mensagens (por exemplo, mpi4py). Há também um módulo de processamento na loja de queijos Python, que fornece aos processos a mesma interface dos encadeamentos (ou seja, substituir encadeamento.Thread com processamento.Processo).

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

    
por 13.02.2013 / 04:57
fonte
39

Primeiro: o Python não tem um 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 do Python. Alguns têm um GIL, outros não.

Uma explicação simples para ter um GIL é que escrever código concorrente é difícil. Colocando uma trava gigante em torno do seu código, você o força a sempre executar serialmente. Problema resolvido!

No CPython, em particular, um objetivo importante é facilitar a extensão do interpretador com plugins escritos em C. Novamente, escrever código concorrente é difícil, então, garantindo que não haverá concorrência, torna-se mais fácil escreva extensões para o intérprete. Além disso, muitas dessas extensões são apenas invólucros finos em torno de bibliotecas existentes que podem não ter sido escritas com a simultaneidade em mente.

    
por 13.02.2013 / 04:22
fonte
15

Qual é o propósito de uma GIL?

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

The Python interpreter is not fully thread-safe. In order to support multi-threaded Python programs, there’s a global lock, called the global interpreter lock or GIL, that must be held by the current thread before it can safely access Python objects. Without the lock, even the simplest operations could cause problems in a multi-threaded program: for example, when two threads simultaneously increment the reference count of the same object, the reference count could end up being incremented only once instead of twice.

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

Quais são as alternativas?

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

    
por 13.02.2013 / 07:37
fonte