Carregar um grande módulo WebAssembly costumava ser serial: o navegador baixava o arquivo inteiro para um ArrayBuffer, entregava-o ao motor JavaScript e só então começava a compilar. Em conexões rápidas, a rede já não era o gargalo; o tempo de CPU para decodificar e compilar era, e tudo isso acontecia após o download, com a thread principal pausada.
As correções óbvias tinham cada uma um custo. Um único compilador otimizador produzia código rápido, mas demorava a iniciar. Os motores JavaScript compilavam de forma preguiçosa em tempo de execução, o que evitava trabalho, mas mantinha a compilação na thread principal e esperava o término da análise. E parecia não haver maneira de compilar antes de o arquivo chegar, porque a API antiga só aceitava um ArrayBuffer completo.
O Firefox 58 atacou as duas pontas. A compilação em streaming injetava cada trecho baixado diretamente no motor por meio da API de streaming e, como a seção de código de um módulo wasm vem antes de seus dados, a compilação podia começar enquanto megabytes de dados ainda estavam em trânsito. Um novo compilador em camadas adicionou uma camada de base que gerava código 10-15x mais rápido que o compilador otimizador, e sua saída rodava apenas cerca de 2x mais devagar, de modo que o código começava a executar quase imediatamente; threads em segundo plano então recompilavam o módulo inteiro com o otimizador e faziam hot-swap. Ambos os compiladores dividiam o trabalho por função entre os núcleos.
Como a taxa de compilação agora excedia a taxa da rede, o código ficava pronto quase assim que o último pacote chegava. A Mozilla enquadrou a mudança como fazer o WebAssembly carregar como a decodificação de uma imagem, em vez de analisar JavaScript: sem pausa de compilação na thread principal e sem esperar pela carga inteira.
Transmitir bytes para uma camada de base rápida à medida que chegam, executar esse código imediatamente e fazer hot-swap do código otimizado em segundo plano
Compilar o primeiro pacote esconde o tempo de download dentro do trabalho de CPU, em vez de serializar download e depois compilação.
Uma camada de base que compila 10-15x mais rápido transforma a inicialização em um início rápido; o código cerca de 2x mais lento roda enquanto a otimização prossegue.
Como as funções wasm compilam independentemente, o trabalho se espalha pelos núcleos em vez de enfileirar em uma única thread.
Colocar o código de um módulo antes de seus dados permite que o streaming compile a parte cara enquanto os bytes da carga ainda estão chegando.
Cada mudança encurta a inicialização sem alterar o código de máquina que acaba rodando, então o desempenho de pico fica intacto.
Quando a etapa gargalo espera pela entrada inteira, comece-a no primeiro trecho e entregue primeiro uma versão barata; atualize-a em segundo plano em vez de bloquear
O Firefox 58 lançou o compilador de streaming e em camadas em janeiro de 2018; o texto explicativo do lançamento gerou uma thread de 912 pontos no Hacker News. A página de teste da Mozilla permitia aos usuários medir a velocidade de compilação em seus próprios dispositivos, e a equipe disse que a base estava lançada para armazenar em cache o código de máquina compilado no cache HTTP, estendendo o cache de bytecode JavaScript lançado na mesma versão.
CONFIRA AS EVIDÊNCIAS
As fontes
- Making WebAssembly even faster: Firefox's new streaming and tiering compiler Mozilla Hacks · hacks.mozilla.org ·
- Hacker News discussion: Making WebAssembly even faster Hacker News · news.ycombinator.com ·