Potencia la eficiencia de la compresión con diccionarios compartidos

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

Si bien gzip es muy eficaz por sí mismo, en los últimos años se han implementado más mejoras en la compresión en la Web. En 2016, se incluyó el algoritmo Brotli en Chrome, que proporcionó mejores proporciones de compresión en general para los recursos aptos. A fines de 2017, todos los navegadores modernos admitían Brotli, y la compatibilidad de los servidores con ese servicio empezó a generalizarse. Recientemente, Chrome envió la compresión ZStandard.

Sin embargo, el trabajo no termina ahí. El equipo de Chrome estuvo trabajando para que los diccionarios compartidos se puedan utilizar en la Web, que ahora están disponibles en una prueba de origen tanto para Brotli como para ZStandard. Los diccionarios compartidos pueden complementar la compresión Brotli y ZStandard para proporcionar proporciones de compresión mucho más altas para los sitios web que envían código actualizado con frecuencia y, en algunos casos, proporcionar relaciones de compresión del 90% o mejores. Esta publicación explica en más detalle cómo funcionan los diccionarios compartidos y cómo puedes registrarte en las pruebas de origen para usarlos con Brotli y ZStandard en tu sitio web.

Explicación de diccionarios compartidos

La compresión es un proceso de encontrar secuencias redundantes en una entrada y usar esa información para crear una salida mucho más pequeña, que se puede revertir más adelante. La compresión funciona bien en la Web porque reduce sustancialmente los tiempos de carga de los recursos. Tanto Brotli como ZStandard pueden aumentar aún más su efectividad mediante 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 hasta cierto punto mediante el uso de un diccionario interno.

Sin embargo, se pueden usar diccionarios personalizados seleccionados por el usuario con Brotli y ZStandard que contienen patrones específicos para recursos particulares. En la práctica, un diccionario personalizado es un archivo externo que puede aplicarse a cualquier entrada. Los diccionarios pueden ser muy específicos para el código de producción de una aplicación o para cualquier tipo de contenido. La aplicación 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 producirán salidas con índices de compresión más altos que aquellos con contenidos genéricos o diferentes.

Este es un ejemplo de la eficacia de un diccionario de compresión personalizado: supongamos que tu sitio web usa el framework de Angular y la versión actual que estás usando es la 1.7.9. Esta versión del framework de Angular tiene alrededor de 172 KiB sin comprimir. Cuando se comprime con la configuración predeterminada de Brotli, su tamaño se acerca a los 53 KiB. Esto produce un índice 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, es posible que tenga la misma proporción de compresión que la versión anterior.

Aquí es donde un diccionario personalizado puede ser ú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 usando la versión 1.7.9 como diccionario, el resultado será un poco más de 4 KiB. Esto representa un índice de compresión de cerca del 98%. Está claro que los diccionarios de compresión pueden tener un gran impacto en el rendimiento de carga, y su eficacia ya se materializó en aplicaciones del mundo real.

Sin embargo, hacer que este flujo funcione en la Web presenta un desafío. El problema es que, si usas un diccionario a fin de comprimir un recurso, necesitas el mismo diccionario para descomprimirlo. Este flujo se intentó en la Web anteriormente, es decir, SDCH, pero fue difícil de implementar de forma segura. Esta última propuesta de compresión de diccionarios compartido aborda esas inquietudes y, al mismo tiempo, proporciona un beneficio significativo para los recursos estáticos y dinámicos.

Cómo anuncia Chrome 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 codificaciones compatibles:

Accept-Encoding: gzip, br, zstd

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

Cuando la compatibilidad con diccionarios compartidos está habilitada 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 aborda 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 comprimió con el diccionario de la codificación aplicable. La forma de lograr esto en la práctica depende de si la solicitud es para un recurso estático o dinámico.

Compresión de diccionario compartido para recursos estáticos

Un recurso de página estático es el que siempre produce la misma respuesta para una URL solicitada. Algunos ejemplos comunes de recursos de páginas estáticas que se pueden comprimir son los archivos JavaScript y CSS. Por lo general, se controlan las versiones de estos recursos para fines de almacenamiento en 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 de creación de huellas digitales 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 a menudo se almacenan en caché durante largos períodos y tienden a actualizarse con cierta frecuencia.

Se puede especificar un diccionario para un recurso estático configurando el encabezado de respuesta Use-As-Dictionary para este. 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 del recurso en el 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 versiones futuras de un recurso que coinciden con el patrón especificado en él. Por lo tanto, 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.

Luego de un tiempo, actualizarás el CSS de tu sitio web y enviarás una nueva versión 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 contendrá 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 en su extremo para garantizar que se use el diccionario coincidente. A continuación, se enviará el recurso comprimido con ese diccionario y se utilizará el diccionario disponible en la caché del navegador del usuario para descomprimirlo.

Si envías código nuevo con frecuencia para 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 diccionario compartido para recursos dinámicos

Los recursos dinámicos también pueden beneficiarse de la compresión de diccionarios compartidos. 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 como noticias de último momento. Los documentos HTML suelen ser recursos dinámicos. En estos casos, el diccionario puede contener la mayor parte de la estructura HTML común del sitio y el código de plantilla que conduce a páginas comprimidas donde 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 su uso posterior. Cargar un diccionario con anticipación implica que la aplicación de la compresión de diccionarios compartida a los recursos dinámicos es especulativa. En esos casos, se espera que el sitio web reciba tráfico suficiente para que el costo del diccionario se pueda amortizar en una gran cantidad de navegaciones. Si decides probarlo, el primer paso es especificar la ubicación del diccionario mediante un elemento <link> en el HTML de tu página:

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

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

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

Desde aquí, el flujo es en gran medida el mismo que el de los recursos estáticos. El navegador verá que el diccionario se aplica a los recursos coincidentes y adjuntará un encabezado Available-Dictionary a la solicitud con un hash del contenido del diccionario, de manera similar al flujo de recursos estáticos que se explicó anteriormente.

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

Si conoces los agrupadores, es posible que conozcas los diversos complementos que pueden comprimir recursos en el tiempo de compilación y, luego, entregar esos recursos comprimidos. Por ejemplo, Apache te permite usar directivas para entregar esos recursos precomprimidos en el momento de la solicitud.

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

Ten en cuenta que los diccionarios disponibles para cualquier 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 según corresponda. Apunta a un equilibrio y genera recursos que beneficien a la cantidad máxima de usuarios recurrentes de la mejor manera posible. En la actualidad, los proveedores de CDN están experimentando con la compresión de diccionarios compartidos. Aún no hay implementaciones disponibles para uso público, pero esperamos que eso cambie.

Pruébala.

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 con frecuencia envían código de producción actualizado y reciben una cantidad considerable de tráfico de los visitantes recurrentes. Si quieres realizar una toma de compresión de diccionario compartido, tienes dos opciones:

  1. Si solo deseas modificar la compresión de diccionarios compartida por tu cuenta para tener una idea de cómo funciona, puedes habilitar la función experimental Compression Dictionary Transport en la página chrome://flags.
  2. Si quieres probar esto en tu sitio web de producción y ver cómo la compresión de diccionarios compartido podría beneficiar a 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

Estamos muy entusiasmados con este gran avance en la tecnología de compresión en la Web y con la velocidad con la que podría acelerar las aplicaciones existentes que la gente utiliza todos los días. Te recomendamos que lo pruebes y, lo que es más importante, queremos conocer tu opinión si lo haces. Si encuentras un error, infórmalo en crbug.com. Para obtener recursos y herramientas adicionales, visita use-as-dictionary.com. Por último, si te interesa obtener más información sobre cómo funciona todo, la explicación es un buen paso a seguir.