Pular para o conteúdo principal

Comemorando 10 anos do V8

· Leitura de 14 minutos
Mathias Bynens ([@mathias](https://twitter.com/mathias)), historiador do V8

Este mês marca o aniversário de 10 anos do lançamento não apenas do Google Chrome, mas também do projeto V8. Este post apresenta uma visão geral dos principais marcos do projeto V8 nos últimos 10 anos, bem como nos anos anteriores, quando o projeto ainda era secreto.

Uma visualização da base de código do V8 ao longo do tempo, criada usando gource.

Antes do lançamento do V8: os primeiros anos

O Google contratou Lars Bak no outono de 2006 para construir um novo motor JavaScript para o navegador web Chrome, que na época ainda era um projeto interno secreto do Google. Lars havia se mudado recentemente de volta para Aarhus, Dinamarca, vindo do Vale do Silício. Como não havia um escritório do Google na região e Lars queria permanecer na Dinamarca, ele e vários dos engenheiros originais do projeto começaram a trabalhar no projeto em um anexo na fazenda dele. O novo runtime JavaScript foi batizado de “V8”, uma referência descontraída ao motor potente encontrado em carros muscle clássicos. Mais tarde, quando a equipe do V8 cresceu, os desenvolvedores se mudaram de suas instalações modestas para um prédio de escritórios moderno em Aarhus, mas a equipe levou consigo seu foco singular em construir o runtime JavaScript mais rápido do planeta.

Lançando e evoluindo o V8

O V8 foi aberto como software de código aberto no mesmo dia em que o Chrome foi lançado: em 2 de setembro de 2008. O commit inicial data de 30 de junho de 2008. Antes dessa data, o desenvolvimento do V8 acontecia em um repositório privado CVS. Inicialmente, o V8 suportava apenas os conjuntos de instruções ia32 e ARM e usava SCons como sistema de construção.

2009 viu a introdução de um novo motor de expressões regulares chamado Irregexp, resultando em melhorias de desempenho para expressões regulares no mundo real. Com a introdução de uma portabilidade para x64, o número de conjuntos de instruções suportados aumentou de dois para três. 2009 também marcou o primeiro lançamento do projeto Node.js, que embute o V8. A possibilidade de projetos fora do navegador embutirem o V8 foi explicitamente mencionada na HQ original do Chrome. Com o Node.js, isso realmente aconteceu! Node.js tornou-se um dos ecossistemas JavaScript mais populares.

2010 testemunhou um grande aumento no desempenho em tempo de execução à medida que o V8 introduziu um novo compilador JIT otimizado. Crankshaft gerava código de máquina duas vezes mais rápido e 30% menor do que o compilador anterior (sem nome) do V8. Nesse mesmo ano, o V8 adicionou seu quarto conjunto de instruções: MIPS de 32 bits.

2011 chegou, e a coleta de lixo foi amplamente aprimorada. Um novo coletor de lixo incremental reduziu drasticamente os tempos de pausa enquanto mantinha ótimo desempenho máximo e baixo uso de memória. O V8 introduziu o conceito de Isolates, que permite aos embutidores iniciar várias instâncias do runtime V8 em um processo, abrindo caminho para Web Workers mais leves no Chrome. A primeira das duas migrações de sistema de construção do V8 ocorreu quando fizemos a transição do SCons para GYP. Implementamos suporte para o modo estrito do ES5. Enquanto isso, o desenvolvimento mudou de Aarhus para Munique (Alemanha) sob nova liderança, com muita troca de ideias com a equipe original em Aarhus.

2012 foi um ano de marcos para o projeto V8. A equipe realizou sprints de velocidade para otimizar o desempenho do V8, medido através das suítes de benchmark SunSpider e Kraken. Posteriormente, desenvolvemos uma nova suíte de benchmarks chamada Octane (com V8 Bench em seu núcleo), que trouxe a competição de desempenho máximo para o centro das atenções e impulsionou melhorias massivas na tecnologia de runtime e JIT em todos os principais motores de JavaScript. Um dos resultados desses esforços foi a mudança de amostragem aleatória para uma técnica determinística baseada em contagem para detectar funções “quentes” no perfil de tempo de execução do V8. Isso tornou significativamente menos provável que alguns carregamentos de página (ou execuções de benchmark) fossem aleatoriamente muito mais lentos do que outros.

2013 testemunhou o aparecimento de um subconjunto de baixo nível do JavaScript chamado asm.js. Como o asm.js se limita a aritmética tipada estaticamente, chamadas de função e acessos à pilha apenas com tipos primitivos, o código validado em asm.js poderia executar com desempenho previsível. Lançamos uma nova versão do Octane, Octane 2.0, com atualizações para benchmarks existentes e novos benchmarks que visam casos de uso como asm.js. Octane impulsionou o desenvolvimento de novas otimizações de compilador, como allocation folding e otimizações baseadas em locais de alocação para transições de tipos e pré-alocação, que melhoraram bastante o desempenho máximo. Como parte de um esforço que internamente apelidamos de “Handlepocalypse”, a API Handle do V8 foi completamente reescrita para torná-la mais fácil de usar corretamente e com segurança. Ainda em 2013, a implementação de TypedArrays em JavaScript no Chrome foi movida do Blink para o V8.

Em 2014, o V8 transferiu parte do trabalho de compilação JIT fora da thread principal com compilação concorrente, reduzindo interrupções e melhorando significativamente o desempenho. Mais tarde naquele ano, nós lançamos a versão inicial de um novo compilador de otimização chamado TurboFan. Paralelamente, nossos parceiros ajudaram a portar o V8 para três novas arquiteturas de conjuntos de instruções: PPC, MIPS64 e ARM64. Seguindo o Chromium, o V8 fez a transição para outro sistema de construção, GN. A infraestrutura de teste do V8 teve melhorias significativas, com um Tryserver disponível para testar cada patch em diversos bots antes de ser integrado. Para o controle de versão, o V8 migrou de SVN para Git.

2015 foi um ano movimentado para o V8 em várias frentes. Implementamos armazenamento de código em cache e streaming de scripts, acelerando significativamente os tempos de carregamento de páginas web. O trabalho no uso de mementos de alocação em nosso sistema de runtime foi publicado na ISMM 2015. Mais tarde naquele ano, nós iniciamos o trabalho em um novo interpretador chamado Ignition. Experimentamos a ideia de subdefinir JavaScript com modo forte para alcançar garantias mais fortes e desempenho mais previsível. Implementamos o modo forte por trás de uma flag, mas posteriormente descobrimos que seus benefícios não justificavam os custos. A adição de uma fila de commits trouxe grandes melhorias na produtividade e estabilidade. O coletor de lixo do V8 também começou a cooperar com incorporadores, como o Blink, para agendar trabalho de coleta de lixo durante períodos de inatividade. A coleta de lixo em tempo de inatividade reduziu significativamente interrupções perceptíveis de coleta de lixo e o consumo de memória. Em dezembro, o primeiro protótipo de WebAssembly foi integrado ao V8.

Em 2016, enviamos as últimas partes do conjunto de recursos do ES2015 (anteriormente conhecido como "ES6") (incluindo promessas, sintaxe de classe, escopo lexical, desestruturação e mais), bem como alguns recursos do ES2016. Também começamos a implementar o novo pipeline Ignition e TurboFan, usando-o para compilar e otimizar recursos do ES2015 e ES2016, e ativando Ignition como padrão para dispositivos Android de baixo desempenho. Nosso trabalho bem-sucedido sobre coleta de lixo em tempo de inatividade foi apresentado em PLDI 2016. Iniciamos o projeto Orinoco, um novo coletor de lixo majoritariamente paralelo e concorrente para o V8, com o objetivo de reduzir o tempo de coleta de lixo na thread principal. Em uma grande mudança de foco, desviamos nossos esforços de desempenho de micro-benchmarks sintéticos e passamos a medir e otimizar seriamente o desempenho do mundo real. Para depuração, o inspetor do V8 foi migrado do Chromium para o V8, permitindo que qualquer integrador do V8 (e não apenas o Chromium) use o DevTools do Chrome para depurar o JavaScript em execução no V8. O protótipo WebAssembly evoluiu de protótipo para suporte experimental, em coordenação com outros fornecedores de navegadores, proporcionando suporte experimental ao WebAssembly. O V8 recebeu o Prêmio ACM SIGPLAN de Linguagens de Programação e Software. E uma nova porta foi adicionada: S390.

Em 2017, finalmente concluímos nossa reformulação de vários anos do motor, ativando o novo pipeline Ignition e TurboFan por padrão. Isso tornou possível posteriormente remover o Crankshaft (130.380 linhas de código removidas) e Full-codegen da base de código. Lançamos a versão 1.0 do Orinoco, incluindo marcação concorrente, coleta concorrente, escaneamento paralelo e compactação paralela. Reconhecemos oficialmente o Node.js como um integrador de primeira classe do V8, ao lado do Chromium. Desde então, é impossível que um patch do V8 seja aceito caso isso quebre o conjunto de testes do Node.js. Nossa infraestrutura ganhou suporte para validação de corretude por meio de fuzzing, garantindo que qualquer parte do código produza resultados consistentes independentemente da configuração na qual é executado.

Em um lançamento coordenado por toda a indústria, o V8 enviou o WebAssembly ativado por padrão. Implementamos suporte para módulos JavaScript, bem como os conjuntos completos de recursos ES2017 e ES2018 (incluindo funções assíncronas, memória compartilhada, iteração assíncrona, propriedades de descanso/espalhamento e recursos RegExp). Lançamos suporte nativo para cobertura de código JavaScript e o Web Tooling Benchmark para nos ajudar a medir como as otimizações do V8 impactam o desempenho de ferramentas reais de desenvolvimento e do código JavaScript que elas geram. O rastreamento de wrapper de objetos JavaScript para objetos DOM C++ e vice-versa permitiu que resolvêssemos vazamentos de memória de longa data no Chrome e manejássemos eficientemente o fechamento transitivo de objetos nas heaps do JavaScript e Blink. Posteriormente, usamos essa infraestrutura para aumentar as capacidades da ferramenta de snapshot da heap.

O ano de 2018 viu um evento de segurança em toda a indústria abalar o que pensávamos saber sobre segurança da informação em CPUs com a divulgação pública das vulnerabilidades Spectre/Meltdown. Os engenheiros do V8 realizaram extensas pesquisas ofensivas para ajudar a entender a ameaça para linguagens gerenciadas e desenvolver mitigações. O V8 enviou mitigações contra Spectre e ataques de canal lateral semelhantes para integradores que executam códigos não confiáveis.

Recentemente, enviamos um compilador básico para WebAssembly chamado Liftoff, que reduz significativamente o tempo de inicialização para aplicativos WebAssembly enquanto ainda alcança desempenho previsível. Lançamos o BigInt, um novo primitivo JavaScript que permite inteiros de precisão arbitrária. Implementamos builtins incorporados e possibilitamos desserialização preguiçosa deles, reduzindo significativamente a pegada do V8 para múltiplos Isolates. Tornamos possível compilar o bytecode do script em uma thread em segundo plano. Iniciamos o projeto Unified V8-Blink Heap para executar uma coleta de lixo sincronizada entre os componentes V8 e Blink. E o ano ainda não acabou...

Altos e baixos de desempenho

A pontuação do V8 Bench do Chrome ao longo dos anos mostra o impacto das mudanças do V8 no desempenho. (Estamos usando o V8 Bench porque é um dos poucos benchmarks que ainda pode ser executado no beta original do Chrome.)

Pontuação do V8 Bench no Chrome de 2008 a 2018

Nossa pontuação neste benchmark aumentou nos últimos dez anos!

No entanto, você pode notar duas quedas de desempenho ao longo dos anos. Ambas são interessantes porque correspondem a eventos significativos na história do V8. A queda de desempenho em 2015 ocorreu quando o V8 introduziu versões básicas dos recursos do ES2015. Esses recursos abrangiam vários aspectos na base de código do V8, e, portanto, focamos na correção em vez do desempenho para o lançamento inicial. Aceitamos essas quedas moderadas de velocidade para disponibilizar os recursos aos desenvolvedores o mais rápido possível. No início de 2018, a vulnerabilidade Spectre foi divulgada, e o V8 enviou mitigações para proteger os usuários contra possíveis explorações, resultando em outra regressão no desempenho. Felizmente, agora que o Chrome está lançando Site Isolation, podemos desativar as mitigações novamente, trazendo o desempenho de volta ao nível anterior.

Outra conclusão deste gráfico é que ele começa a estabilizar por volta de 2013. Isso significa que o V8 desistiu e parou de investir em desempenho? Muito pelo contrário! O achatamento dos gráficos representa a mudança de foco da equipe do V8 de micro-benchmarks sintéticos (como V8 Bench e Octane) para otimização de desempenho no mundo real. O V8 Bench é um benchmark antigo que não utiliza quaisquer recursos modernos do JavaScript, nem se aproxima do código de produção do mundo real. Compare isso com o conjunto de benchmarks mais recente, Speedometer:

Pontuação do Speedometer 1 do Chrome de 2013 a 2018

Embora o V8 Bench mostre melhorias mínimas de 2013 a 2018, nossa pontuação no Speedometer 1 aumentou (mais) durante o mesmo período de tempo. (Usamos o Speedometer 1 porque o Speedometer 2 utiliza recursos modernos do JavaScript que ainda não eram suportados em 2013.)

Hoje em dia, temos benchmarks ainda melhores que refletem com mais precisão os aplicativos modernos em JavaScript, e além disso, medimos e otimizamos ativamente os aplicativos da web existentes.

Resumo

Embora o V8 tenha sido originalmente criado para o Google Chrome, ele sempre foi um projeto independente com uma base de código separada e uma API de incorporação que permite a qualquer programa usar seus serviços de execução de JavaScript. Nos últimos 10 anos, a natureza aberta do projeto ajudou-o a se tornar uma tecnologia essencial não apenas para a Plataforma Web, mas também em outros contextos como o Node.js. Ao longo dessa trajetória, o projeto evoluiu e permaneceu relevante apesar de muitas mudanças e crescimento dramático.

Inicialmente, o V8 suportava apenas dois conjuntos de instruções. Nos últimos 10 anos, a lista de plataformas suportadas chegou a oito: ia32, x64, ARM, ARM64, MIPS de 32 e 64 bits, PPC de 64 bits e S390. O sistema de build do V8 migrou de SCons para GYP e depois para GN. O projeto mudou-se da Dinamarca para a Alemanha e agora conta com engenheiros em todo o mundo, incluindo Londres, Mountain View e São Francisco, com contribuições externas ao Google vindas de muitos outros lugares. Reformulamos toda nossa pipeline de compilação de JavaScript, passando de componentes sem nome para Full-codegen (um compilador básico) e Crankshaft (um compilador otimizador dirigido por feedback) para Ignition (um interpretador) e TurboFan (um compilador otimizador dirigido por feedback ainda melhor). O V8 deixou de ser “apenas” um motor de JavaScript para também oferecer suporte ao WebAssembly. A própria linguagem JavaScript evoluiu do ECMAScript 3 para o ES2018; a versão mais recente do V8 ainda implementa recursos pós-ES2018.

O arco de história da Web é longo e duradouro. Celebrar o 10º aniversário do Chrome e do V8 é uma boa oportunidade para refletir que, embora este seja um grande marco, a narrativa da Plataforma Web já dura mais de 25 anos. Não temos dúvidas de que a história da Web continuará pelo menos tanto tempo no futuro. Estamos comprometidos em garantir que V8, JavaScript e WebAssembly continuem sendo personagens interessantes nessa narrativa. Estamos animados para ver o que a próxima década reserva. Fique ligado!