- O Python steering council declarou a intenção de aprovar a PEP 703, que torna o GIL opcional ao longo de várias releases, e as condições finais ainda estão sendo ajustadas
- O build
--disable-gildo CPython 3.13 será preparado como experimental, e a stable ABI e a compatibilidade de wheels de módulos de extensão surgem como as maiores questões técnicas - Wheels
abi3existentes podem não ser diretamente compatíveis com o CPython 3.13 sem GIL, então estão sendo discutidas a introdução deabi4, mudanças na C API limitada e a conversão de macros de contagem de referências em chamadas de função - Há preocupação de que o
pippossa escolher o wheel errado para builds com GIL e sem GIL; uma instalação silenciosamente incorreta é mais perigosa do que uma simples falha de instalação - Para o nome do build sem GIL, foi sugerido free-threading em vez de
nogil, mas também é preciso resolver nomes de executáveis, shebangs, instalações paralelas e empacotamento por distribuições
Posição atual da PEP 703 e do CPython sem GIL
- No fim de julho, o Python steering council declarou a intenção de aprovar a PEP 703, que propõe tornar o global interpreter lock (GIL) opcional no CPython
- As condições detalhadas da aprovação ainda não foram finalizadas, mas as discussões de implementação relacionadas e a preparação do ecossistema já estão em andamento
- No longo prazo, presume-se um caminho rumo a uma única versão do CPython sem GIL, mas, por enquanto, a fase é testar o comportamento sem GIL em um interpretador compilado com a opção
--disable-gil - O CPython 3.13 está previsto para outubro de 2024, e o build sem GIL dessa versão terá caráter experimental
Stable ABI e compatibilidade de módulos de extensão
- Sam Gross abordou no fórum de discussão do Python como a PEP 703 se relaciona com a stable ABI do CPython
- A stable ABI serve para permitir que módulos de extensão funcionem em várias versões do CPython com o mesmo wheel binário, sem precisar recompilar a cada nova release do CPython
- Extensões compiladas para a stable ABI podem não funcionar diretamente no build sem GIL do CPython 3.13
- Para resolver isso, foram propostas algumas adições e alterações à C API limitada
- Extensões que usam apenas a C API limitada podem produzir binários que usam a stable ABI
- As mudanças incluem o plano já existente de converter algumas macros que incrementam e decrementam a contagem de referências de objetos em chamadas de função
- O objetivo é possibilitar binários de extensão que funcionem tanto no build com GIL quanto no build sem GIL
abi3, abi4 e o problema de seleção de wheels
- Victor Stinner considera que, para o experimento sem GIL ser bem-sucedido, é necessária uma solução simples para extensões que funcionem nos dois tipos de interpretador
- Como extensões compiladas para a stable ABI no CPython 3.12 ou anterior não são compatíveis com builds sem GIL a partir da 3.13, surgiu a ideia de criar uma nova versão de ABI, a abi4
- A stable ABI atual é
abi3 - O número da ABI não precisa estar necessariamente ligado ao número da versão principal do CPython
- A stable ABI atual é
- Gross considera aceitável, em certa medida, que extensões que queiram dar suporte ao modo sem GIL tenham o ônus de criar dois wheels binários
- Ele se preocupa mais com a possibilidade de o projeto sem GIL ficar excessivamente preso a melhorias na C API e na stable ABI
- Alex Gaynor também mantém vários pacotes de wheels
abi3, mas considera que criar dois wheels uma vez não é um fardo excessivamente grande- Porém, é importante que o
pip, tanto o atual quanto versões futuras, escolha o wheel correto entre os dois
- Porém, é importante que o
- Brett Cannon avalia que a lógica atual do
pipnão distingue as duas versões; portanto, sem uma mudança comoabi4, opipatual e versões antigas não funcionarão corretamente
Preocupação com mau funcionamento silencioso do pip
- Gross considera que não é preciso se preocupar muito com o suporte a versões antigas do
pipno build experimental--disable-gildo CPython 3.13- O motivo é que é comum versões antigas do
pipquebrarem em novas versões do Python - Como exemplo, ele citou o caso em que
pip==23.1.1ou anterior quebra no CPython 3.13 devido à ausência depkgutil.ImpImporter
- O motivo é que é comum versões antigas do
- O mantenedor do
pip, Paul Moore, vê quebrar de forma explícita e instalar silenciosamente o pacote errado como problemas diferentes- Há usuários que usam versões antigas do
pip - Uma falha explícita e um erro silencioso têm impactos diferentes sobre o usuário
- Há usuários que usam versões antigas do
- Moore teme que usuários interessados em testar builds sem GIL ou free-threaded fiquem desmotivados se precisarem depurar problemas de compatibilidade de ABI
- Gaynor também considera que, se o
pipse comportar silenciosamente de forma incorreta em pacotes afetados, pode haver uma enxurrada de issues
Instalação paralela e nomes de executáveis
- Barry Warsaw perguntou se há planos para instalar, no mesmo sistema, builds com GIL e sem GIL lado a lado
- Gross respondeu que essa situação é semelhante a instalar versões diferentes do Python
- Cannon considera possível colocar os dois binários em um único wheel “fat”
- Porém, os nomes dos binários dentro do wheel precisam ser diferentes
- A discussão sobre nomes de executáveis continuou em uma thread separada
- Paul Moore considera que os usuários devem conseguir testar facilmente o modo sem GIL e escolher com facilidade entre GIL e sem GIL
- Se esse processo for difícil em Windows, macOS, Linux etc., isso pode afetar negativamente o projeto sem GIL
- Os usuários precisam conseguir experimentar com facilidade para que surja demanda por builds sem GIL e, com isso, pressão sobre mantenedores de pacotes para oferecer wheels compatíveis com no-GIL
Debate sobre os nomes nogil e free-threading
- Barry Scott considera que o nome do executável é importante, pois a linha shebang precisa indicar qual interpretador chamar
- Como exemplos, sugeriu nomes como
python-nogil3epython-nogil3.13
- Como exemplos, sugeriu nomes como
- Gregory P. Smith expressou a opinião pessoal de que, como o build sem GIL do CPython 3.13 é um recurso experimental, distribuições não deveriam colocá-lo no
$PATHpadrão- Ele também considera indesejável que nomes longos de executáveis permaneçam por muito tempo em shebangs
- Sugeriu adiar a decisão sobre o nome de instalação para depois da 3.14
- Petr Viktorin, desenvolvedor do Fedora, observou que é provável que distribuições queiram empacotar o interpretador sem GIL para permitir experimentos dos usuários
- Moore considera desejável especificar um build free-threaded em uma forma como
#!/usr/bin/env python3.13-nogil- A exigência vem da intenção de não codificar diretamente um caminho longo e pouco intuitivo
- Em uma thread iniciada por Steve Dower sobre o instalador do Windows, Smith afirmou que o steering council quer evitar o nome
nogil- Os motivos são que ele não comunica bem para a maioria dos desenvolvedores que não são do core, que não é necessário saber o que é o GIL e que o termo inclui uma forma negativa
- Como alternativa, foi proposto o termo free-threading
- Gross considera que
free-threadingtambém não é fácil para pessoas de fora entenderem e não é um termo amplamente usado - Na discussão real, havia forte preferência por nomes curtos, e
nogilera o candidato mais forte nesse aspecto - A mudança concreta refletida foi trocar a tag de ABI do build sem GIL de
nparattsignifica threading
Proposta de abi4 e trabalhos restantes
- Gross e Viktorin discutiram os pontos problemáticos da proposta de mudanças na API, e esse feedback levou à proposta de uma nova ABI, a abi4
- Gross criou um protótipo da nova ABI
- Viktorin concorda em linhas gerais com a abordagem, mas considera que os detalhes ainda precisam ser mais refinados
- Stinner considera que uma PEP para a abi4 é necessária, e Viktorin entende isso como uma discussão pré-PEP
- Há confusão sobre as garantias de compatibilidade oferecidas pela combinação da versão da C API limitada com
abi3, e isso também afeta a direção daabi4 - A investigação relacionada continua em andamento, e é possível que haja discussões presenciais no core developer sprint em meados de outubro
Texto final de aprovação e impacto de longo prazo
- O trabalho no CPython sem GIL ou free-threaded continua, mas a aprovação final da PEP 703 ainda está pendente
- Embora o atraso tenha se alongado um pouco, a PEP 703 e seus efeitos colaterais provavelmente terão grande impacto sobre o desenvolvimento do CPython e seu ecossistema pelos próximos cinco anos ou mais
- O steering council tenta tornar claros os critérios de aprovação
- Thomas Wouters afirmou que está refinando o texto exato da aprovação e tentando esclarecer várias decisões
- Parte do trabalho também poderá ser realizada no core developer sprint
1 comentários
Opiniões no Hacker News
Olhando para os computadores modernos, dá para pensar que paralelismo explícito talvez seja um elemento mais fundamental da ciência da computação do que está na moda nos livros-texto
Talvez agora estejamos no ponto em que sempre precisamos escrever código paralelo explicitamente
Por exemplo, loops
forestão sendo substituídos por operações comoforeach,mapefilter. Essas expressões informam ao compilador/interpretador a intenção de aplicar alguma operação a todos os itens de uma estrutura de dados, e deixam para o compilador/runtime decidir se e como paralelizarNa execução de serviços web, cada requisição é rápida o suficiente, e o verdadeiro ganho do paralelismo está em processar muitas requisições em paralelo. É aí que No-GIL se encaixa
Quando há muitas sub-requisições dentro de uma única requisição, normalmente isso é tratado com código assíncrono, mas muitas vezes é mais porque criar threads é caro ou porque pools de threads são trabalhosos do que por ganhos de desempenho da assincronicidade. Assíncrono é bom para throughput, mas ruim para latência, e, ao paralelizar requisições de serviço, geralmente a latência preocupa mais. O assíncrono venceu principalmente por usabilidade
Outro tipo de paralelismo aparece em grandes trabalhos offline. São coisas como MapReduce ou Presto, que em geral se parecem com problemas de dividir para conquistar. O treinamento de modelos em GPU também é parecido com isso
O que não aconteceu foram algoritmos locais altamente paralelos. Em serviços web, o tamanho dos dados é pequeno, então o ganho de latência é pequeno, a implementação é complexa e o custo de coordenação entre threads aumenta. Uma pequena exceção são os algoritmos vetorizados, mas eles rodam em um único núcleo e não têm overhead de coordenação, e a inferência online também é, de novo, muito fortemente vetorizada
Com o tempo, ambos vêm melhorando. Assim como mais linguagens e bibliotecas ficam seguras por padrão, agora mais coisas são paralelas por padrão. Ainda há um caminho a percorrer, mas acho bom que isso não tenha sido feito cedo demais, porque a tecnologia melhorou muito nos últimos 10 anos
Por exemplo, dá para comparar o que se pode fazer com segurança com Rayon em Rust e o que se fazia sem segurança com OpenMP em C++
Mais externamente, também há estas coisas em que trabalho: https://legion.stanford.edu/, https://regent-lang.org/, https://github.com/nv-legate/cunumeric
Como são detalhes de implementação, se pudermos abstraí-los para usá-los com mais facilidade, devemos fazer isso
Para comparação, um mutex leva cerca de 25 nanossegundos, e aumenta mais quando há contenção, mas mutex é sincronização ponto a ponto
O bom do Disruptor é que várias threads podem receber a mesma mensagem sem muito esforço adicional
https://github.com/LMAX-Exchange/disruptor/wiki/Performance-...
https://gist.github.com/rmacy/2879257
Sonho com uma linguagem parecida com Smalltalk, mas que permaneça single-thread até que paralelizar faça sentido
Estou procurando problemas de paralelismo que não sejam big data. Paralelismo é mais parecido com colocar mais carros na estrada do que aumentar a velocidade do carro. Mas ainda estou tentando descobrir o que usuários de desktop ou mobile precisam fazer localmente usando a força matemática do computador
Também estou pensando em Itanium e na arquitetura VLIW como ideias de paralelismo
Basta usar
-ng. No sentido de no-gil ou next-generationHavia novos flags de compilador, novos flags de linker, linkagem com bibliotecas diferentes e até o uso de comandos de compilador completamente diferentes. AIX era especialmente assim
Para o problema do shebang, talvez seja melhor se apoiar na convenção existente do Python:
from __future__ import nogilNesse ponto, basta fazer hot-swap do interpretador
from __future__ importnão é uma instrução de runtime, mas uma instrução especial que indica flagshttps://docs.python.org/3/reference/simple_stmts.html#future...
Instruções future são por módulo, e GIL/no-GIL não se encaixa facilmente nesse modelo
Sempre que vejo esta proposta, fico me perguntando como garantir que os programas continuem funcionando corretamente. Uma boa parte do código Python multithread existente foi escrita de forma insegura
Em especial, o problema são as corridas de dados, que vi repetidamente em bases de código de várias empresas e em projetos open source. Esses programas só não quebram porque dependem implicitamente do fato de que o GIL permite que apenas uma thread execute por vez
Se o GIL desaparecer, esses programas vão quebrar. Como Python é uma linguagem de tipagem dinâmica, tenho muita dúvida de que exista algum analisador estático capaz de encontrar esses problemas em programas Python existentes
O mais provável são bugs sutis que aparecem de forma não determinística em runtime. Até seria melhor se dessem crash, mas essa classe de bug tem grande chance de levar a comportamentos incorretos
Talvez essa proposta sem GIL nem seja para ser usada na maioria dos programas. Pode ser uma ferramenta superespecializada para um conjunto muito pequeno de situações em que o programador sabe que não há GIL e pode escrever o código de acordo
O GIL só significa que apenas uma thread por vez pode executar bytecode Python. Mesmo um interpretador com GIL pode alternar threads entre bytecodes, e muitas operações em Python exigem vários bytecodes. Isso inclui métodos embutidos de tipos embutidos que muita gente considera “atômicos”
Por isso o Python, mesmo hoje tendo GIL, oferece coisas como locks, mutexes e semáforos
Threads competindo pelo GIL já podem tomá-lo umas das outras em momentos ruins e causar confusão
Se um programa só for executado sem GIL quando todas as dependências permitirem, haverá tempo suficiente para corrigir esses bugs
Então provavelmente só vamos lidar com esse problema em larga escala perto de 2030. Também não se vê muita produção atualizando o runtime em uso diretamente para a release mais recente
Não quero soar duro, mas o Steering Council disse que não quer outra migração de 2 para 3, então as pessoas não vão atualizar de forma leviana. Muito do que está online hoje pode ser perigoso para copiar e colar
Há muitos bugs de threading em código Python real
OCaml não passou por uma evolução parecida? Fico curioso se há pontos comparáveis entre os dois projetos
Assim, a API de threads existente cria threads dentro do domain atual e consegue isolar código que espera adquirir um lock. Código novo pode, em vez disso, criar um novo domain que começa com uma thread. Também é possível usar os dois intencionalmente juntos como uma forma de escalonamento
O Python está tentando tornar o lock completamente opcional de forma global, fora do controle dos autores de bibliotecas. Dito isso, o lock do Python parece ser garantido apenas para proteger o próprio runtime, então a maior parte do código que depende desse lock provavelmente já tem bugs de qualquer forma, e por isso o plano do Python também parece viável
Se houver algo em comum, talvez seja apenas a necessidade de encontrar e corrigir estados compartilhados inesperados em toda a base de código do runtime e revisar a C ABI
Agora o Python também tem a chance de alcançar o Tcl em desempenho multithread: https://www.hammerdb.com/blog/uncategorized/why-tcl-is-700-f...
Eu preferiria portar o código Python para Mojo e obter multithreading, SIMD e outros ganhos de velocidade
Não quer migrar código Python para nogil Python? Então toma downvote, é mais ou menos isso
Se for para dar um nome, poderia ser algo como
python4,python3-gilfoil,python3-gilfreeO foco atual em um Python sem GIL me parece bem estranho. A equipe Faster CPython estabeleceu uma meta ambiciosa de aumentar o desempenho do CPython em 50% a cada release
O 3.11 teve melhorias reais, mas ficou muito aquém dos 50%, e em muitos dos nossos testes o 3.12 foi parecido ou mais lento. Multithreading de verdade seria ótimo, mas eu preferiria muito mais ver primeiro uma melhora no desempenho single-thread
Claro, reconheço que nossas necessidades podem não representar as de todo mundo, e sou grato por todo o trabalho feito para tornar Python uma ótima linguagem. Ainda assim, fico curioso sobre o que estou deixando passar
Hoje o uso de múltiplos cores acontece via multiprocessing, que tem muitas limitações. Entendo que múltiplos interpretadores possam vir junto com coisas como corrotinas, mas ainda assim prefiro uma opção real de multithreading
No Python nogil, por exemplo, vários threads podem chamar código C com estado compartilhado acessível como objetos Python. Isso é bem central para machine learning e, na verdade, a forma atual desta PEP veio da equipe do PyTorch
Desempenho single-thread também é importante, mas para trechos críticos já existiam desvios bem bons, como numba, Cython e Mojo
A ordem também importa. Se nogil for introduzido, boa parte do trabalho do faster CPython pode acabar sendo totalmente descartada, então as equipes precisaram se coordenar
Num mundo ideal, teríamos tanto o modo nogil quanto melhorias de desempenho single-thread. Guido também deu a entender que está considerando um JIT sofisticado
Python torna muito conveniente lidar com abstrações de baixo nível em uma linguagem de nível mais alto. Por isso, como desenvolvedor Python de longa data, nunca fiquei muito estressado com o GIL
Se fosse preciso escolher apenas um dos dois, concordo que, para a maioria dos casos de uso, código single-thread simplesmente mais rápido se encaixa melhor. Mas também não há motivo para não ter os dois
Em hindsight, é claro, mas acho que, se o lado do Python soubesse o quão longa e dolorosa seria a transição do 2 para o 3, teria feito mudanças muito maiores também no interior do interpretador
Mesmo depois de uma transição de 12 anos, o desempenho single-thread ainda é péssimo, e ainda restam algumas transições dolorosas até chegar a multithreading de verdade
Sei que devemos ser gentis com o desenvolvimento open source, mas me pergunto a partir de que ponto podemos chamar uma linguagem de muito mal administrada
As piores partes do Python são aquelas difíceis de mudar porque Python é popular demais e seu ecossistema é grande demais. Por isso, todo tipo de mudança fica mais difícil por causa da retrocompatibilidade
Há uma tendência a defender Python rápido demais. É importante olhar objetivamente, sem viés
Projetos que queiram desempenho e sintaxe Python poderiam ir para ela. O Python atual parece estar se debatendo entre vários objetivos e não alcançando nenhum deles direito
Perl 5/6 era citado como exemplo. Mesmo quando ficou claro que ninguém estava migrando, ainda levou cerca de mais 5 anos até tentarem tornar a transição mais fácil