Quais recursos semânticos do Python (e de outras linguagens dinâmicas) contribuem para sua lentidão?


26

Eu não conheço muito bem Python. Estou tentando entender com mais precisão quais recursos exatos das linguagens dinâmicas (à la Python, Lua, Scheme, Perl, Ruby, ....) estão forçando suas implementações a serem lentas.

Como um exemplo em questão, o maquinário metatável Lua 5.3 tornaria intuitivamente Lua bastante lento, mas na prática há rumores de que Lua é bastante rápida (e mais rápida que o Python).

Além disso, tenho a intuição (talvez errada) de que, como nos processadores atuais, a memória é muito mais lenta que a computação bruta (um acesso à memória com falta de cache precisa do mesmo tempo que centenas de operações aritméticas), verificação dinâmica de tipo (à la if (value->type != INTEGER_TAG) return;in Linguagem) poderia correr muito rápido.

Obviamente, toda a análise do programa (como a implementação do Stalin Scheme ) pode fazer uma implementação dinâmica da linguagem, à medida que um tradutor corre rápido, mas vamos fingir que não tenho tempo para projetar um analisador de programa inteiro no início.

(Estou projetando uma linguagem dinâmica no meu monitor MELT , e parte dela seria traduzida para C)



1
Lua Performance Tips , que explica por que alguns programas Lua são lentos e como corrigi-los.
Robert Harvey

Respostas:


24

Quais recursos semânticos do Python (e de outras linguagens dinâmicas) contribuem para sua lentidão?

Nenhum.

O desempenho das implementações de idiomas é uma função do dinheiro, recursos e teses de doutorado, não dos recursos de idiomas. Self é muito mais dinâmico que Smalltalk e um pouco mais dinâmico que Python, Ruby, ECMAScript ou Lua, e tinha uma VM que superava todas as VMs Lisp e Smalltalk existentes (na verdade, a distribuição Self era fornecida com um pequeno interpretador Smalltalk escrito em Self , e até isso era mais rápido que a maioria das VMs Smalltalk existentes) e era competitivo com as implementações de C ++ da época.

Então, a Sun parou de financiar o Self e a IBM, Microsoft, Intel e Co. começaram a financiar C ++, e a tendência foi revertida. Os desenvolvedores do Self deixaram a Sun para abrir sua própria empresa, onde usaram a tecnologia desenvolvida para a Self VM para criar uma das VMs Smalltalk mais rápidas de todos os tempos (a VM Animórfica) e, em seguida, a Sun comprou de volta a empresa e uma versão ligeiramente modificada do essa VM Smalltalk agora é mais conhecida sob o nome de "HotSpot JVM". Ironicamente, os programadores Java desprezam as linguagens dinâmicas por serem "lentos", quando, na verdade, Javaficou lento até adotar a tecnologia de linguagem dinâmica. (Sim, isso mesmo: a JVM do HotSpot é essencialmente uma VM Smalltalk. O verificador de bytecode faz muita verificação de tipo, mas uma vez que o bytecode é aceito pelo verificador, a VM e, especialmente, o otimizador e o JIT, na verdade, não muito interesse com os tipos estáticos!)

O CPython simplesmente não faz muita coisa que torna as linguagens dinâmicas (ou melhor, o despacho dinâmico) rápido: compilação dinâmica (JIT), otimização dinâmica, inlining especulativo, otimização adaptativa, des otimização dinâmica, feedback / inferência do tipo dinâmico. Também existe o problema de que quase todo o núcleo e a biblioteca padrão são escritos em C, o que significa que, mesmo que você torne o Python 100x mais rápido de repente, isso não ajudará muito, porque algo como 95% do código executado por um O programa Python é C, não Python. Se tudo fosse escrito em Python, até mesmo acelerações moderadas criariam um efeito de avalanche, em que os algoritmos ficam mais rápidos e as estruturas de dados principais ficam mais rápidas, mas é claro que as estruturas de dados principais também são usadas nos algoritmos e nos algoritmos e dados principais. estruturas são usadas em qualquer outro lugar,

Existem algumas coisas notoriamente ruins para as linguagens OO gerenciadas por memória (dinâmicas ou não) nos sistemas atuais. A memória virtual e a proteção de memória podem ser determinantes para o desempenho da coleta de lixo em particular e o desempenho do sistema em geral. E é completamente desnecessário em um idioma seguro para a memória: por que proteger contra acessos ilegais à memória quando não há acesso à memória no idioma? A Azul decidiu usar MMUs poderosas e modernas (Intel Nehalem e mais recentes, e o equivalente da AMD) para ajudar na coleta de lixo em vez de impedi-la, mas mesmo sendo suportado pela CPU, os atuais subsistemas de memória dos sistemas operacionais convencionais não são poderosos o suficiente para permitir isso (e é por isso que a JVM da Azul realmente é virtualizada no bare metal, além de o SO, não dentro dele).

No projeto Singularity OS, a Microsoft mediu um impacto de ~ 30% no desempenho do sistema ao usar a proteção MMU em vez do sistema de tipos para separação de processos.

Outra coisa que a Azul notou ao criar suas CPUs Java especializadas foi que as CPUs mainstream modernas se concentram na coisa completamente errada ao tentar reduzir o custo de falhas de cache: elas tentam reduzir o número de falhas de cache através de coisas como previsão de ramificação, pré-busca de memória, e assim por diante. Mas, em um programa OO fortemente polimórfico, os padrões de acesso são basicamente pseudo-aleatórios, simplesmente não há nada a prever. Portanto, todos esses transistores são desperdiçados e o que se deve fazer é reduzir o custo de cada falta de cache individual. (O custo total é #misss * cost, o mainstream tenta reduzir o primeiro, o Azul o segundo.) Os Java Compute Accelerators da Azul podem ter 20.000 erros de cache simultâneos em andamento e ainda fazer progresso.

Quando a Azul começou, eles pensaram em pegar alguns componentes de E / S disponíveis no mercado e projetar seu próprio núcleo de CPU especializado, mas o que eles realmente precisaram fazer foi exatamente o oposto: eles adotaram um padrão padrão de mercado. prateleira núcleo RISC de 3 endereços e projetou seu próprio controlador de memória, MMU e subsistema de cache.

tl; dr : A "lentidão" do Python não é uma propriedade da linguagem, mas a) sua implementação (primária) ingênua eb) o fato de que as CPUs e sistemas operacionais modernos são projetados especificamente para fazer o C rodar mais rápido e os recursos que eles oferecem. O C para C não está ajudando (cache) ou até prejudicando ativamente o desempenho do Python (memória virtual).

E você pode inserir praticamente qualquer linguagem gerenciada por memória com polimorfismo ad-hoc dinâmico aqui ... quando se trata dos desafios de uma implementação eficiente, até Python e Java são praticamente "a mesma linguagem".


Você tem um link ou referência para Azul?
Basile Starynkevitch

4
Discordo que a semântica de uma linguagem não afeta sua capacidade de ser implementada com eficiência. Sim, uma boa implementação de JIT com otimizações e análises de primeira classe pode fazer uma grande melhoria no desempenho de uma linguagem, mas no final existem certos aspectos da semântica que inevitavelmente acabarão sendo gargalos. Seja o requisito de C para aliasing estrito de ponteiros ou o requisito de Python de que as operações de lista sejam executadas atomicamente, existem certas decisões semânticas que inevitavelmente acabam prejudicando o desempenho de alguns aplicativos.
Jules

1
Como um aparte ... você tem uma referência para essa melhoria de 30% no Singularity? Sou defensor de sistemas operacionais de proteção baseada em idioma há muitos anos, mas nunca vi esse número antes, e o acho bastante surpreendente (os números que vi no passado foram mais próximos de 10%) e me pergunto o que eles fizeram para conseguir que melhora muito ...
Jules

5
@ MasonWheeler: Porque existem apenas implementações de Python ruins por aí. Nenhum implementador de Python gastou até uma fração minúscula do dinheiro, pessoas, pesquisa e recursos que IBM, Sun, Oracle, Google e Co. gastaram em J9, JRockit, HotSpot e Co. Todas as 5 implementações de Python combinadas provavelmente nem sequer ter a mão de obra que a Oracle está gastando apenas no coletor de lixo. A IBM está trabalhando em uma implementação Python baseada no Eclipse OMR (a estrutura VM de código aberto componente extraída do J9). Estou disposto a apostar que seu desempenho estará dentro de uma ordem de magnitude de J9
Jörg W Mittag

2
Para o registro, C é lento em comparação com o Fortran para trabalho numérico, pois o Fortran impõe um aliasing estrito para que o otimizador possa ser mais agressivo.
Michael Shopsin

8

Embora a implementação atual do Python (que carece de muitas otimizações realizadas por outras linguagens dinâmicas, por exemplo, implementações modernas de Javascript e, como você aponta, Lua) seja a fonte da maioria de seus problemas, ela tem alguns problemas semânticos que o tornariam difícil para uma implementação competir com outros idiomas, pelo menos em certos campos. Alguns que valem particularmente a pena considerar:

  • As operações de lista e dicionário são exigidas pela definição de idioma para serem atômicas. Isso significa que, a menos que um compilador JIT seja capaz de provar que nenhuma referência a um objeto de lista escapou de seu encadeamento atual (uma análise que é difícil em muitos casos e impossível no caso geral), deve garantir que o acesso ao objeto seja serializado (por exemplo, via travamento). A implementação do CPython resolve esse problema usando o notório "bloqueio global de intérpretes", que impede que o código Python seja efetivamente usado em ambientes de multiprocessamento com técnicas de multithread e, embora outras soluções sejam possíveis, todos eles têm problemas de desempenho.

  • Python não tem mecanismo para especificar o uso de objetos de valor; tudo é tratado por referência, adicionando indireção extra onde não é necessariamente necessário. Embora seja possível para um compilador JIT inferir objetos de valor em alguns casos e otimizar automaticamente isso, não é possível fazer isso geralmente e, portanto, código que não é escrito com cuidado para garantir que a otimização seja possível (o que é uma arte negra) sofrerá.

  • O Python tem uma evalfunção, o que significa que um compilador JIT não pode fazer suposições sobre ações que não ocorrem, mesmo se ele executar uma análise de todo o programa, desde que evalseja usado uma vez. Por exemplo, um compilador Python não pode assumir que uma classe não tem subclasses e, portanto, desvirtualiza as chamadas de método, porque essa suposição pode ser posteriormente negada por meio de uma chamada para eval. Em vez disso, ele deve executar verificações de tipo dinâmico para garantir que as suposições feitas pelo código compilado nativo não tenham sido invalidadas antes da execução desse código.


3
O último ponto pode ser mitigado evalacionando uma recompilação e / ou des otimização.
Jörg W Mittag

4
A propósito, isso também não é exclusivo do Python. Java (ou melhor, a JVM) possui carregamento dinâmico de código e vinculação dinâmica, portanto, a Análise de hierarquia de classes é equivalente a resolver o problema de parada também. No entanto, o HotSpot especula de maneira feliz especula métodos polimórficos e, se algo na hierarquia de classes mudar, bem, apenas os retirará da linha.
Jörg W Mittag
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.