JIT: compilação em tempo de execução
Por que V8 executa JavaScript quase tão rápido quanto C, enquanto Python interpretado é 50x mais lento — e como o JIT detecta e otimiza automaticamente os caminhos quentes.
Compilar o que é usado, quando é usado
Compilar todo o código antecipadamente (AOT) desperdiça tempo compilando código que raramente roda. Interpretar tudo é simples mas lento. O JIT (Just-In-Time compiler) faz o melhor dos dois: começa interpretando, monitora quais funções são chamadas frequentemente (hot paths), e compila só essas para código de máquina otimizado.
O JIT também faz especulação de tipos: se uma função sempre recebe inteiros, o JIT gera código que assume inteiros — eliminando as verificações de tipo que o interpretador faria a cada iteração. Se um float aparece, o JIT desotimiza e recompila.
a + b verifica o tipo de a e b em tempo de execução.
PyPy, uma implementação alternativa de Python com JIT, é 5-10x mais rápida que CPython.
Veja o JIT em ação
Clique em Executar loop. As primeiras iterações são interpretadas (barras maiores). Quando o JIT detecta o hot path, compila — e as barras ficam muito menores.
function somaQuadrados(n) {
let soma = 0;
for (let i = 1; i <= n; i++) {
soma += i * i; // ← caminho quente
}
return soma;
}
// chamada em loop:
for (let c = 0; c < 20; c++) somaQuadrados(1000);Como o JIT decide o que compilar
// Como o JIT decide o que compilar // Cada função tem um contador de chamadas function chamada(fn) { fn.callCount++; if (fn.callCount === THRESHOLD_COMPILE) { // "Caminho quente" detectado! Compila para código nativo fn.nativeCode = jitCompile(fn.bytecode, fn.types); } if (fn.nativeCode) { return fn.nativeCode(); // execução nativa — 10-100x mais rápido } else { return interpretar(fn.bytecode); // ainda interpretando } } // Deoptimização: se a suposição de tipo muda, joga fora function soma(a, b) { return a + b; } soma(1, 2); // JIT assume: ambos são inteiros → gera ADD int32 soma(1.5, 2); // float aparece! Deoptimiza e recompila para float
O pipeline do V8
# Pipeline de execução do V8 (Chrome/Node.js) JavaScript Source ↓ [Parser] → AST ↓ [Ignition] → Bytecode (interpretador rápido) ↓ (hot functions) [Sparkplug] → Código nativo não otimizado (compilação rápida) ↓ (very hot) [Maglev] → Código nativo otimizado (médio prazo) ↓ (very very hot) [TurboFan] → Código nativo altamente otimizado (especulativo) ↓ (type changes) Deoptimize → volta ao Ignition bytecode # V8 usa 4 níveis de otimização progressiva! # Cada nível compila mais rápido mas gera código menos otimizado.
obj.metodo(), o JIT verifica o tipo de obj e armazena na "cache" o ponteiro
direto para o método. Na próxima chamada, pula a verificação de tipo — só compara "é o mesmo tipo?"
e salta direto. Isso elimina o dispatch de método dinâmico, a operação mais cara de linguagens OOP.
Simule um JIT simples
Mini projeto: implemente um "mini JIT" simulado: mantenha um contador de chamadas por função. Quando o contador atinge 5, "compile" (no caso: gere um cache com os tipos dos parâmetros e retorne uma versão pré-calculada mais rápida). Teste com uma função chamada em loop.
Projeto principal: use a API performance.now() para medir o tempo de 1000 chamadas de uma função matemática pesada antes e depois de aplicar memoization (cache de resultados). Depois compare com uma versão em WebAssembly (compile o mesmo algoritmo em C para Wasm usando Emscripten).
Desafio extra: estude o bytecode do CPython e do V8 Ignition. Escreva uma função que é rápida no V8 (tipos estáveis, sem polimorfismo) e uma que força deoptimização (mistura de tipos no mesmo array). Meça com %NeverOptimizeFunction e --trace-opt do Node.js.
Teste sua intuição
soma(a,b) assumindo que a é inteiro. Se a virar float, o que acontece?
Onde você encontra isso
JavaScript no browser
V8 (Chrome/Node), SpiderMonkey (Firefox) e JavaScriptCore (Safari) todos têm JITs. É por isso que JavaScript moderno é rápido o suficiente para jogos 3D, editores de vídeo e CAD no browser — coisas que pareciam impossíveis em 2008.
WebAssembly vs. JS JIT
Wasm não precisa de JIT — é compilado AOT para bytecode tipado e verificável. O browser compila Wasm para código nativo em milissegundos. Para algoritmos numéricos, Wasm supera o JIT do JS porque não precisa especular sobre tipos.
PyPy e Python 3.13
PyPy é uma implementação de Python com JIT que chega a 10x mais rápido que CPython. Python 3.13 adicionou um JIT experimental (copy-and-patch). NumPy e numba usam JIT (via LLVM) para computação científica rápida em Python.
JVM HotSpot
A JVM HotSpot monitora bytecode e usa dois JITs: C1 (client, compila rápido) e C2 (server, compila lento mas gera código ótimo). Após warm-up (~10.000 chamadas), Java frequentemente supera C++ em benchmarks de longa duração.