Início > Desenvolvimento > Por uma web mais rápida: compressão radical de JavaScript – parte 1

Por uma web mais rápida: compressão radical de JavaScript – parte 1

Post inspirado pela palestra do Sérgio Lopes sobre otimização de sites, que foi apresentada na #QConSP. Gostaria de ter demonstrado esse caso no momento, como sugestão para todos da área, mas eu sou tímido. :P

Em uma empresa onde trabalhei no ano passado, tive que desenvolver um código JS que usa uma biblioteca adicional, XRegExp, para named capture em expressões regulares. Juntando essa biblioteca e meu código, o tamanho total ficava em cerca de 36 KB – no mínimo, já que o código era modificado por variáveis que o cliente da plataforma poderia configurar. A projeção de hits no servidor solicitando um JS era da ordem de milhões por mês, dado que é uma plataforma de advertising. Considerando que cada site deve incluir esse JS e um cliente tem vários sites: (+36 KB * clientes * sites * milhões): não resulta em um número muito bom.

* Detalhe importante: o JS não é totalmente estático. Caso o cliente da plataforma modificasse uma regra, ela teria que ser imediatamente aplicada ao JS, logo, não poderia ficar muito tempo em cache do usuário. O jeito encontrado foi servir sem cache e com o máximo possível de compressão.

Há 3 anos eu conheci o Packer, um compactador de JavaScript que é bastante utilizado por frameworks JS e módulos de pipelining em frameworks web – detalhe que na época eu o conheci através de um código malicioso e como eu queria saber o que o código estava fazendo, então tive que estudar a forma como ele era compactado e o que significava aquela function(p,a,c,k,e,r) no começo do arquivo. Ele tem muito valor e utilidade em ambientes onde você precisa fazer entrega de conteúdo com eficiência.

Ele não é tão simples como outros códigos que eliminam comentários, espaços, tabs, pontos-e-vírgulas duplicados, quebras de linha, deixando a estrutura básica da linguagem para que o código seja funcional. Ele faz bem mais que isso. Explico agora:

- O Packer parseia seu código JS, fazendo tudo o que eu descrevi ali em cima e também separa cada palavra-chave ou valor, montando um array* de strings.
– Cada palavra armazenada no array possui um índice numérico e esse índice é usado como referência no código que o Packer gera.
– O Packer cria um wrapper, que é a função anônima (p,a,c,k,e,r), responsável por descompactar o código gerado, buscar as referências no array e remontar o código, substituindo as referências pelos valores respectivos, terminando com um eval() para interpretar o código remontado.
– Como as referências são únicas e as strings usadas para descrevê-las são pequenas, muitas vezes usando números de base 62 – dependendo da configuração, o código gerado é bastante econômico e sucinto.

* Este array de strings é na verdade uma string só, com seus valores delimitados por pipes

Vou dar um exemplo bem simples de como o packer vai montar um o código compactado:

function stack_messages(strings) {
var i = 0;
for (i = 0; i < strings.length(); i++) {
var message = “Annoying message #” + i + “: ” + strings[i]“;
alert(message);
}
}

Esse é o “array” de palavras úteis que o Packer gerou após fazer o parsing:

‘|i|var|function|stack_messages|for|length|Annoying|message|alert||’

E gerar o seguinte código:

‘3 4(a){2 1=0;5(1=0;1<a.6();1++){2 b=”7 8 #”+1+”: “+a[1]“;9(b)}}’

Não parece, mas esta é a função stack_messages. Ignorando chaves, pontos-e-vírgulas, parênteses e outros símbolos que obviamente não podem ser reduzidos, é possível entender que que: a referência 3 corresponde ao índice 3 no “array”, que quer dizer “function”. A referência de número 4 é “stack_messages”. Isso é só pra começar.

Note algo interessante: palavras que foram utilizadas várias vezes no código, como var, message e i só aparecem uma vez no array. Elas possuem as referências 2, 8 e 1, respectivamente.

‘3 4(a){2 1=0;5(1=0;1<a.6();1++){2 b=”7 8 #”+1+”: “+a[1]“;9(b)}}’

Trocando de 1 a 7 caracteres por 1 só. Inteligente demais, não?

* Não entendi o porque da palavra message ter duas referências, 8 e b, mas assim que descobrir, atualizo o post.

Não vou me aprofundar nos detalhes do algoritmo de descompressão do Packer, mas por cima é possível notar que:

- È um algoritmo iterativo: o único código que roda imediatamente é ele e o código desenvolvido por você não vai rodar até que ele tenha iterado o array de palavras úteis e transformado o código traduzido em código válido para ser interpretado com eval(). A velocidade com a qual ele roda depende bastante da velocidade da máquina e do browser de seu cliente.
– Não é bom para códigos pequenos. O overhead causado pela função de descompressão não compensa o trabalho da compressão.
– Concatene todos os arquivos JS que sua página necessita através de um pipeliner antes de comprimir com o Packer. Isso faz com que o tamanho do array de palavras úteis seja menor e centralizado. No caso de frameworks e bibliotecas de terceiros, escolha as versões non-minified e non-packed. O ideal é não judiar do forçar o browser do seu cliente a executar o descompactador do Packer mais de uma vez, seja em cada arquivo, seja em “compressão em cima de compressão”, como um código packed passando novamente pelo Packer.

Nesse ponto, meu código (XRegExp + minha aplicação) já está “minified” com o Packer, mas ainda está pesado para o cenário de milhões de requests. Vou explicar na parte 2 como o servidor foi otimizado para servir arquivos com compressão.

About these ads
  1. 12/09/2011 às 18:00

    Excelente post! Gosto bastante do packer também. Há diversos outros compressores, e dizem que o UglifyJS é o mais eficiente hoje em dia (eu uso o YUI ainda nos projetos)

  1. 12/09/2011 às 15:00

Deixe um comentário

Preencha os seus dados abaixo ou clique em um ícone para log in:

Logotipo do WordPress.com

Você está comentando utilizando sua conta WordPress.com. Sair / Alterar )

Imagem do Twitter

Você está comentando utilizando sua conta Twitter. Sair / Alterar )

Foto do Facebook

Você está comentando utilizando sua conta Facebook. Sair / Alterar )

Foto do Google+

Você está comentando utilizando sua conta Google+. Sair / Alterar )

Conectando a %s

Seguir

Obtenha todo post novo entregue na sua caixa de entrada.

Junte-se a 424 outros seguidores

%d blogueiros gostam disto: