Potencia la eficiencia de la compresión con diccionarios compartidos

La compresión de datos es una técnica de optimización del rendimiento comprobada que reduce el tamaño de los recursos de página aptos. Durante algún tiempo, la práctica habitual era usar gzip principalmente en servidores web para comprimir recursos de página comunes basados en texto, como archivos HTML, CSS y JavaScript, y enviarlos al cliente, donde se podían descomprimir. El resultado son tiempos de carga más rápidos para los recursos sin afectar el comportamiento deseado de una página.

Si bien gzip es muy eficaz por sí solo, en los últimos años se han realizado más mejoras en la compresión en la Web. En 2016, el algoritmo Brotli se envió en Chrome, lo que proporcionó mejores relaciones de compresión generales para los recursos aptos. A fines de 2017, todos los navegadores modernos admitían Brotli, y la compatibilidad del servidor con este comenzó a generalizarse. Más recientemente, Chrome lanzó la compresión ZStandard.

Pero el trabajo no termina ahí. El equipo de Chrome trabajó para que los diccionarios compartidos se puedan usar en la Web, que ahora están disponibles en una prueba de origen para Brotli y ZStandard. Los diccionarios compartidos pueden complementar la compresión de Brotli y ZStandard para ofrecer relaciones de compresión sustancialmente más altas para los sitios web que envían código actualizado con frecuencia y, en algunos casos, pueden ofrecer relaciones de compresión del 90% o mejores. En esta publicación, se explica con más detalle cómo funcionan los diccionarios compartidos y cómo puedes registrarte para las pruebas de origen para usarlos en Brotli y ZStandard en tu sitio web.

Explicación de los diccionarios compartidos

La compresión es un proceso de búsqueda de secuencias redundantes en una entrada y el uso de esa información para crear un resultado mucho más pequeño, que se puede revertir más adelante. La compresión funciona bien en la Web porque reduce significativamente los tiempos de carga de los recursos. Tanto Brotli como ZStandard pueden aumentar aún más su eficacia con un diccionario de compresión, que es una colección de patrones adicionales que estos algoritmos pueden usar durante la compresión. De hecho, la alta eficiencia de Brotli se logra en cierta medida con el uso de un diccionario interno.

Sin embargo, se pueden usar diccionarios personalizados seleccionados por el usuario con Brotli y ZStandard que contengan patrones específicos de recursos particulares. En la práctica, un diccionario personalizado es un archivo externo que se puede aplicar a cualquier entrada. Los diccionarios pueden ser muy específicos para el código de producción de una aplicación o para cualquier contenido. El grado de aplicabilidad de un diccionario determinado a su entrada puede tener un gran impacto en la eficiencia general de la compresión. Los diccionarios que son muy similares al contenido de una entrada generarán resultados con relaciones de compresión más altas que los diccionarios con contenido genérico o disímil.

A continuación, se muestra un ejemplo de la eficacia que puede tener un diccionario de compresión personalizado: supongamos que tu sitio web usa el framework de Angular y que la versión actual que usas es la 1.7.9. Esta versión del framework de Angular ocupa alrededor de 172 KiB sin comprimir. Cuando se comprime con la configuración predeterminada de Brotli, su tamaño se reduce a alrededor de 53 KiB. Esto genera una relación de compresión de casi el 70%. Sin embargo, supongamos que decides actualizar a Angular 1.8.3 más adelante. Dado que esta versión de Angular tiene aproximadamente el mismo tamaño que la versión 1.7.9, puedes esperar prácticamente la misma relación de compresión que la versión anterior.

Aquí es donde un diccionario personalizado puede resultar útil mediante un proceso conocido como compresión delta , que es cuando se puede usar un diccionario de una versión anterior de un recurso para comprimir una versión posterior. En el ejemplo anterior, si comprimiste la versión 1.8.3 de Angular con la versión 1.7.9 como diccionario, el resultado sería de poco más de 4 KiB. Esto representa una relación de compresión de casi el 98%. Claramente, los diccionarios de compresión pueden tener un gran impacto en el rendimiento de carga, y su eficacia ya se demostró en aplicaciones del mundo real.

Sin embargo, hay un desafío para que este flujo funcione en la Web. El problema es que, si usas un diccionario para comprimir un recurso, necesitas ese mismo diccionario para descomprimirlo. Este flujo ya se intentó en la Web, en particular con SDCH, pero fue difícil implementarlo de forma segura. Esta propuesta más reciente para la compresión de diccionarios compartidos aborda esas inquietudes y, al mismo tiempo, proporciona un beneficio sustancial para los recursos estáticos y dinámicos.

Cómo Chrome anuncia la compatibilidad con diccionarios compartidos

Todos los navegadores anuncian los algoritmos de compresión que admiten a través del encabezado de solicitud Accept-Encoding. El contenido del encabezado es una lista separada por comas de las codificaciones admitidas:

Accept-Encoding: gzip, br, zstd

Este encabezado Accept-Encoding en particular indica que el navegador que solicita el recurso admite los algoritmos de compresión gzip, Brotli y ZStandard. Un servidor web que responde a la solicitud puede decidir qué algoritmo usar cuando responde a la solicitud.

Cuando se habilita la compatibilidad con diccionarios compartidos y hay un diccionario relevante disponible para un recurso, se agregan tokens adicionales al encabezado Accept-Encoding. Estos tokens son br-d para Brotli y zstd-d para Zstandard. Chrome también incluirá el hash de un diccionario disponible, que se explica a continuación.

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

Si un servidor web está configurado para reconocer este token y reconoce el diccionario, puede responder a esa solicitud con un recurso que se comprime con el diccionario para la codificación aplicable. La forma en que se logra esto en la práctica depende de si la solicitud es para un recurso estático o dinámico.

Compresión de diccionarios compartidos para recursos estáticos

Un recurso de página estático es aquel que siempre produce la misma respuesta para una URL solicitada. Algunos ejemplos comunes de recursos de página estáticos comprimibles son los archivos JavaScript y CSS. Por lo general, estos recursos se crean con control de versiones para la caché de alguna manera, a veces con un hash del contenido del archivo en el nombre del archivo (por ejemplo, styles.abcd1234.css) o algún otro método para crear una huella digital del recurso. Estos tipos de recursos son un excelente candidato para la compresión delta que proporcionan los diccionarios compartidos, ya que los recursos estáticos suelen almacenarse en caché durante largos períodos y tienden a actualizarse con cierta frecuencia.

Para especificar un diccionario para un recurso estático, configura el encabezado de respuesta Use-As-Dictionary. El encabezado toma uno de los pocos pares clave-valor, pero el único obligatorio es match, que acepta la sintaxis URLPattern que especifica la ruta de acceso al recurso en la que se debe usar el diccionario:

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

Piensa en el encabezado Use-As-Dictionary como un mecanismo que se aplica a las versiones futuras de un recurso que coincidan con el patrón especificado en él. Supongamos que tu sitio web envía todos sus estilos en un solo archivo CSS. Para simplificar, supongamos que la primera versión de ese recurso se encuentra en /dist/styles.v1.css y se envía con un encabezado de respuesta Use-As-Dictionary que contiene un valor match de /dist/styles.*.css.

Después de un tiempo, actualizas el CSS de tu sitio web y envías una versión nueva ubicada en /dist/styles.v2.css. Debido a que el valor match que se usa en el encabezado de respuesta Use-As-Dictionary de la versión anterior se aplica a esta solicitud, el navegador enviará un encabezado Available-Dictionary que contiene un hash del diccionario codificado como una secuencia de bytes de campo estructurado:

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

En este punto, depende del servidor configurar la compresión para garantizar que se use el diccionario coincidente. Luego, se enviará el recurso comprimido con ese diccionario y se usará el diccionario disponible en la caché del navegador del usuario para descomprimirlo.

Si envías código nuevo con frecuencia a tu sitio web, la compresión delta puede ser muy útil. Sin embargo, el proceso es flexible. Si el navegador no determina que hay un diccionario disponible en la caché del navegador del usuario, no especificará los tokens br-d o zstd-d adicionales en el encabezado Accept-Encoding. En ese caso, se aplica el flujo de compresión estándar.

Compresión de diccionarios compartidos para recursos dinámicos

Los recursos dinámicos también pueden beneficiarse de la compresión del diccionario compartido. Los recursos dinámicos son aquellos que cambian según un contexto, como un sitio web de noticias en el que la página principal se actualiza con frecuencia a medida que se publican noticias, por ejemplo. Los documentos HTML suelen ser recursos dinámicos. En esos casos, el diccionario puede contener la mayor parte de la estructura HTML común del sitio y el código de plantilla, lo que genera páginas comprimidas en las que solo se envían las partes únicas de cada página.

Debido a la naturaleza de los recursos generados de forma dinámica, se debe cargar un diccionario en el cliente para usarlo más adelante. Cargar un diccionario con anticipación significa que aplicar la compresión de diccionarios compartidos a los recursos dinámicos es una especulación. En esos casos, la esperanza es que tu sitio web reciba suficiente tráfico para que el costo del diccionario se amortice en una gran cantidad de navegaciones. Si decides probarlo, el primer paso es especificar la ubicación del diccionario a través de un elemento <link> en el código HTML de tu página:

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

Cuando Chrome encuentra este elemento <link>, puede recuperar el diccionario una vez que la página esté inactiva y con prioridad baja para evitar la contención de ancho de banda. La respuesta del diccionario debe especificar un encabezado Use-As-Dictionary y a qué ruta de recursos dinámicos se aplica:

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

A partir de este punto, el flujo es en gran medida el mismo que para los recursos estáticos. El navegador verá que el diccionario en sí se aplica a los recursos que coinciden y adjuntará un encabezado Available-Dictionary a la solicitud con un hash del contenido del diccionario, nuevamente, similar al flujo de recursos estáticos que se explicó antes.

Comprime recursos estáticos en el tiempo de compilación

Si conoces los empaquetadores, es posible que también conozcas varios complementos para ellos que pueden comprimir recursos en el tiempo de compilación y, posteriormente, entregarlos. Por ejemplo, Apache te permite usar directivas para entregar esos recursos comprimidos previamente en el momento de la solicitud.

La mayoría de los empaquetadores basados en Node.js que admiten la compresión usan la biblioteca Zlib integrada de Node. Zlib ofrece compatibilidad con Brotli y los empaquetadores que lo usan suelen ofrecer una interfaz para pasar opciones directamente a Zlib, que admite la compresión con diccionario. Estos son algunos empaquetadores que admiten el uso de diccionarios:

Ten en cuenta que los diccionarios disponibles para una versión determinada de un recurso pueden usar una de las versiones anteriores de un recurso. Esto significa que deberás analizar el tráfico de los usuarios y planificar en consecuencia. Intenta lograr un equilibrio y genera recursos que beneficien a la mayor cantidad posible de usuarios recurrentes. Actualmente, los proveedores de CDN están experimentando con la compresión de diccionarios compartidos. Aún no hay implementaciones disponibles para el uso público, pero esperamos que eso cambie.

¡Pruébalo!

La integración de la compresión de diccionarios compartidos con las capacidades de compresión existentes del navegador tiene el potencial de mejorar sustancialmente el rendimiento de carga de los sitios web que envían con frecuencia código de producción actualizado y reciben tráfico significativo de visitantes recurrentes. Si te interesa probar la compresión de diccionarios compartidos, tienes dos opciones:

  1. Si solo quieres probar la compresión de diccionarios compartidos por tu cuenta para ver cómo funciona, puedes habilitar la función experimental Compression dictionary transport en la página chrome://flags.
  2. Si te interesa probar esto en tu sitio web de producción y ver cómo la compresión de diccionarios compartidos podría beneficiar a los usuarios reales, regístrate en la prueba de origen para obtener un token y lee sobre cómo funcionan las pruebas de origen.

Conclusión

Nos entusiasma este gran avance en la tecnología de compresión en la Web y lo mucho más rápidas que podrían ser las aplicaciones existentes que las personas usan todos los días. Te recomendamos que la pruebes y, lo más importante, nos encantaría conocer tu opinión si lo haces. Si encuentras un error, envíalo a crbug.com. Para obtener recursos y herramientas adicionales, consulta use-as-dictionary.com. Por último, si te interesa profundizar en cómo funciona todo, la explicación es un buen paso a seguir.