2 pontos por GN⁺ 2024-12-06 | 1 comentários | Compartilhar no WhatsApp

Melhorando o desempenho do Ruby: reescrevendo C em Ruby

Comparação de desempenho do Ruby
  • Em um repositório recente de comparação entre linguagens, o Ruby foi avaliado como mais rápido que R e Python, mas ainda como a terceira linguagem mais lenta.
  • Os benchmarks são compostos por dois testes, "Loops" e "Fibonacci", cada um destacando loops e condicionais, overhead de chamadas de função e desempenho de recursão.
Comparação de desempenho entre Ruby e Node.js
  • Em um MacBook Pro com M3, o Ruby 3.3.6 leva 28 segundos no exemplo de loop e 12 segundos no exemplo de Fibonacci.
  • O Node.js leva cerca de 1 segundo em ambos os exemplos.
  • Em um MacBook Air com M2, o desempenho do Ruby é ainda pior.
O significado desses benchmarks
  • Esses benchmarks talvez não tenham grande significado na prática.
  • O Python foi avaliado como a linguagem mais lenta, mas é a linguagem mais usada no GitHub.
  • Linguagens de programação devem ser eficientes, mas utilidade e produtividade da linguagem são mais importantes que desempenho.
Aplicação do YJIT
  • Ao aplicar o YJIT, o desempenho de Fibonacci melhora bastante.
  • No exemplo de loop, a melhora de desempenho é pequena.
Otimização de código Ruby
  • Range#each é escrito em C, então não pode ser otimizado pelo YJIT.
  • Integer#times foi convertido de C para Ruby no Ruby 3.3, permitindo otimização pelo YJIT.
  • Array#each foi convertido de C para Ruby no Ruby 3.4.
Otimização de Integer#times
  • Integer#succ funciona mais rápido que i += 1.
  • O YJIT otimiza Integer#times, melhorando bastante o desempenho.
Otimização de Array#each
  • Array#each foi convertido de C para Ruby no Ruby 3.4, permitindo otimização pelo YJIT.
  • O código C é avaliado em Ruby usando o módulo Primitive.
Repositório Ruby Microbench
  • Os benchmarks são executados com várias versões do Ruby e com YJIT.
  • O Ruby 3.4 com YJIT apresenta uma grande melhora de desempenho.
Otimização de range#each
  • É possível melhorar o desempenho implementando a classe Range em Ruby puro.
Biblioteca padrão do YJIT
  • A equipe do YJIT está substituindo código C por Ruby para melhorar o desempenho.
  • Usa-se o bloco with_yjit para utilizar a implementação em Ruby quando o YJIT está ativado.
Investigação de otimizações do YJIT
  • O YJIT otimiza o desempenho convertendo bytecode da VM do Ruby em código de máquina.
  • Ao analisar o código de máquina de Integer#succ, é possível entender o processo de otimização do YJIT.

1 comentários

 
GN⁺ 2024-12-06
Comentários do Hacker News
  • O exemplo de loop repete 1 bilhão de vezes e usa loops aninhados. A suposição é que esse benchmark consumirá mais de 99% do tempo nas duas primeiras linhas

    • Com análise de vivacidade sobre os elementos do array, seria possível eliminar todo o loop externo e transformar o programa de forma simples
    • Fico curioso se o compilador consegue fazer esse tipo de análise
    • Mesmo que u não seja conhecido em tempo de compilação, o loop interno ainda poderia ser substituído por algumas instruções
  • Há uma menção às futuras versões do Ruby, com Ruby 3.4.0 previsto para este Natal e Ruby 3.5.0 para o próximo Natal

    • Fico curioso sobre o impacto do JIT mínimo do Python nesse tipo de loop
    • O Python 3.13 precisa ser compilado com o JIT ativado, e seria interessante rodar o benchmark assim
  • Ainda existe muito carinho pelo Ruby. Obrigado, Matz

  • Houve um PR no começo de 2024 para melhorar o desempenho de Integer#succ, e isso ajudou a entender por que usar Integer#succ

    • Integer#succ é usado ao reescrever métodos de loop, e no interpretador opt_succ (i = i.succ) é processado mais rapidamente do que putobject 1; opt_plus (i += 1)
    • Pessoalmente, uso #succ com frequência por legibilidade, e o uso duas vezes no método #bytes de uma biblioteca de UUID para manter o “modo de fatiamento de bits” ao ler o código
  • Compartilhando uma experiência com TruffleRuby, dizendo que o TruffleRuby é mais rápido que Node.js e se aproxima de Bun ou Golang

    • Não há certeza se o benchmark fornecido mostra a velocidade do TruffleRuby depois da mudança
    • Gostaria de validar o benchmark e adicioná-lo como commit no repositório principal
  • O Ruby ficou muito rápido, e o TruffleRuby é ainda mais impressionante

  • Não sabia que o YJIT foi escrito em Rust

  • O Python foi a linguagem mais lenta no benchmark, mas em outubro de 2024 era a linguagem mais usada no GitHub

    • Parece haver uma correlação entre a lentidão da linguagem e sua popularidade
  • Existe um repositório antigo de comparação entre linguagens com mais linguagens incluídas

  • Isso trouxe uma grande mudança para as soluções do Advent of Code, e elas parecem surpreendentemente parecidas