Sites modernos como o Google Docs e o Gmail executam tanto JavaScript que a velocidade do motor tinha dois gargalos separados. O interpretador de bytecode em C++ do Firefox era lento e não coletava nenhuma informação de tipo, enquanto o JIT Baseline compilava cada função para código de máquina; em páginas pesadas isso significava compilar milhares de funções, de modo que o tempo ia para a compilação em vez da execução. O JIT otimizador Ion só ficava rápido depois que o JIT Baseline havia coletado dados de tipo.

As correções óbvias tinham cada uma um custo: acelerar o interpretador em C++ e ele ainda não fornece nenhum dado de tipo ao otimizador; atrasar ou mover a compilação Baseline e os caminhos frios ou os bailouts tornam-se arriscados; retomar os bailouts do Ion no meio do código JIT e cada recurso precisa de metadados extras e código complexo para depuradores e exceções. A Mozilla, em vez disso, adicionou uma nova camada, o Interpretador Baseline, situado entre o interpretador em C++ e o JIT Baseline.

A medida que tornou isso barato não foi escrever um interpretador do zero. O JIT Baseline é um JIT de modelo: cada instrução de bytecode já mapeia para uma sequência majoritariamente fixa de código de máquina. A Mozilla gerou essas mesmas sequências em um laço de interpretador encadeado (threaded), compartilhando um único gerador de código modelado, o mesmo layout de frame e uma única estrutura JitScript que guarda os caches inline e os dados de perfilamento usados pelas duas camadas.

Como a interpretação agora coletava informações de tipo, o limiar de aquecimento do JIT Baseline pôde subir de 10 para 100, reduzindo drasticamente quantas funções eram compiladas. O Firefox Nightly mediu carregamentos de página de 2 a 8% mais rápidos, testes de devtools de 2 a 10% mais rápidos, economia de memória e código mais simples: os bailouts do Ion passaram a ser retomados no interpretador, e boa parte da complicada maquinaria de depuração e tratamento de exceções foi removida.

Gerar a camada barata a partir do mesmo gerador de código da camada rápida, para que os caches e os dados de tipo permaneçam compartilhados

Reutilizar o gerador de código do JIT significa que o comportamento do interpretador e os caches inline coincidem com os do JIT por construção, sem uma segunda implementação para manter em sincronia.

A interpretação agora coleta os mesmos dados de tipo que o JIT costumava coletar, de modo que a camada otimizadora obtém seu perfil sem esperar pela compilação.

Elevar o limiar de aquecimento do JIT em dez vezes reduziu drasticamente quantas funções eram compiladas em páginas com muito JS.

Bailouts, suporte a depurador e tratamento de exceções ficaram mais simples porque o Ion é retomado no interpretador em vez de no meio do código JIT, precisando de menos metadados.

Entrar na camada intermediária é barato: a alocação de JitScript é pelo menos 10 vezes mais rápida que a compilação do JIT Baseline.

Não escreva à mão a camada intermediária entre a lenta e a rápida: gere-a a partir dos próprios modelos da camada rápida, e caches, frames e comportamento permanecem idênticos por construção

O Interpretador Baseline chegou ao Firefox Nightly em julho de 2019 e estava a caminho do lançamento geral do Firefox 70 naquele outubro. As medições da Mozilla mostraram carregamentos de página de 2 a 8% mais rápidos e testes de devtools de 2 a 10% mais rápidos, com economia de memória além disso. A limpeza que ele possibilitou também acelerou a compilação do JIT Baseline, e o próximo passo declarado pela equipe era tirar essa compilação da thread principal.

CONFIRA AS EVIDÊNCIAS

As fontes

  1. The Baseline Interpreter: a faster JS interpreter in Firefox 70 Mozilla Hacks · hacks.mozilla.org ·
  2. Hacker News discussion: The Baseline Interpreter: a faster JS interpreter in Firefox 70 Hacker News · news.ycombinator.com ·