1 pontos por GN⁺ 2025-11-19 | 1 comentários | Compartilhar no WhatsApp
  • YJIT e ZJIT são estruturas de compiladores JIT no Ruby 3.x que convertem código Ruby em linguagem de máquina para aumentar a velocidade de execução
  • O YJIT conta quantas vezes cada função ou bloco é chamado e, ao atingir um certo limite, converte esse código em linguagem de máquina
  • O código convertido é armazenado em blocos YJIT, e cada bloco converte vários comandos YARV em instruções de máquina ARM64 correspondentes
  • Usando Branch Stub, observa os tipos de dados reais em tempo de execução e gera seletivamente as instruções de máquina apropriadas
  • Essa estrutura é um mecanismo central para alcançar ao mesmo tempo melhor desempenho de execução do Ruby e eficiência no tratamento de tipos dinâmicos

Chapter 4: Compilando Ruby para linguagem de máquina

Interpreting vs. Compiling Ruby Code

  • O texto original não traz detalhes

Counting Method and Block Calls

  • O YJIT rastreia o número de chamadas de funções e blocos do programa para identificar código hotspot
    • Ao lado da sequência de instruções YARV de cada função ou bloco, armazena os valores jit_entry e jit_entry_calls
    • jit_entry começa como null e depois armazena o ponteiro para o código de máquina gerado pelo YJIT
    • jit_entry_calls aumenta em 1 a cada chamada
  • Quando o número de chamadas atinge o limite, o YJIT compila aquele código em linguagem de máquina
    • No Ruby 3.5, o limite padrão é de 30 chamadas para programas pequenos e 120 chamadas para aplicações de grande porte
    • Pode ser alterado em tempo de execução com a opção --yjit-call-threshold
  • Com isso, o YJIT converte em linguagem de máquina apenas o código executado com frequência, garantindo um caminho de execução mais eficiente

YJIT Blocks

  • O YJIT armazena as instruções de máquina geradas em blocos YJIT
    • Um bloco YJIT é diferente de um bloco Ruby e corresponde a uma parte da sequência de instruções YARV
    • Cada função ou bloco Ruby é composto por vários blocos YJIT
  • No programa de exemplo, o YJIT começa a compilar quando o bloco é executado pela 30ª vez
    • Converte a primeira instrução YARV, getlocal_WC_1, em linguagem de máquina e cria um novo bloco YJIT
    • Depois compila também a instrução getlocal_WC_0 e a inclui no mesmo bloco
  • Segundo a Figura 4-8, o YJIT gera instruções ARM64 e carrega valores nos registradores x1 e x9 do processador M1
    • getlocal_WC_1 armazena na pilha a variável local do frame de pilha anterior, e getlocal_WC_0 armazena a variável da pilha atual
    • As instruções de máquina geradas executam o mesmo comportamento

YJIT Branch Stubs

  • Ao compilar a instrução opt_plus, o YJIT encontra o problema de não saber o tipo dos operandos
    • Dependendo do tipo, como inteiro, string ou ponto flutuante, as instruções de máquina necessárias são diferentes
    • Ex.: soma de inteiros usa a instrução adds, enquanto soma de ponto flutuante exige outra instrução
  • Para resolver isso, o YJIT usa observação em tempo de execução em vez de análise prévia
    • Durante a execução do programa, verifica os tipos reais dos valores passados e gera a linguagem de máquina correspondente
  • Para esse comportamento, usa Branch Stub
    • Quando um novo branch ainda não tem um bloco conectado, ele é ligado temporariamente a um stub
    • Depois que o tipo real é confirmado, esse stub é substituído pelo bloco apropriado

ZJIT (apenas mencionado)

  • O sumário inclui uma seção sobre ZJIT, mas o texto não traz explicações concretas

Resumo

  • O YJIT é um compilador JIT para o Ruby 3.5 voltado a melhorar a eficiência de execução de uma linguagem de tipos dinâmicos
  • Os pontos centrais são gatilho de compilação baseado em número de chamadas, estrutura de blocos YJIT e verificação de tipos em tempo de execução por meio de Branch Stub
  • Na arquitetura ARM64, ele converte para instruções reais de máquina para aumentar a velocidade de execução do código Ruby
  • O ZJIT é mencionado como um JIT de próxima geração, mas sem detalhes no texto

1 comentários

 
GN⁺ 2025-11-19
Comentários do Hacker News
  • Houve uma época em que o MacRuby era compilado para código nativo no macOS usando LLVM e integrado aos frameworks Objective‑C
    Era uma ideia bem interessante, mas no fim parece que a Apple mudou de direção para o Swift
    Quando sair a nova edição, pretendo comprar e ler Ruby Under a Microscope. Ainda gosto muito de Ruby, mas não tive muitas oportunidades de usá-la na prática

    • Depois que o criador do MacRuby saiu da Apple, ele criou o RubyMotion
      Hoje outras pessoas tocam o projeto, mas a impressão é que o foco atual está mais no DragonRuby (uma implementação de Ruby voltada para jogos)
    • Depois que o autor saiu, o MacRuby continuou como RubyMotion
      Aliás, também existe um artigo na Wikipedia
    • Ainda hoje dá para criar apps para macOS, iOS e iPadOS usando Objective‑C
      Só que as APIs antigas talvez não sejam mais suportadas
    • Para quem já trabalhou com várias linguagens, a mudança da Apple para Swift lembra um pouco a transição da Microsoft de VB6 para VB.Net
      O VB6 era realmente muito rápido para desenvolver, e dava para mexer com Direct3D e até ASP Classic
      A elegância e a facilidade de desenvolvimento do Ruby me fazem lembrar daquela época
      Se Ruby tivesse tido ferramentas de GUI no nível do VB6, acho que sua popularidade poderia ter sido bem diferente
  • Fico muito feliz em ver o Pat continuando a tocar o projeto
    O primeiro livro Ruby Under a Microscope e os posts de blog dele foram uma grande inspiração para mim
    Já cheguei a conhecê-lo pessoalmente numa conferência Euruko, e ele era realmente uma pessoa excelente

    • Obrigado pelo comentário tão gentil
  • Quando li Ruby Under a Microscope pela primeira vez, achei muito divertido
    Isso inclusive me ajudou antigamente a resolver desafios de CTF
    Hoje em dia não acompanho mais de perto a implementação interna do Ruby, mas pretendo comprar a nova edição quando sair

    • Usei bastante Ruby de 2002 até 2010, mas depois praticamente larguei
      Este texto me deu vontade de ler de novo a nova edição do livro
  • Já que estamos falando de compilação de Ruby, fiquei curioso se alguém já experimentou o Sorbet compiler, criado por desenvolvedores da Stripe
    Post de anúncio open source do Sorbet Compiler

    • É uma pena que ele tenha sumido do repositório e aparentemente não esteja mais em desenvolvimento
      Compilação AOT é realmente muito difícil em Ruby
      O interessante na abordagem do Sorbet é que ela consegue criar caminhos rápidos com base na verificação de tipos do Ruby
      Eu também estou fazendo um compilador de Ruby como projeto pessoal, e estou usando como referência hokstad.com/compiler e
      writing-a-compiler-in-ruby
      No momento estou focado em passar no RubySpec, e depois pretendo tentar otimizações baseadas em tipos
  • Não é diretamente sobre compilação de Ruby, mas o livro Enterprise Integration with Ruby me trouxe muitos insights sobre o uso de Ruby fora do contexto web

  • Desde que conheci o MRuby, fiquei viciado na diversão de transformar meus projetos e scripts em executáveis independentes

  • Fico feliz que Ruby Under a Microscope continue sendo atualizado
    Acho que é leitura obrigatória para quem quer entender o funcionamento interno do Ruby

  • Eu tinha curiosidade sobre como o YJIT rastreia a compilação por tipo de entrada quando um bloco é executado várias vezes
    Queria entender como o Ruby lida com diferentes tipos, como int e float

    • Esse é justamente o ponto central do YJIT
      Ele usa uma abordagem de "esperar para ver" e adia a compilação até que os tipos reais sejam fornecidos
      Mantém versões separadas do bloco para cada tipo e chama a apropriada conforme a situação
      Esse algoritmo é chamado de Basic Block Versioning
      Maxime Chevalier‑Boisvert, da Shopify, explica isso muito bem na palestra da RubyConf 2021
      O novo motor JIT, o ZJIT, parece usar uma abordagem diferente
  • Tornar linguagens dinamicamente tipadas rápidas com JIT normalmente cobra o preço de aumentar o uso de memória
    Fora de empresas grandes como a Shopify, isso pode ser um problema ainda maior

    • Mas empresas pequenas normalmente também têm aplicações menores
      Hoje em dia, instâncias de nuvem costumam oferecer algo em torno de 4 GiB de memória por núcleo, então algumas centenas de MB de código JIT são perfeitamente administráveis
  • Achei simples o jeito como o YJIT encontra hotspots apenas contando o número de chamadas de função
    Fiquei pensando se não existe algo como nos JITs de JavaScript, que detectam operações pesadas dentro de loops
    A estrutura de blocos do Ruby talvez até ajude nesse tipo de otimização

    • Exato, como o Ruby trata o corpo do loop como um bloco,
      o JIT pode tratar esse bloco como se fosse uma função separada e otimizar iterações de forma natural
      Esse ponto deve ser tratado mais a fundo no próximo capítulo