O que governa a "velocidade" de uma linguagem de programação?
Não existe a "velocidade" de uma linguagem de programação. Existe apenas a velocidade de um programa específico escrito por um programador específico executado por uma versão específica de uma implementação específica de um mecanismo de execução específico executando em um ambiente específico.
Pode haver grandes diferenças de desempenho na execução do mesmo código escrito no mesmo idioma na mesma máquina usando implementações diferentes. Ou mesmo usando versões diferentes da mesma implementação. Por exemplo, executar exatamente o mesmo benchmark ECMAScript na mesma máquina, usando uma versão do SpiderMonkey de 10 anos atrás versus uma versão deste ano provavelmente trará um aumento de desempenho entre 2 × –5 ×, talvez até 10 ×. Isso significa que o ECMAScript é 2x mais rápido que o ECMAScript, porque executar o mesmo programa na mesma máquina é 2x mais rápido com a implementação mais recente? Isso não faz sentido.
Isso tem algo a ver com gerenciamento de memória?
Na verdade não.
Por que isso acontece?
Recursos. Dinheiro. A Microsoft provavelmente emprega mais pessoas fazendo café para seus programadores de compiladores do que a comunidade inteira de PHP, Ruby e Python combinada e pessoas trabalhando em suas VMs.
Para mais ou menos qualquer recurso de uma linguagem de programação que afeta o desempenho de alguma forma, também há uma solução. Por exemplo, C (eu estou usando C aqui como substituto de uma classe de linguagens semelhantes, algumas das quais existiam antes de C) não é seguro para a memória, de modo que vários programas C em execução ao mesmo tempo podem atropelar memória um do outro. Então, inventamos a memória virtual e fazemos com que todos os programas C passem por uma camada de indireção, para que possam fingir que são os únicos em execução na máquina. No entanto, isso é lento e, portanto, inventamos a MMU e implementamos a memória virtual no hardware para acelerá-la.
Mas! Idiomas seguros para memória não precisam de tudo isso! Ter memória virtual não os ajuda nem um pouco. Na verdade, é pior: não apenas a memória virtual não ajuda as linguagens seguras, mas também a memória virtual, mesmo quando implementada em hardware, afeta o desempenho. Isso pode ser especialmente prejudicial ao desempenho dos coletores de lixo (que é o que um número significativo de implementações de linguagens seguras para memória).
Outro exemplo: as CPUs modernas de uso geral convencionais empregam truques sofisticados para reduzir a frequência de falhas de cache. Muitos desses truques significam tentar prever qual código será executado e que memória será necessária no futuro. No entanto, para idiomas com um alto grau de polimorfismo de tempo de execução (por exemplo, idiomas OO), é realmente difícil prever esses padrões de acesso.
Porém, existe outra maneira: o custo total de falhas de cache é o número de falhas de cache multiplicado pelo custo de uma falha individual de cache. As CPUs convencionais tentam reduzir o número de erros, mas e se você pudesse reduzir o custo de um erro individual?
A CPU Azul Vega-3 foi projetada especificamente para executar JVMs virtualizadas e tinha uma MMU muito poderosa com algumas instruções especializadas para ajudar na coleta de lixo e na detecção de escape (o equivalente dinâmico à análise de escape estático) e poderosos controladores de memória e todo o sistema ainda pode progredir com mais de 20.000 falhas de cache pendentes em voo. Infelizmente, como a maioria das CPUs específicas de idioma, seu design foi simplesmente gasto e forçado pelos "gigantes" Intel, AMD, IBM e similares.
A arquitetura da CPU é apenas um exemplo que afeta a facilidade ou a dificuldade de implementar uma linguagem de alto desempenho. Uma linguagem como C, C ++, D, Rust, que seja adequada ao modelo de programação de CPU mainstream moderno, será mais fácil de acelerar do que uma linguagem que precisa "combater" e contornar a CPU, como Java, ECMAScript, Python, Ruby , PHP.
Realmente, é tudo uma questão de dinheiro. Se você gastar quantias iguais de dinheiro para desenvolver um algoritmo de alto desempenho no ECMAScript, uma implementação de alto desempenho do ECMAScript, um sistema operacional de alto desempenho projetado para o ECMAScript, uma CPU de alto desempenho projetada para o ECMAScript como foi gasto nos últimos décadas para fazer com que os idiomas do tipo C sejam mais rápidos, é provável que você tenha desempenho igual. Só que, neste momento, muito mais dinheiro foi gasto para tornar as linguagens do tipo C mais rápidas do que para as linguagens do ECMAScript, e as suposições das linguagens do tipo C são agrupadas em toda a pilha, de MMUs e CPUs a sistemas operacionais e sistemas operacionais. sistemas de memória virtual até bibliotecas e estruturas.
Pessoalmente, eu estou mais familiarizado com Ruby (que geralmente é considerado uma "linguagem lenta"), então darei dois exemplos: a Hash
classe (uma das estruturas de dados centrais em Ruby, um dicionário de valores-chave) no Rubinius A implementação do Ruby é escrita em Ruby 100% puro e tem o mesmo desempenho que oHash
classe em YARV (a implementação mais usada), escrita em C. E há uma biblioteca de manipulação de imagens escrita como uma extensão C para YARV, que também possui uma "versão de fallback" pura (lenta) do Ruby para implementações que não suporta C, que usa uma tonelada de truques Ruby altamente dinâmicos e reflexivos; um ramo experimental do JRuby, utilizando a estrutura de intérpretes Truffle AST e a estrutura de compilação Graal JIT do Oracle Labs, pode executar a "versão de fallback" pura do Ruby tão rápido quanto o YARV pode executar a versão C altamente otimizada original. Isso é simplesmente (bem, qualquer coisa, menos) alcançado por algumas pessoas realmente inteligentes fazendo coisas realmente inteligentes com otimizações dinâmicas de tempo de execução, compilação JIT e avaliação parcial.