Shopping cart

Subtotal $0.00

View cartCheckout

Building better devs

TnewsTnews
Programação

Matz Usou o Claude para Criar um Compilador Ruby 86x Mais Rápido

Email : 17

Matz Acabou de Dropar um Compilador que Transforma Ruby em Binários Nativos

Yukihiro “Matz” Matsumoto, o criador do Ruby, largou um repositório no GitHub que ninguém esperava: um compilador AOT (ahead-of-time) que pega código Ruby e cospe executáveis nativos. Sem dependência do interpretador. Sem runtime pesado. Só um binário que roda direto no sistema operacional.

O projeto se chama Spinel, e os benchmarks mostram speedups de até 86.7x em tarefas computacionais pesadas. Eu fiquei cético quando vi o número — até olhar os detalhes.

Ah, e tem um detalhe que fez o Hacker News pirar: Matz usou o Claude como assistente de programação para construir o compilador. Em aproximadamente um mês. Um compilador auto-hospedado. Escrito em Ruby. Que compila a si mesmo.

Vamos destrinchar isso.

O Que o Spinel Faz (E O Que Ele Não Faz)

O Spinel implementa um pipeline de compilação em quatro estágios:

  1. Parsing — usa o Prism (libprism), o parser oficial do Ruby, para gerar uma AST (árvore sintática abstrata)
  2. Inferência de tipos — analisa o programa inteiro e deduz os tipos de cada variável e expressão
  3. Geração de código C — transforma a AST tipada em código C otimizado
  4. Compilação nativa — um compilador C padrão (gcc/clang) gera o binário final

O resultado? Um executável standalone que depende apenas de libc e libm. Nada de Ruby instalado na máquina. Nada de gems. Nada de bundler. Só o binário.


# Compilar um programa Ruby
./spinel meu_programa.rb

# Resultado: um executável nativo
./meu_programa

Parece simples demais, né? É porque por baixo tem 21 mil linhas de código fazendo o trabalho pesado.

Os Benchmarks que Chamaram Atenção

Eu não costumo confiar em benchmarks de README — todo mundo cherry-picka os números bonitos. Mas o Spinel roda contra 28 benchmarks diferentes, e a média geométrica dá ~11.6x mais rápido que o miniruby (Ruby 4.1.0dev).

Os destaques:

Benchmark Speedup vs miniruby O que faz
Game of Life (Conway) 86.7x Simulação com arrays e loops
Ackermann 74.8x Recursão profunda
Red-black tree 22.6x Estrutura de dados com inserção/busca
JSON parsing 10.1x Parse de strings e manipulação
Ray tracing 8.0x Cálculos de ponto flutuante
Lista encadeada 2.9x Alocação de objetos

O baseline é o miniruby do Ruby 4.1.0dev — que já é consideravelmente mais rápido que o Ruby 3.x que a maioria roda em produção. Então o speedup real comparado ao Ruby de “mercado” é ainda maior.

Os casos com ganhos enormes (86x, 74x) são computação pura: loops apertados, recursão, manipulação de arrays. O compilador brilha quando consegue eliminar todo o overhead de dispatch dinâmico e garbage collection.

Onde o ganho é menor (2.9x na lista encadeada) é onde a alocação de objetos ainda domina o tempo de execução.

A Arquitetura: Um Compilador que Compila a Si Mesmo

A parte mais elegante do Spinel é que ele é self-hosting. O backend do compilador (spinel_codegen.rb) é escrito em Ruby — um subconjunto de Ruby que o próprio Spinel consegue compilar.

Traduzindo: o Spinel compila o Spinel. O compilador gera um binário de si mesmo.

Isso não é só um truque técnico bonito. Self-hosting é um marco clássico em design de compiladores. Se o seu compilador consegue compilar a si mesmo, significa que ele é expressivo o suficiente para implementar um sistema complexo. É o equivalente a um organismo que se reproduz.

A história de desenvolvimento também é interessante:

  • Versão 1: Implementado em C puro (18 mil linhas)
  • Versão 2: Reescrito em Ruby
  • Versão 3 (atual): Reescrito novamente em um subconjunto de Ruby que o Spinel compila

Cada reescrita foi um teste: “meu compilador consegue expressar a próxima versão de si mesmo?”

Os componentes:


spinel                 → Shell wrapper
spinel_parse.c         → Frontend (Prism/libprism) — 1.061 linhas
spinel_codegen.rb      → Backend (inferência + codegen) — 21.109 linhas
lib/sp_runtime.h       → Runtime — 581 linhas
lib/sp_bigint.c        → Suporte a inteiros grandes — 5.394 linhas
lib/regexp/            → Engine de regex — 1.759 linhas

O backend sozinho tem 21 mil linhas em um único arquivo Ruby. Sim, isso levantou discussões sobre manutenibilidade — e é aqui que entra a parte da IA.

O Papel do Claude na Construção

Nos comentários do Hacker News, veio à tona que Matz usou o Claude como assistente de programação durante o desenvolvimento do Spinel. A comunidade teve reações mistas, mas o consenso foi surpreendentemente positivo.

O argumento mais repetido: IA não substituiu o Matz — amplificou ele. Um programador que já tem décadas de experiência em design de linguagens e compiladores conseguiu usar IA para acelerar a implementação de algo que ele já sabia como construir.

Como um comentarista colocou: “transforma programadores 10x em programadores 100x.”

Isso levanta uma questão que eu acho mais interessante que o próprio compilador: qual é o papel da IA quando o humano que usa ela é literalmente o criador da linguagem? Matz não estava pedindo para a IA “inventar” um compilador. Ele estava delegando tarefas de implementação para uma ferramenta que executa rápido o que ele já projetou na cabeça.

É a diferença entre “vibe coding” (jogar prompts e ver o que sai) e usar IA como um par de mãos extras guiado por expertise profunda.

As Otimizações Que Fazem a Mágica

O Spinel não é rápido por acidente. Ele implementa um conjunto de otimizações que atacam diretamente os gargalos clássicos do Ruby:

1. Alocação de structs na stack

Classes pequenas (até 8 campos escalares) viram structs C alocadas na stack, não no heap. Zero pressão no garbage collector.


# No CRuby: aloca no heap, GC precisa rastrear
class Point
  attr_accessor :x, :y
end

# No Spinel: vira struct C na stack
# typedef struct { int64_t x; int64_t y; } Point;

2. Flattening de concatenação de strings

Expressões como a + b + c + d que no CRuby geram 3 alocações intermediárias, no Spinel colapsam em uma única alocação com o tamanho total pré-calculado.

3. Loop-invariant code motion

Cálculos de .length em arrays e strings dentro de loops são movidos para fora do loop quando o compilador prova que o valor não muda.


# Antes da otimização
while i < arr.length   # .length recalculado a cada iteração
  # ...
end

# Depois da otimização
len = arr.length        # calculado uma vez
while i < len
  # ...
end

4. Inlining de métodos

Métodos curtos recebem a diretiva static inline no código C gerado, permitindo que o compilador C os insira diretamente no call site.

5. Interning de symbols em tempo de compilação

Symbols literais (:nome, :status) são resolvidos em tempo de compilação. Interning dinâmico só é incluído no binário quando efetivamente usado — dead code elimination remove o resto.

6. Propagação de constantes

Valores literais são inlined diretamente nos pontos de uso, eliminando lookups em tabelas de constantes.

Essas otimizações combinadas explicam por que o Game of Life roda 86x mais rápido: é um programa que aloca muitos objetos pequenos, faz loops intensivos e usa operações de array repetidamente. Exatamente o perfil que o Spinel otimiza melhor.

O Que o Spinel NÃO Suporta

Aqui é onde a realidade bate. O Spinel não compila Ruby “de verdade” — ele compila um subconjunto de Ruby. E as exclusões são significativas:

Não funciona:

  • eval, instance_eval, class_eval — avaliação dinâmica de código
  • send, method_missing, define_method — metaprogramação runtime
  • Thread, Mutex — multi-threading
  • Encoding além de UTF-8/ASCII
  • Patterns complexos de lambda calculus

Pra quem programa Ruby no dia a dia, essa lista dói. method_missing é a base de inúmeras gems. eval alimenta DSLs inteiras. send está em todo framework de testes.

Um dos comentários mais contundentes no HN resumiu assim: “Unless this gets back eval, metaprogramming and threads this isn’t all that interesting as an actual language.”

E ele tem razão — se você pretende compilar uma aplicação Rails ou rodar o RSpec, esqueça. Basicamente nenhuma gem popular roda sob as restrições do Spinel.

Mas eu acho que essa crítica erra o alvo. O Spinel não está tentando substituir o CRuby. Ele está criando uma nova categoria: Ruby para sistemas.

Ruby Para Sistemas: O Nicho Que Ninguém Ocupava

Pense nos casos de uso onde o Spinel brilha:

  • Ferramentas de CLI — um binário único, sem dependências, que roda em qualquer Linux
  • Scripts de infraestrutura — substituir bash por algo com tipos e performance
  • Ferramentas de build — bootstrapping sem precisar do Ruby instalado
  • Microserviços leves — processos com startup instantâneo
  • Computação científica — loops numéricos intensivos

Hoje, se você quer um binário nativo a partir de uma linguagem com sintaxe elegante, suas opções são:

Linguagem Trade-off
Go Verboso, sem expressividade
Rust Curva de aprendizado brutal
Crystal Parecido com Ruby, mas é outra linguagem
Nim Ecossistema pequeno
Spinel Subconjunto de Ruby, sem metaprogramação

O Crystal sempre foi a comparação óbvia. Mas o Crystal exige tipos estáticos explícitos e tem divergências significativas da sintaxe Ruby. O Spinel preserva a sensação dinâmica do Ruby enquanto faz inferência de tipos por debaixo dos panos.

Self-Hosting como Prova de Conceito

O fato do Spinel compilar a si mesmo é mais do que um truque legal — é uma demonstração de que o subconjunto suportado é genuinamente útil.

O backend do compilador (spinel_codegen.rb) usa:

  • Classes com herança
  • Mixins via include
  • attr_accessor
  • Pattern matching (case/in)
  • Operador de navegação segura (&.)
  • Iteradores (each, map, select, reduce)
  • Tratamento de exceções (begin/rescue)
  • Fibers para concorrência cooperativa
  • Regex via engine NFA embutida
  • Inteiros de precisão arbitrária
  • I/O de arquivos

21 mil linhas de código real, não um toy example. Se esse subconjunto é suficiente para implementar um compilador completo, é suficiente para a maioria das ferramentas de linha de comando e scripts de sistema.

Como Se Compara ao CRuby 4.1

O CRuby não parou no tempo. O Ruby 4.1 (dev) que serve de baseline para os benchmarks já inclui:

  • YJIT — JIT compiler que melhora performance de código orientado a objetos
  • Prism parser — o mesmo parser que o Spinel usa
  • Melhorias contínuas no garbage collector

E mesmo assim o Spinel é 11.6x mais rápido na média. Por quê?

A resposta é simples: compilação AOT com inferência de tipos global elimina overhead que nenhum JIT consegue remover. O YJIT otimiza hot paths em runtime, mas ainda carrega todo o peso de um interpretador completo, dispatch dinâmico e garbage collection generacional.

O Spinel sabe, em tempo de compilação, exatamente quais tipos cada variável vai ter. Não precisa de guards, deoptimization ou warmup. O binário sai otimizado desde a primeira instrução.

O Elefante na Sala: 21 Mil Linhas em Um Arquivo

Vou ser direto: um arquivo de 21 mil linhas com 15 níveis de nesting é um code smell monumental. Vários desenvolvedores apontaram isso no HN, questionando se o projeto é mantível sem assistência de IA.

A preocupação é legítima. Se o Spinel depende de “jogar o arquivo inteiro no contexto de um LLM” para fazer modificações, ele tem um problema de sustentabilidade. LLMs são ótimos para gerar código, mas a complexidade não desaparece só porque uma IA consegue navegar nela.

Por outro lado, compiladores historicamente são sistemas complexos com arquivos enormes. O GCC tem arquivos de 15 mil linhas. O V8 do Chrome também. Não é ideal, mas é a realidade de certos domínios.

O mais provável é que o Spinel evolua com refatoração gradual se ganhar tração na comunidade. Ou morra como um experimento fascinante — o que também é um resultado válido.

Implicações Para o Ecossistema Ruby

O Ruby vive um momento interessante. Depois de anos sendo “a linguagem do Rails” e perdendo mindshare para Python, Go e Rust, vários projetos estão tentando reinventar o que Ruby pode ser:

  • YJIT (Shopify) — JIT compiler integrado ao CRuby
  • Prism — parser unificado para o ecossistema
  • Spinel — compilação AOT nativa
  • rv — toolchain unificada para Ruby (em desenvolvimento)

O Spinel pode nunca substituir o CRuby para aplicações web. Mas ele abre uma porta que estava fechada: usar Ruby onde hoje você usaria Go ou Rust por necessidade, não por preferência.

Se você é um Rubyist que já escreveu scripts em bash porque “não vale a pena instalar Ruby no servidor de CI” ou “preciso de um binário único pra distribuir” — o Spinel é para você.

Instalação e Primeiros Passos

O Spinel roda em Linux e macOS. Você precisa de:

  • Ruby (CRuby) para bootstrap inicial
  • GCC ou Clang
  • libprism

# Clonar o repositório
git clone https://github.com/matz/spinel.git
cd spinel

# Bootstrap: compilar o Spinel usando CRuby
make bootstrap

# Agora o Spinel compila a si mesmo
make self-host

# Testar com um programa
echo 'puts "Hello from Spinel!"' > hello.rb
./spinel hello.rb
./hello

O binário gerado não precisa de nada além do sistema operacional. Copie pra outro servidor e rode.

O Que Esperar Daqui Pra Frente

O Spinel é um projeto jovem. Se Matz mantiver o desenvolvimento ativo (e considerando que ele é Matz, isso não é garantido — ele tem o Ruby inteiro pra cuidar), os próximos passos lógicos seriam:

  • Suporte a mais da stdlib — File, Net, JSON nativo
  • Concorrência — Fibers já funcionam, mas threads seriam um game-changer
  • Melhor interop com C — FFI simplificado para usar bibliotecas existentes
  • Tooling — LSP, debugger, profiler

Mas honestamente? Mesmo que o Spinel pare no estado atual, ele já provou um ponto: Ruby pode ser compilado para código nativo competitivo. E isso muda a conversa sobre o futuro da linguagem.

O repositório está em github.com/matz/spinel — e pelo nível de atividade nos últimos dias, a comunidade está prestando atenção.


Fonte de inspiração: Spinel: Ruby AOT Native Compiler — Hacker News, 268 pontos

Leave a Reply

Your email address will not be published. Required fields are marked *

Related Posts