Avanços no desempenho de JavaScript no IE10 e no Windows 8

Na quinta-feira, 31 de maio de 2012, lançamos o Windows 8 Release Preview e o Sexto IE10 Platform Preview. O Windows 8 inclui um mecanismo de navegação em HTML5 que impulsiona as experiências de navegação (estilo Metro e área de trabalho) assim como os aplicativos estilo Metro que usam HTML5 e JavaScript. A release preview representa uma revisão importante do mesmo mecanismo JavaScript moderno, Chakra, que estreou primeiro no IE9. Com cada visualização de plataforma fazemos progressos em relação às nossas metas para criar um mecanismo que ofereça ótimo desempenho na Web enquanto garante que ele seja altamente compatível, interoperável e seguro. Esta postagem vai explorar como o mecanismo JavaScript foi aprimorado para oferecer um ótimo desempenho em cenários de aplicativo Web emergentes.

Desempenho de aplicativos Web reais

Os aplicativos Web têm evoluído rapidamente em anos recentes. Há uma década, a Web consistia principalmente em sites com conteúdo estático, como o que você pode encontrar em um blog, uma página de aterrissagem de uma pequena empresa, ou no Wikipedia. O surgimento do AJAX ajudou a gerar sites mais complexos e interativos como o que você vê no Facebook ou JetSetter. Avanços subsequentes no desempenho permitiram que fossem criados aplicativos maiores e complexos, tais como Office 365, Bing Maps, etc. Mais recentemente, a expansão das APIs W3C padrão, os ganhos no desempenho de JavaScript e os gráficos acelerados de hardware possibilitaram a criação de jogos ainda mais sofisticados na Web, por exemplo, Angry Birds, Pirates Love Daisies, Cut The Rope, etc.

Diagrama mostrando o espectro de páginas da Web e suas características de desempenho. À esquerda, estão páginas da Web básicas onde o tempo de carregamento de página direciona a meta de desempenho. À direita estão os Aplicativos Web, os Jogos em HTML5 e os aplicativos estilo Metro do Windows 8, em que a Velocidade de Execução de JavaScript, Interações DOM e gráficos acelerados têm o maior impacto no desempenho.

Conforme os aplicativos evoluem, os fatores de desempenho que afetam a experiência do usuário se alteram. Para sites da Web tradicionais, o carregamento da página inicial determina a rapidez com que o usuário pode ver o conteúdo. Sites da Web interativos e aplicativos da Web grandes podem ser retidos pela eficiência das operações DOM, pelo processamento de CSS e pela manipulação de estado interno grande na memória. Jogos em HTML5 frequentemente dependem da renderização rápida da tela, da execução de JavaScript e da coleta de lixo eficiente. Resumindo, o desempenho do navegador é um problema complexo, que requer que sejam levadas em conta as necessidades de um amplo espectro de aplicativos diversos.

Nesta postagem, focalizaremos o desempenho de apenas um subsistema de navegador, o mecanismo JavaScript. Com os ganhos recentes no desempenho de JavaScript, para muitos aplicativos Web, a execução de JavaScript não é mais um fator limitador. Por outro lado, conforme o desempenho aumenta, surgem novos cenários que representam demandas adicionais para o mecanismo JavaScript. Continuamos a procura de oportunidades para desenvolver o Chakra de modo a atender aos requisitos de desempenho de aplicativos reais com uso intensivo de JavaScript.

Gráfico de duas dimensões mostrando capturas de tela de vários sites plotados em dois eixos: Uso de outros componentes de navegador (Y) e Execução de JavaScript (X). Os sites de conteúdo são ilustrados no lado esquerdo inferior (menor uso de outros componentes de navegador e menos uso de JavaScript). Jogos de gráficos intensivos, tais como o Angry Birds são mostrados no quadrante direito superior.
Dimensões do desempenho do aplicativo Web

Bastidores do Chakra

Desde a sua origem no IE9, o mecanismo JavaScript Chakra foi criado em torno de duas diretrizes, que permanecem igualmente importantes no IE10:

  • Minimizar a quantidade de trabalho no caminho crítico da experiência do usuário. Isso envolve atrasar o máximo possível até que seja absolutamente necessário, evitando trabalhar junto, usando períodos de inatividade e trabalhando em paralelo para minimizar o impacto na resposta do aplicativo.
  • Tirar vantagem de todo o hardware disponível. Isso se traduz em utilizar todos os núcleos da CPU disponíveis, assim como gerar instruções de CPU especializadas avançadas, por exemplo, SSE2 da Intel, se disponível.

Diagrama ilustrando o uso de dois núcleos de processador pelo mecanismo JavaScript Chakra'.
Arquitetura paralela do Chakra

O Chakra, embora seja apenas um dos subsistemas de navegador – é por si só composto de vários componentes que trabalham juntos para processar e executar código JavaScript. Quando o navegador baixa um arquivo JavaScript, ele passa o seu conteúdo para o analisador do Chakra verificar sua correção sintática. Essa é a única operação que se aplica ao arquivo inteiro. As etapas subsequentes são realizadas individualmente em cada função (incluindo a função global). Quando uma função estiver para ser executada (a função global é executada imediatamente após a análise), o analisador do Chakra cria uma representação do código da árvore de sintaxe abstrata (AST) e a entrega para o gerador de código de bytes, que produz um formato intermediário (código de bytes) adequado para ser executado pelo intérprete (mas não diretamente pela CPU). A AST e o código de bytes da função são preservados de forma que não precisem ser recriados em execuções subsequentes. O intérprete é invocado para executar a função. Conforme o intérprete executa operações individuais, ele coleta informações (um perfil) sobre os tipos de entradas que encontra e controla quantas vezes a função foi chamada.

Quando o número de chamadas alcança determinado limite, o intérprete enfileira a função para compilação. Ao contrário de outros navegadores, o compilador JIT (just-in-time) do Chakra é executado em um thread dedicado separado e, portanto, não interfere na execução do script. A única tarefa do compilador é gerar instruções de máquina otimizadas para cada função na fila de compilação. Quando uma função é compilada, a disponibilidade do código de máquina é sinalizada para o thread do script principal. Na próxima invocação, o ponto de entrada para a função é redirecionado para o código de máquina recém-compilado e a execução procede diretamente na CPU. É importante notar que as funções chamadas apenas uma ou duas vezes nunca são realmente compiladas, o que economiza tempo e recursos.

O JavaScript é um tempo de execução gerenciado nesse gerenciamento de memória ocultado do desenvolvedor e realizado por um coletor de lixo automático, que é executado periodicamente para limpar quaisquer objetos que não estejam mais sendo usados. O Chakra emprega um coletor de lixo conservador, quase geracional, marcar e limpar, que faz a maior parte de seu trabalho de forma simultânea em um thread dedicado para minimizar pausas na execução de script que interromperiam a experiência do usuário.

Essa arquitetura permite ao Chakra iniciar a execução do código JavaScript quase que imediatamente durante o carregamento da página. Por outro lado, durante períodos de tempo de intensa atividade de JavaScript, o Chakra pode trabalhar em paralelo e saturar até três núcleos de CPU, executando script, compilando e coletando lixo ao mesmo tempo.

Tempo de carregamento de página rápido

Até mesmo sites da Web relativamente estáticos tendem a usar JavaScript para interatividade, publicidade ou compartilhamento social. Na verdade, o volume de JavaScript incluído em Alexa’s Top 1 Million Pages tem aumentado regularmente, como relatado pelo HTTP Archive de Steve Souders.

Gráfico mostrando o volume de JavaScript em Alexa’s Top 1 Million Pages
Volume de JavaScript em Alexa’s Top 1 Million Pages

O código JavaScript incluído nesses sites da Web devem ser processados pelo mecanismo JavaScript do navegador e a função global de cada arquivo de script deve ser executada antes que o conteúdo seja totalmente renderizado. Consequentemente, é fundamental que a quantidade de trabalho realizado nesse caminho crítico seja minimizada. O analisador e o intérprete de código de bytes do Chakra foram criados com esse objetivo em mente.

Intérprete de código de bytes. O código JavaScript executado durante o carregamento da página frequentemente realiza a inicialização e a configuração que são feitas apenas uma vez. Para minimizar o tempo de carregamento de página geral, é fundamental começar a executar esse código imediatamente – sem esperar o compilador just-in-time processar o código e emitir instruções de máquina. O intérprete começa a executar o código JavaScript, assim que ele é convertido em código de bytes. Para reduzir mais o tempo da primeira instrução executada, o Chakra processa e emite o código de bytes apenas para funções que estão para ser executadas por meio de um mecanismo chamado análise adiada.

Análise adiada. Gráfico mostrando a fração de código executada em 11 sites populares. A quantidade vai de um pouco mais de 30% a um pouco mais de 50%.O projeto JSMeter da Microsoft Research mostrou que páginas da Web típicas usam apenas uma fração do código baixado – geralmente na ordem de 40-50% (consulte o gráfico à direita). Intuitivamente, isso faz sentido: os desenvolvedores frequentemente incluem bibliotecas JavaScript populares como jQuery ou dojo ou personalizadas como as usadas no Office 365, mas aproveita apenas uma fração da funcionalidade que a biblioteca suporta.

Para otimizar tais cenários, o Chakra realiza apenas a mais básica análise apenas de sintaxe do código-fonte. O resto do trabalho (criar a árvore de sintaxe abstrata e gerar o código de bytes) é realizado uma função de cada vez apenas quando a função está para ser invocada. Essa estratégia ajuda não somente a resposta do navegador quando a página da Web é carregada, mas também reduz o volume de memória.

No IE9, havia uma limitação na análise adiada do Chakra. As funções aninhadas dentro de outras funções tinham que ser analisadas imediatamente com suas funções incluídas. Essa restrição era importante porque muitas bibliotecas JavaScript empregam o chamado “padrão de módulo,” no qual a maior parte do código da biblioteca é incluída em uma função grande que é executada imediatamente. No IE10, removemos essa restrição e o Chakra agora adia a análise e a geração do código de bytes de qualquer função que não seja executada imediatamente.

Melhorias de desempenho de aplicativos com uso intensivo de JavaScript

No IE10, como antes no IE9, batalhamos para aprimorar o desempenho de aplicativos Web reais. Entretanto, os aplicativos Web dependem do desempenho de JavaScript em um grau variado. Para discutir as melhorias no IE10, é mais útil focalizar os aplicativos que fazem uso intenso de JavaScript; nos quais as melhorias no Chakra proporcionaram ganhos de desempenho substanciais. Uma classe importante de aplicativos com uso intensivo de JavaScript inclui os jogos e simulações em HTML5.

Na criação do IE10, analisamos uma amostra de jogos JavaScript populares (por exemplo, Angry Birds, Cut the Rope ou Tankworld) e simulações (por exemplo, FishIE Tank, HTML5 Fish Bowl, Ball Pool, Particle System) para compreender que as melhorias de desempenho teriam o impacto mais significativo na experiência do usuário. Nossa análise revelou inúmeras características e padrões de codificação comuns. Todos os aplicativos são direcionados por um retorno de chamada de timer de alta frequência. Muitos deles usam telas para renderização, mas alguns contam com elementos de animação DOM e alguns usam uma combinação dos dois. Em muitos aplicativos, pelo menos porções do código são escritas no estilo orientado em objeto – seja no código do aplicativo ou em bibliotecas incluídas (por exemplo, Box2d.js). Funções curtas são comuns, assim como leituras e gravações de propriedade e polimorfismo são frequentes. Todos os aplicativos realizam aritmética de ponto flutuante e muitos alocam uma quantidade razoável de memória, colocando pressão no coletor de lixo. Esses padrões comuns se tornaram o foco de nosso trabalho de desempenho no IE10. As seções seguintes descrevem as alterações que fizemos em resposta.

Compilador Just-in-Time – reconsiderado e aprimorado

O IE10 inclui melhorias substanciais no compilador JIT do Chakra. Adicionamos suporte a duas arquiteturas adicionais de processador: x64 e ARM. Por isso, se o seu aplicativo JavaScript é experimentado pelo usuário em um PC de 64 bits ou um tablet baseado em ARM, ele aproveita os benefícios da execução direta na CPU.

Também alteramos a abordagem fundamental para gerar o código de máquina. O JavaScript é uma linguagem muito dinâmica, o que limita a forma em que o compilador pode saber quando gerar o código. Por exemplo, ao compilar a função abaixo, o compilador não sabe a forma (layout de propriedade) dos objetos envolvidos ou os tipos de suas propriedades.

function compute(v, w) {

return v.x + w.y;

}

No IE9, o compilador do Chakra gerou um código que localizava cada propriedade no tempo de execução e lidava com todas as operações plausíveis (no exemplo acima: adição de inteiro, adição de ponto flutuante ou até mesmo concatenação de cadeias de caracteres). Algumas dessas operações eram manipuladas diretamente no código de máquina, enquanto outras requeriam a ajuda do tempo de execução do Chakra.

No IE10, o compilador JIT gera código de máquina baseado em perfil, de tipo especializado. Em outras palavras, ele gera um código de máquina que é adaptado a objetos de uma forma e valores em particular de um tipo particular. Para emitir o código correto, o compilador precisa saber quais tipos de valores de entrada esperar. Como o JavaScript é uma linguagem dinâmica, essas informações não ficam disponíveis no código-fonte. Aprimoramos o intérprete do Chakra para coletá-las no tempo de execução, uma técnica que chamados de criação dinâmica de perfil. Quando uma função é agendada para compilação JIT, o compilador examina o perfil de tempo de execução coletado pelo intérprete e emite o código adaptado às entradas esperadas.

O intérprete coleta as informações das execuções que ele observa, mas é possível que a execução do programa resulte em valores de tempo de execução que violem as presunções feitas no código otimizado gerado. Para cada presunção que faz, o compilador emite uma verificação de tempo de execução. Se uma execução posterior resultar em um valor inesperado, a verificação falha, a execução esgota o código de máquina especializado, e continua no intérprete. O motivo do esgotamento (a verificação falha) é registrado, informações de perfil adicionais são coletadas pelo intérprete, e a função é recompilada com presunções diferentes. Esgotamento e recompilação são dois recursos fundamentalmente novos no IE10.

O efeito prático é que o compilador IE10 do Chakra gera menos instruções de máquina para o seu código, reduzindo o volume de memória geral e agilizando a execução. Isso impacta particularmente os aplicativos com aritmética de ponto flutuante e acesso à propriedade do objeto, como os jogos e simulações em HTML5 que discutimos anteriormente.

Se você escrever o código JavaScript no estilo orientado a objeto, seu código também se beneficiará do suporte do Chakra a inlining de função. O código orientado a objeto comumente contém uma proporção grande de métodos relativamente pequenos, para a qual a sobrecarga da chamada à função é comparada significativamente ao tempo de execução da função. O inlining de função permite que o Chakra reduza a sobrecarga, mas, o mais importante, expande muito o escopo de outras otimizações de compilador tradicionais, tais como movimentação de código invariável em loop ou propagação de cópia.

Aritmética de ponto flutuante mais rápida

A maior parte dos programas JavaScript realizam uma certa quantidade de aritmética de inteiros. Como o exemplo abaixo ilustra, mesmo em programas que não focalizam principalmente a aritmética, valores inteiros são usados comumente como variáveis de iteração em loops ou como índices em matrizes.

function findString(s, a) {

for (var i = 0, al = a.length; i < al; i++) {

if (a[i] == s) return i;

}

return -1;

}

Por outro lado, a matemática de ponto flutuante é geralmente restrita a certas classes de aplicativos, tais como jogos, simulações, som, processamento de imagem ou de vídeo, etc. Historicamente, alguns desses aplicativos foram escritos em JavaScript, mas avanços recentes no desempenho do navegador tornaram viáveis as implementações de JavaScript. No IE9, otimizamos o Chakra para as operações de inteiro mais comuns. No IE10, aprimoramos extremamente a matemática de ponto flutuante.

function compute(a, b, c, d) {

return (a + b) * (c − d);

}

Dada uma função simples acima, o compilador JavaScript não pode determinar os tipos de argumentos a, b, c e d do código-fonte. O compilador do IE9 presumiria que os argumentos provavelmente eram números inteiros e geraria instruções rápidas de máquina de inteiro. Isso funcionaria muito bem se durante a execução os argumentos fossem, realmente, inteiros. Se, em vez disso, fossem usados números de ponto flutuante, o código teria que contar com funções de ajuda bem mais lentas no tempo de execução do Chakra. A sobrecarga de chamadas de função foi mais exacerbada, através do empacotamento e desempacotamento de valores intermediários no heap (na maioria dos mecanismos JavaScript de 32 bits, incluindo o Chakra, valores de ponto flutuante individuais devem ser alocados no heap). Na expressão acima, o resultado de cada operação exigiu uma alocação de heap, seguida pelo armazenamento do valor no heap e, em seguida, a recuperação do valor do heap para a próxima operação.

No IE10, o compilador tira vantagem das informações de perfil coletadas pelo intérprete para gerar um código de ponto flutuante muito mais rápido. No exemplo acima, se o perfil indica que todos os argumentos são provavelmente números de ponto flutuante, o compilador emitirá instruções de máquina de ponto flutuante. A expressão inteira será computada em apenas três instruções de máquina (presumindo-se que todos os argumentos já estão em registros), todos os valores intermediários serão armazenados em registros, e apenas a alocação de heap será necessária para retornar o resultado final.

Para aplicativos intensivos de ponto flutuante, trata-se de um ganho massivo de desempenho. Os experimentos mostram que no IE10 as operações de ponto flutuante são executadas cerca de 50% mais rapidamente do que no IE9. Além disso, a taxa reduzida de alocação de memória significa menos coletas de lixo.

Objetos mais rápidos e acesso à propriedade

Os objetos JavaScript são um mecanismo conveniente e amplamente usado para agrupar conjuntos de valores logicamente relacionados. Se você estiver usando objetos JavaScript em um estilo de programação orientado a objeto estruturado ou simplesmente como pacote flexível para valores, seu código se beneficiará muito das melhorias no desempenho de alocação de objeto e de acesso à propriedade adicionadas no IE10.

Como mencionado anteriormente, o acesso à propriedade eficiente é complicado no JavaScript porque a forma de um objeto não é conhecida durante a compilação. Os objetos JavaScript podem ser criados ad hoc sem uma classe ou tipo predefinido. Novas propriedades podem ser adicionadas (ou até mesmo removidas) a objetos ao acaso e em qualquer ordem. Consequentemente, ao compilar o método seguinte, o compilador não sabe onde encontrar os valores das propriedades x, y e z no objeto Vector.

Vector.prototype.magnitude = function() {

return Math.sqrt(this.x * this.x + this.y * this.y + this.z * this.z);

}

No IE9, introduzimos caches embutidos, o que aumentou muito o acesso a propriedades. Caches embutidos lembram a forma do objeto e o local na memória do objeto onde uma determinada propriedade pode ser encontrada. Caches embutidos podem lembrar a forma de apenas um objeto e funcionam bem se todos os objetos com os quais uma função trabalha forem da mesma forma . No IE10, adicionamos um mecanismo de cache secundário que aprimora o desempenho da operação do código em objetos de formas diferentes (polimórficos).

Antes que um valor de propriedade possa ser lido, o compilador deve verificar se a forma desse objeto corresponde a que está armazenada no cache embutido. Para fazer isso, no IE9, o compilador gera uma verificação de forma de tempo de execução antes de cada acesso à propriedade. Como os programas frequentemente leem ou gravam várias propriedades do mesmo objeto em uma sucessão fechada (como no exemplo abaixo), todas as verificações adicionam sobrecarga.

function collide(b1, b2) {

var dx = b1.x - b2.x;

var dy = b1.y - b2.y;

var dvx = b1.vx - b2.vx;

var dvy = b1.vy - b2.vy;

var distanceSquare = (dx * dx + dy * dy) || 1.0;

//...

}

No IE10, o Chakra gera um código adaptado à forma esperada do objeto. Através do controle de símbolos cuidadoso combinado com recursos de esgotamento e recompilação, o novo compilador reduz significativamente o número de verificações de forma de tempo de execução executadas. No exemplo acima, em vez de oito verificações de forma separadas, somente duas são feitas, cada uma para b1 e b2. Além disso, quando a forma de um objeto tiver sido estabelecida, todas as localizações de propriedade são conhecidas, e operações de leitura ou gravação são tão eficientes como em C++.

No ECMAScript, cinco objetos podem conter um novo tipo de propriedade, chamada propriedade de acessador. As propriedades de acessador diferem das propriedades de dados tradicionais no sentido de que funções get e set personalizadas são invocadas para lidar com operações de leitura e gravação. As propriedades de acessador são um mecanismo conveniente para adicionar encapsulamento de dados, propriedades computadas, validação de dados ou notificação de alteração. O sistema de tipo interno do Chakra e os caches embutidos são projetados para acomodar propriedades de acessador e facilitar a leitura e gravação eficientes de seus valores.

Se você for criar um jogo ou animação em HTML5, frequentemente precisará de um mecanismo de física que realize a computação necessária para produzir movimentos de objetos realistas sob a força da gravidade, simular colisões, etc. Para física muito simples, você pode criar seu próprio mecanismo, mas para requisitos mais complexos, você geralmente usaria uma das bibliotecas de física populares que estão disponíveis agora no JavaScript, tais como Box2d.js (portadas de Box2d). Essas bibliotecas frequentemente usam objetos pequenos, tais como Point, Vector ou Color. Em cada quadro de animação, um grande número desses objetos são criados e prontamente descartados. Portanto, é importante que o tempo de execução do JavaScript crie objetos de forma eficiente.

var Vector = function(x, y, z) {

this.x = x;

this.y = y;

this.z = z;

}

 

Vector.prototype = {

//...

normalize : function() {

var m = Math.sqrt((this.x * this.x) + (this.y * this.y) + (this.z * this.z));

return new Vector(this.x / m, this.y / m, this.z / m);

},

 

add : function(v, w) {

return new Vector(w.x + v.x, w.y + v.y, w.z + v.z);

},

 

cross : function(v, w) {

return new Vector(-v.z * w.y + v.y * w.z, v.z * w.x - v.x * w.z, -v.y * w.x + v.x * w.y);

},

//...

}

No IE10, o layout interno de objetos JavaScript é otimizado para simplificar a criação do objeto. No IE9, cada objeto consistia em um cabeçalho de tamanho fixo e uma matriz de propriedade expansível. A matriz é necessária para acomodar propriedades adicionais que podem ser adicionadas depois que o objeto tiver sido criado. Nem todos os aplicativos JavaScript exploram essa flexibilidade, e os objetos frequentemente recebem a maioria de suas propriedades na construção. Esse característica permite que o Chakra aloque muitas das propriedades de tais objetos diretamente com o cabeçalho, o que resulta em uma única alocação de memória (em vez de duas) para cada objeto recém-criado. Essa alteração também reduz o número de desreferências da memória para ler ou gravar a propriedade do objeto, e aprimora a utilização do registro. O layout de objeto aprimorado e menos verificações de forma de tempo de execução resultam no acesso à propriedade até 50% mais rápido.

Melhorias na coleta de lixo

Como discutido acima, os jogos e as animações em HTML5 frequentemente criam e descartam objetos em uma taxa muito alta. Os programas JavaScript não destroem explicitamente os objetos descartados e recuperam memória. Em vez disso, eles contam com o coletor de lixo do mecanismo para recuperar periodicamente a memória ocupada por objetos não utilizados para dar espaço para novos. A coleta de lixo automática torna a programação mais fácil, mas geralmente requer que a execução do JavaScript pause a cada momento para, em seguida, o coletor fazer seu trabalho. Se o coletor demorar muito para ser executado, todo o navegador pode ficar sem responder. Em jogos HTML5, até mesmo pausas curtas (dezenas de milissegundos) são perturbadoras porque elas são percebidas pelo usuário como falhas técnicas em animação.

No IE10, fizemos inúmeras melhorias em nosso alocador de memória e coletor de lixo. Já discutimos alterações no layout de objeto e geração de código de máquina especializado para aritmética de ponto flutuante, o que resulta em menos alocações de memória. Além disso, o Chakra agora aloca objetos folha (por exemplo, números e cadeias de caracteres) de um espaço de memória separado. Os objetos folha não contêm ponteiros para outros objetos, portanto, eles não requerem tanta atenção durante a coleta de lixo como os objetos comuns. Alocar objetos folha de um espaço separado possui duas vantagens. Primeiro, esse espaço inteiro pode ser ignorado durante a fase de marca, o que reduz a sua duração. Segundo, durante a coleta simultânea, novas alocações de espaço do objeto folha não requerem examinar novamente as páginas afetadas. Como o coletor do Chakra funciona simultaneamente com o thread do script principal, o script em execução pode modificar ou criar novos objetos em páginas que já foram processadas. Para garantir que tais objetos não sejam coletados prematuramente, o Chakra protege as páginas contra gravação antes do início da fase de marca. As páginas que foram gravadas durante a fase de marca devem ser examinadas novamente mais tarde no thread do script principal. Como os objetos folha não requerem tal processamento, as páginas do espaço do objeto folha não precisam ser protegidas contra gravação ou examinadas novamente mais tarde. Isso economiza um tempo precioso no thread do script principal, reduzindo as pausas. Os jogos e as animações em HTML5 se beneficiam significativamente dessa alteração, pois eles frequentemente trabalham duro com números de ponto flutuante e devotam muito da memória alocada a números heap-boxed.

Quando o usuário interage diretamente com um aplicativo Web, é fundamental que o código do aplicativo seja executado o mais rapidamente possível, idealmente sem interrupções para coleta de lixo. Entretanto, quando o usuário sai do navegador, ou apenas muda de guias, é importante reduzir o volume de memória do site ou aplicativo agora inativo. Por isso, no IE9, o Chakra disparava a coleta ao sair do código JavaScript, se memória suficiente tivesse sido alocada. Isso funcionava bem para a maioria dos aplicativos, mas era problemático para aplicativos conduzidos por timers de alta frequência, tais como jogos e animações em HTML5. Para tais aplicativos, as coletas eram disparadas muito frequentemente e resultavam em quadros ignorados e degradação geral da experiência do usuário. Talvez a manifestação mais aparente desse problema fosse o jogo Tankworld, mas outras simulações em HTML5 também exibiram pausas na animação induzida pela frequente coleta de lixo.

No IE10, resolvemos esse problema coordenando as coletas de lixo com o restante do navegador. O Chakra agora atrasa a coleta de lixo no final da execução do script e solicita um solicita um retorno de chamada do navegador após um intervalo de inatividade do script. Se o intervalo passar antes que qualquer script seja executado, o Chakra começa uma coleta, caso contrário, a coleta é adiada novamente. Essa técnica nos permite reduzir o volume de memória quando o navegador (ou uma de suas guias) se torna inativo, enquanto ao mesmo tempo reduz muito a frequência da coleta em aplicativos conduzidos por animação.

Combinadas, essas alterações reduziram o tempo gasto na coleta de lixo no thread principal, por um fator de média de quatro nas simulações em HTML5 medidas. Como uma proporção do tempo de execução de JavaScript, a coleta de lixo foi reduzida de em torno de 27% para cerca de 6%.

Resumo

O IE10 alcança ganhos significativos de desempenho em aplicativos com uso intensivo de JavaScript, particularmente jogos e simulações em HTML5. Esses ganhos foram atingidos através de uma série importante de melhorias no Chakra: de novos recursos fundamentais do compilador JIT a alterações no coletor de lixo.

Ao encerrarmos o desenvolvimento no IE10, celebramos o progresso que fizemos, mas estamos muito cientes de que o desempenho é um desafio eterno. Quase todos os dias surgem novos aplicativos que testam os limites de navegadores modernos e seus mecanismos JavaScript. Sem dúvidas, haverá muito trabalho na próxima versão!

Se você for um desenvolvedor de JavaScript, adoraríamos ouvir a sua opinião. Se os novos recursos e os avanços de desempenho no IE10 ajudaram você a criar experiências totalmente novas para os seus usuários, ou tornar os aplicativos existentes melhores, nos informe. Se você atingiu quaisquer limitações de desempenho no IE, envie-nos uma mensagem. Lemos cuidadosamente todos os comentários sobre este blog, e batalhamos para tornar o IE10 e o Windows 8 a plataforma de aplicativos mais abrangente e segura.

—Andrew Miadowicz, Gerente de Programa, JavaScript