Aumente a eficiência da compactação com dicionários compartilhados

A compactação de dados é uma técnica de otimização de desempenho testada pelo tempo que reduz o tamanho dos recursos de página qualificados. Por algum tempo, era prática comum usar gzip em servidores da Web para compactar recursos de página comuns baseados em texto, como arquivos HTML, CSS e JavaScript, e enviá-los ao cliente, onde eles poderiam ser descompactados. O resultado é um tempo de carregamento mais rápido dos recursos sem afetar o comportamento pretendido de uma página.

Embora o gzip seja altamente eficaz, outras melhorias na compressão na Web foram realizadas nos últimos anos. Em 2016, o algoritmo Brotli foi lançado no Chrome, oferecendo taxas de compactação melhores para recursos qualificados. No final de 2017, todos os navegadores modernos ofereciam suporte ao Brotli, e o suporte do servidor a ele começou a se tornar mais difundido. Mais recentemente, o Chrome lançou a compactação ZStandard.

Mas o trabalho não para por aí. A equipe do Chrome está trabalhando para tornar os dicionários compartilhados utilizáveis na Web. Eles estão disponíveis em um teste de origem para Brotli e ZStandard. Os dicionários compartilhados podem complementar a compactação Brotli e ZStandard para oferecer taxas de compactação substancialmente mais altas para sites que enviam códigos atualizados com frequência e, em alguns casos, podem oferecer taxas de compactação de 90% ou mais. Esta postagem explica em detalhes como os dicionários compartilhados funcionam e como você pode se inscrever nos testes de origem para usá-los com Brotli e ZStandard no seu site.

Dicionários compartilhados explicados

A compressão é um processo de encontrar sequências redundantes em uma entrada e usar essas informações para criar uma saída muito menor, que pode ser revertida mais tarde. A compactação funciona bem na Web porque reduz significativamente os tempos de carregamento de recursos. O Brotli e o ZStandard podem aumentar ainda mais a eficácia usando um dicionário de compactação, que é uma coleção de padrões adicionais que esses algoritmos podem usar durante a compactação. Na verdade, a alta eficiência do Brotli é alcançada em certa medida usando um dicionário interno.

No entanto, é possível usar dicionários personalizados criados pelo usuário com o Brotli e o ZStandard que contêm padrões específicos para recursos específicos. Na prática, um dicionário personalizado é um arquivo externo que pode ser aplicado a qualquer entrada. Os dicionários podem ser muito específicos para o código de produção de um aplicativo ou qualquer conteúdo. A aplicabilidade de um determinado dicionário à entrada pode ter um grande impacto na eficiência geral da compactação. Dicionários muito semelhantes ao conteúdo de uma entrada vão gerar saídas com taxas de compressão mais altas do que dicionários com conteúdo genérico ou diferente.

Confira um exemplo de como um dicionário de compactação personalizado pode ser eficaz: digamos que seu site use o framework Angular e que a versão atual que você está usando seja a 1.7.9. Esta versão do framework Angular tem cerca de 172 KiB sem compactação. Quando compactado com as configurações padrão do Brotli, o tamanho fica em cerca de 53 KiB. Isso gera uma taxa de compactação de quase 70%. No entanto, digamos que você decida fazer upgrade para o Angular 1.8.3 mais tarde. Como essa versão do Angular tem aproximadamente o mesmo tamanho da versão 1.7.9, você pode esperar praticamente a mesma taxa de compactação da versão anterior.

É aí que um dicionário personalizado pode ser útil usando um processo conhecido como compactação delta , que é quando um dicionário de uma versão anterior de um recurso pode ser usado para compactar uma versão mais recente. Usando o exemplo anterior, se você comprimir a versão 1.8.3 do Angular usando a versão 1.7.9 como um dicionário, a saída será de pouco mais de 4 KiB. Isso representa uma taxa de compactação de quase 98%. Os dicionários de compactação podem ter um grande impacto no desempenho de carregamento, e a eficácia deles já foi comprovada em aplicativos reais.

No entanto, há um desafio em fazer esse fluxo funcionar na Web. O problema é que, se você usar um dicionário para compactar um recurso, vai precisar dele para descompactar. Esse fluxo já foi tentado na Web, especificamente o SDCH, mas foi difícil de implementar com segurança. Essa proposta mais recente de compactação de dicionário compartilhado resolve essas preocupações e oferece um benefício substancial para recursos estáticos e dinâmicos.

Como o Chrome anuncia suporte a dicionários compartilhados

Todos os navegadores anunciam os algoritmos de compactação compatíveis no cabeçalho de solicitação Accept-Encoding. O conteúdo do cabeçalho é uma lista separada por vírgulas das codificações compatíveis:

Accept-Encoding: gzip, br, zstd

Esse cabeçalho Accept-Encoding específico indica que o navegador que solicita o recurso oferece suporte aos algoritmos de compactação gzip, Brotli e ZStandard. Um servidor da Web que responde à solicitação pode decidir qual algoritmo usar ao responder à solicitação.

Quando o suporte a dicionários compartilhados está ativado e um dicionário relevante está disponível para um recurso, outros tokens são adicionados ao cabeçalho Accept-Encoding. Esses tokens são br-d para Brotli e zstd-d para Zstandard. O Chrome também vai incluir o hash de um dicionário disponível, que será abordado a seguir.

Accept-Encoding: gzip, br, zstd, br-d, zstd-d
Available-Dictionary: :pZGm1Av0IEBKARczz7exkNYsZb8LzaMrV7J32a2fFG4=:

Se um servidor da Web estiver configurado para reconhecer esse token e o dicionário, ele poderá responder a essa solicitação com um recurso compactado usando o dicionário para a codificação aplicável. A forma de fazer isso na prática depende se a solicitação é para um recurso estático ou dinâmico.

Compactação de dicionário compartilhado para recursos estáticos

Um recurso de página estático sempre produz a mesma resposta para um URL solicitado. Exemplos comuns de recursos de página estáticos com compactação são arquivos JavaScript e CSS. Esses recursos geralmente são versionados para fins de armazenamento em cache, por exemplo, com um hash do conteúdo do arquivo no nome do arquivo (por exemplo, styles.abcd1234.css) ou algum outro método de impressão digital do recurso. Esses tipos de recursos são ótimos candidatos para a compressão delta que os dicionários compartilhados oferecem, já que os recursos estáticos geralmente são armazenados em cache por longos períodos e tendem a ser atualizados com alguma frequência.

É possível especificar um dicionário para um recurso estático definindo o cabeçalho de resposta Use-As-Dictionary. O cabeçalho usa um dos poucos pares de chave-valor, mas o único obrigatório é match, que aceita a sintaxe URLPattern especificando o caminho do recurso em que o dicionário deve ser usado:

Use-As-Dictionary: match="/dist/styles.*.css"

O cabeçalho Use-As-Dictionary é um mecanismo que se aplica a versões futuras de um recurso que correspondem ao padrão especificado nele. Digamos que seu site envie todos os estilos em um único arquivo CSS. Para simplificar, digamos que a primeira versão desse recurso esteja localizada em /dist/styles.v1.css e seja enviada com um cabeçalho de resposta Use-As-Dictionary contendo um valor match de /dist/styles.*.css.

Depois de algum tempo, você atualiza o CSS do site e envia uma nova versão dele localizada em /dist/styles.v2.css. Como o valor match usado no cabeçalho de resposta Use-As-Dictionary da versão anterior se aplica a essa solicitação, o navegador vai enviar um cabeçalho Available-Dictionary contendo um hash do dicionário codificado como uma sequência de bytes de campo estruturado:

Accept-Encoding: gzip, br, zstd, br-d, zstd-d
Available-Dictionary: :pZGm1Av0IEBKARczz7exkNYsZb8LzaMrV7J32a2fFG4=:

Nesse ponto, cabe ao servidor configurar a compactação para garantir que o dicionário correspondente seja usado. O recurso comprimido com esse dicionário será enviado, e o dicionário disponível no cache do navegador do usuário será usado para descompactá-lo.

Se você envia um novo código com frequência para seu site, a compactação delta pode ser muito útil. No entanto, o processo é flexível. Se o navegador não determinar que um dicionário está disponível no cache do navegador do usuário, ele não vai especificar os tokens br-d ou zstd-d adicionais no cabeçalho Accept-Encoding. Nesse caso, o fluxo de compactação padrão é aplicado.

Compactação de dicionário compartilhado para recursos dinâmicos

Os recursos dinâmicos também podem se beneficiar da compactação de dicionário compartilhado. Os recursos dinâmicos são aqueles que mudam com base em um contexto, como um site de notícias em que a página principal é atualizada com frequência à medida que as notícias são publicadas, por exemplo. Os documentos HTML geralmente são recursos dinâmicos. Nesses casos, o dicionário pode conter a maior parte da estrutura HTML comum do site e o código do modelo, levando a páginas compactadas em que apenas as partes exclusivas de cada página são enviadas.

Devido à natureza dos recursos gerados dinamicamente, um dicionário precisa ser carregado no cliente para uso posterior. Carregar um dicionário com antecedência significa que a aplicação da compactação de dicionário compartilhado a recursos dinâmicos é especulativa. Nesses casos, a esperança é que o site receba tráfego suficiente para que o custo do dicionário possa ser amortizado em um grande número de navegações. Se você decidir tentar, a primeira etapa é especificar o local do dicionário usando um elemento <link> no HTML da página:

<link rel="dictionary" href="/dictionary.dat">

Quando o Chrome encontra esse elemento <link>, ele pode buscar o dicionário quando a página está inativa e com baixa prioridade para evitar a disputa de largura de banda. A resposta do dicionário precisa especificar um cabeçalho Use-As-Dictionary e o caminho do recurso dinâmico a que se aplica:

Use-As-Dictionary: match="/product/*"

A partir daqui, o fluxo é basicamente o mesmo dos recursos estáticos. O navegador vai perceber que o dicionário se aplica aos recursos correspondentes e vai anexar um cabeçalho Available-Dictionary à solicitação com um hash do conteúdo do dicionário, semelhante ao fluxo de recursos estáticos explicado anteriormente.

Compactar recursos estáticos no momento da criação

Se você já conhece os bundlers, talvez conheça vários plug-ins para eles que podem compactar recursos no momento da criação e, em seguida, oferecer esses recursos compactados. Por exemplo, o Apache permite usar diretivas para servir esses recursos pré-compactados no momento da solicitação.

A maioria dos bundlers baseados em Node.js que oferecem suporte à compactação usa a biblioteca Zlib integrada do Node. O Zlib oferece suporte ao Brotli e aos bundlers que o usam, geralmente oferecendo uma interface para transmitir opções diretamente para o Zlib, que oferece suporte à compactação com dicionário. Confira alguns bundlers que oferecem suporte a dicionários:

Os dicionários disponíveis para qualquer versão de um recurso podem usar uma das versões anteriores dele. Isso significa que você precisa analisar o tráfego de usuários e planejar de acordo com isso. Procure um equilíbrio e gere recursos que beneficiem o maior número possível de usuários recorrentes. Os provedores de CDN estão testando a compactação de dicionário compartilhado. Ainda não há implementações disponíveis para uso público, mas esperamos que isso mude.

Faça um teste

A integração da compactação de dicionário compartilhado com os recursos de compactação atuais do navegador pode melhorar significativamente o desempenho de carregamento de sites que enviam com frequência o código de produção atualizado e recebem tráfego significativo de visitantes recorrentes. Se você quiser testar a compactação de dicionário compartilhado, há duas opções:

  1. Se você quiser mexer na compactação de dicionário compartilhado por conta própria para ter uma ideia de como ela funciona, ative o recurso experimental Transporte de dicionário por compactação na página chrome://flags.
  2. Se você quiser testar isso no seu site de produção e conferir como a compactação de dicionário compartilhado pode beneficiar usuários reais, faça o teste de origem para receber um token e leia como os testes de origem funcionam.

Conclusão

Estamos muito animados com esse grande avanço na tecnologia de compactação na Web e com o quanto ela pode acelerar os aplicativos que as pessoas usam todos os dias. Recomendamos que você teste o recurso e, mais importante, queremos saber sua opinião. Se você encontrar um bug, envie um relatório em crbug.com. Para mais recursos e ferramentas, confira use-as-dictionary.com. Por fim, se você quiser saber mais sobre como tudo funciona, a explicação é uma boa próxima etapa.