Text Compression for Web Developers

HTML5 Rocks

Introduction

Les contenus textuels sur le web recouvrent principalement HTML, JavaScript et CSS. Ces formats ne se prêtent pas à une compression à perte. On est donc limités aux compressions sans perte, qui n'offrent pas les phénoménales réductions de taille que certains codecs à perte pour les images ou la vidéo permettent. Alors comment réduire la taille de son appli web sans péter les plombs ? Cet article vous guide pas à pas et vous aide à rester sains d'esprit.

Text Compression for Developers

TL;DR : Checklist de Compression de Données Textuelles

  1. Pensez mobile d’abord pour vos expériences utilisateurs.
    1. Que pèsent les ressources de votre page ? Pouvez-vous réduire ça ?
    2. Combien de temps vos utilisateurs vont-ils attendre que votre page soit chargée sur une connexion moyenne ?
  2. Minifiez tous les contenus qui peuvent l’être.
    1. Les minifieurs CSS et JavaScript sont puissants, simples d’emploi, et intégrables dans vos chaînes de build.
    2. Passez vos contenus à travers leurs préprocesseurs le plus en amont possible.
  3. Utilisez la compression GZIP pour toutes les ressources textuelles.
    1. Assurez-vous que votre serveur a la compression GZIP activée.
    2. Compressez davantage et en amont (mais GZIP) grâce à Zopfli ou 7zip.
  4. Si vous avez encore besoin de réduire, utilisez des codecs avancés tels que BZIP2 et LZMA.

Être petit : un gros enjeu

Le marché mobile est d’ores et déjà immense, et avec l’amélioration de la connectivité dans le monde entier, les sociétés technologiques livrent un nouveau combat pour fournir du contenu et des données aux 5 prochains milliards d’humains qui s’apprêtent à être en ligne. « New Digital Age » d’Eric Schmidt définit bien cette problématique :

Il y a déjà plus de 650 millions d’utilisateurs de téléphones mobiles en Afrique, et près de 3 milliards à travers l’Asie. La majorité de ces personnes utilisent des téléphones basiques (appels vocaux et SMS seulement) parce que les services d’accès aux données dans leurs pays affichent des tarifs prohibitifs, de sorte que même ceux qui peuvent s’offrir des téléphones accédant au web, des smartphones, ne peuvent pas ensuite s’en servir sans se ruiner. Tout cela va changer, et quand ça arrivera, la révolution des smartphones améliorera profondément la vie de ces populations.

Tout ceci n’a rien de nouveau : si on en croit un rapport de Cisco, le nombre des utilisateurs 100% mobile a déjà tellement grandi qu’il avoisinera 788 millions en 2015. Évidemment, pour des grosses sociétés comme Cisco, c’est un enjeu majeur, vu qu’en 2012 597 péta-octets par mois circulaient sur leur matériel.

Vitesses de connexion mobile et performances des appareils

Le monde a connu de belles améliorations de vitesse des réseaux ces dernières années. Il importe toutefois de bien remarquer à quel point ces améliorations sont disparates en termes de volumes et de géographie. Google Analytics a un excellent graphique illustrant les tendances de connectivité à travers le monde. On y voit clairement que cette notion d’amélioration est loin d’être homogène ; par exemple, la Chine a connue une augmentation de 8% du temps moyen de chargement de page sur le desktop (ça a donc ralenti), tandis que leurs temps sur le mobile ont chuté de 33% (ça va plus vite), même si ça reste au-dessus de 3,5 secondes de chargement ; ce qui est encore beaucoup trop long, vu que 42% de leur population de 1,53 milliards est en ligne.

Et en vrai, la perception utilisateur du temps de chargement et de la réactivité est la métrique la plus importante à optimiser. Comme nous l’avons vu, la latence est le nouveau goulot d’étranglement de la performance web et il est manifeste que l’amélioration des réseaux est surtout en butte à l’infrastructure matérielle dans la plupart des pays. Construire de nouvelles tours cellulaires et tirer des câbles de fibre optique constituent un cauchemar d’ingéniérie civile, et représentent un énorme coût d’investissement. Les problèmes sont si complexes que certaines sociétés développent carrément des satellites à plusieurs milliards de dollars pour tenter une approche différente. Pour résumer : les réseaux mobiles vont continuer à péniblement monter en vitesse, lentement, de façon inégale, et ça va coûter un bras. Si vous attendez que le web mobile devienne tout à coup plus rapide, autant vous trouver un fauteuil confortable parce que ça va prendre un bon bout de temps.

Donnez plus à vos utilisateurs en leur envoyant moins

En tant que développeur web, vous avez la plus grande part de contrôle sur l’optimisation de votre site pour offrir à vos utilisateurs l’expérience la plus rapide, la moins chère, et de la meilleure qualité possible ; et la compression est l’un des meilleurs outils pour y arriver.

Naturellement vous pourriez juste faire un site mobile, avec moins de contenu et couvrant moins de choses. Toutefois, il a été démontré que beaucoup d’utilisateurs ne veulent pas d’un site mobile : un tiers des visiteurs de ces sites optent pour le site complet lorsqu’on leur propose. Les propriétaires de sites capables de fournir une expérience utilisateur qui soit rapide, fiable et multi-plate-forme, sur de multiples appareils et connexions, auront remporté la bataille du web dans un futur par si lointain.

Types d’algorithmes de compression

La fédération de la compression textuelle est principalement composée d’algorithmes de compression sans perte (si on ignore certains cas à la marge de données textuelles à virgule flottante qui traînent par endroits). Ces algorithmes permettent la récupération directe du flux d’origine sans perte de précision ou de données. Dans la plupart des outils d’archivage et de compression, les codecs sans perte les plus populaires sont LZ77, Huffman, et le codage arithmétique. Les algorithmes de compression sans perte sont la colonne vertébrale de la plupart des codecs, souvent appliqués après d’autre algorithmes pour tenter de gagner encore quelques points de pourcentages de compression.

Avant Après
aaaaabbbbbcccddddeeeeffffaaaaabb a5b4c2d4e4f4a5bb
Figure 1 — Un exemple de compression sans perte. Des séquences de valeur identique sont encodées avec le symbole suivi de la longueur de la séquence. Il est facile de restaurer le flux d’origine. Notez que dès que si la série ne dépasse pas 2 caractères, autant la laisser telle quelle. On le voit à la fin avec le « bb ».

Dans certains cas rares, on peut encore économiser de la place en appliquant une transformation à perte sur certaines parties du contenu avant de compresser sans perte. Dans la mesure où il devient alors impossible de restaurer les données exactes du flux source, ce type de procédé est généralement réservé à des types de données textuelles qui ne souffriront pas de cette perte d’informations ; par exemple, tronquer des nombres à virgule flottante à seulement deux chiffres après la virgule peut être estimé acceptable sur certains jeux de données.

Avant Après
0.123, 1.2345, 21.2165, 21.999, 12.123 0,0,20,20,10
Figure 2 - Un exemple de compression à perte. Les valeurs sont quantifiées au plus proche multiple de 10 inférieur ou égal à elles. Cette transformation ne peut être inversée.

Formats de compression textuelle

La majorité des systèmes actuels de compression textuelles fonctionnent en enchaînant plusieurs transformations de données successives pour obtenir le résultat souhaité. Chaque étape du processus cherche à produire un format que l’étape suivante pourra exploiter, en compressant le mieux possible. La combinaison de ces étapes produit un petit fichier au contenu d’origine récupérable sans perte. Il y a littéralement des centaines de formats/systèmes de compression, chacun avec ses avantages et inconvénients en fonction du type de données manipulé. Vous n'avez probablement jamais entendu parler de la plupart d'entre eux, parce qu'ils ne sont pas soit assez robustes (capables de traiter des types variés de données), soit assez efficaces. En ce qui nous concerne, nous allons examiner les trois formats les plus populaires : GZIP, BZip2 et 7zip.

Formats acceptés par le Web : GZIP et Deflate

Il y a deux mécanismes de compression HTTP couramment utilisés sur le web aujourd’hui : DEFLATE, and GZIP.

DEFLATE est un algorithme de compression très populaire qui enrobe généralement les données à l’aide de l’algorithme LZ77 et du codage de Huffman. GZIP est un format de fichier qui utilise DEFLATE en interne, en complétant avec des heuristiques intéressantes de gestion de blocs et de filtrage, un en-tête et une somme de contrôle. En général, les heuristiques supplémentaires de GZIP produisent de meilleurs taux de compression que DEFLATE seul.

La stack web a fait de son mieux pour semi-automatiser l’utilisation de ces technologies, en déportant la compression effective des fichiers au serveur de distribution (les deux algorithmes sont raisonnablement rapides tant pour la compression que pour la décompression, ce qui en fait d’excellents candidats pour une utilisation côté serveur). PHP, Apache, et même Google App Engine prennent tous en charge GZIP ; ils compressent les fichiers à votre place et vous permettent de définir des en-têtes HTTP qui décrivent comment le trafic sera transféré.

La prochaine génération de protocoles de transferts comme SPDY et HTTP 2.0 permet même la compression des en-têtes eux-mêmes à l’aide de GZIP, ce qui ancre solidement cet algorithme de compression dans l’avenir la stack web.

Produire vous-même des fichiers GZIP plus petits

La majorité des développeurs déploient simplement du contenu non compressé et délèguent au serveur web leur compression à la volée. Ça donne de bons résultats pour la plupart des gens, et c’est facile à utiliser. Mais ce que beaucoup ne savent pas, c’est que le niveau de compression GZIP sur l’essentiel des serveurs et défini par défaut à 6, alors que le maximum est en fait 9. Ce réglage est volontaire : il permet aux serveurs de compresser les données plus vite, quitte à produire un fichier résultat moins petit.

Vous pouvez obtenir une meilleure compression en utilisant GZIP pour compresser vos fichiers en amont, et en déployant ces fichiers sur le serveur. Vous pouvez bien entendu utiliser GZIP directement pour cela, ou recourir à des compresseurs plus avancés tels que Zopfli et 7zip, qui produisent régulièrement des fichiers gzip plus petits, à l’aide d’algorithmes plus pointus de recherche/correspondance, et de structures de données plus gourmandes en mémoire vive qui permettent une meilleure détection des motifs récurrents.

Afin de bénéficier de ces réductions supplémentaires, compressez vos fichiers en amont et déployez les versions compressées sur le serveur. Vous aurez besoin de configurer votre serveur pour qu’il puisse servir ces contenus pré-compressés correctement (voici comment faire pour Apache, nginx et Amazon Web Services). Quand un client requête une page, elle sera livrée et décompressée comme d’habitude, sans avoir à changer quoi que ce soit côté client.

Autres formats de compression

GZIP est loin d’être le seul candidat sur le marché, et si votre appli web envoie fréquemment de gros paquets de données, alors il va vous falloir investir dans d’autres techniques pour réduire la taille de vos contenus. Une de ces techniques pourrait inclure une format décompressé en JavaScript, qui offrirait de meilleurs résultats que GZIP, avec une vitesse de décompression raisonable.

On a deux formats de compression compétitifs (en gros « ce que les p’tits jeunes d’aujourd’hui utilisent »), à savoir BZIP2 et LZMA, qui produisent tous les deux le plus souvent des fichiers plus petits que GZIP, et qui bien souvent décompressent plus vite par-dessus le marché.

Malheureusement ces deux formats ne sont pas nativement pris en charge par les navigateurs, mais ils sont si populaires qu’on trouve désormais un portage JavaScript des deux, ce qui signifie que vous pouvez compresser vos données avec ces formats, en amont, et les décompresser avec JS côté client.

Les temps de décompression seront plus longs avec cette approche, ce qui implique que ce n’est pas forcément adapté à tous vos contenus, mais les développeurs d’applications web interactives et riches pourraient y trouver des avantages significatifs.

En termes de formats, ces deux-là utilisent des approches bien distinctes dans leurs chaînes de compression, de sorte qu’il est délicat de faire une comparaison détaillée avec GZIP.

Par exemple, BZIP2 est construit autour de la transformée de Burrows-Wheeler, couplée à une transformée Move-To-Front. Ces deux étapes ne réduisent en rien la taille effective des données, mais les transforment de manière à améliorer les résultats ultérieurs d’une Huffman ou d’un encodage arithmétique, qui s’occupent de la compression à proprement parler. BZIP est souvent critiqué pour sa consommation élevée de mémoire (la transformée de B-W peut, si elle est implémentée de façon naïve, consommer très vite beaucoup de mémoire), mais si on compare simplement les résultats, BZIP produit des fichiers plus petits que GZIP.

LZMA peut être considéré comme un cousin éloigné de GZIP. Ils démarrent tous les deux par la très appréciée compression à dictionnaire LZ, suivie d’un système d’encodage à intervalles statistiques. Ce qui permet cependant à LZMA de produire des fichiers plus petits que GZIP, c’est qu’il pousse nettement plus loin ses algorithmes de correspondance et de fenêtrage LZ.

Les préprocesseurs peuvent aider à une meilleure compression

Classiquement, la compression textuelle sur le web est un processus à deux étapes ; d’abord une minification, puis une compression sans perte.

Minification

La première étape, la minification, consiste à réduire la taille des données de façon à permettre sa lecture sans traitement complémentaire par les systèmes internes du navigateur. En gros, on retire tout octet superflu du fichier, sans le changer syntaxiquement. Par exemple, il est généralement acceptable de retirer tout whitespace d’un fichier JavaScript, ce qui réduit sa taille sans changer sa syntaxe. La minification est généralement traitée par votre système de build, soit manuellement soit dans le cadre de builds automatiques.

Minifieurs CSS

Le choix ne manque pas pour les minifieurs CSS. Parmi les choix possibles, on a notamment :

Essayez-en quelques-uns et choisissez celui qui vous donne les meilleurs résultats et s’intègre facilement dans votre outillage existant.

La principale différence entre ces outils tient à la complexité et la profondeur de leurs approches de minification. Par exemple, une optimisation simple filtre le texte pour retirer le whitespace et les blocs vides. Mais des optimisations plus avancées incluent le remplacement de codes couleurs nommés, tels que « AntiqueWhite », par leur équivalent hexadécimal (« #FAEBD7 ») lorsqu'il est plus court, et le passage en minuscule de tous les caractères pour améliorer la compression GZIP.

Des méthodes plus agressives d’optimisations vont encore réduire la taille, mais dans certains cas précis risquent de casser vos règles CSS. Du coup, certaines améliorations ne peuvent être automatisées, et les développeurs doivent choisir si le gain en réduction de taille justifie le risque éventuel ou non.

En fait, on voit apparaître une tendance de création d’autres langages alternatifs à CSS qui vous permettent d’écrire vos feuilles de style plus efficacement, et ont l’avantage que leurs compilateurs peuvent souvent produire des CSS plus compactes.

Minifieurs JavaScript

Comme pour les minifieurs CSS, on ne trouve pas forcément une solution unique qui marche pour tout le monde côté JavaScript. Là encore, ils jouent tous dans la même cour, aussi prenez le temps de les évaluer pour vos besoins et votre environnement de travail. Les plus populaires sont :

La plupart opèrent en analysant votre JavaScript pour produire un Abre Syntaxique Abstrait, puis en re-générant une version plus compacte du JavaScript à partir de là. Parmi les optimisations possibles, on trouve le retrait du whitespace superflu, le raccourcissement des identifiants privés, et la réécriture d’expressions vers des formes plus courtes. Par exemple, foo.bar au lieu de foo['bar'].

Les minifieurs automatisés font bien leur boulot, mais certaines optimisations avancées sont hors de la portée des robots.Une nouvelle génération de hackers JS repousse les limites de la minification au-delà des automatisations à l’aide de minifications artisanales, qui produisent souvent des fichiers plus compacts que ce que les outils automatisés peuvent générer. Naturellement, ça demande un peu de folie pour y arriver.

Traitements spécifiques au contenu

Même si les algorithmes de compression sans perte à portée générale produisent déjà d’excellents résultats, il est courant de pré-traiter vos données pour améliorer la compression. Les plus gros gains dans la plupart des systèmes de compression viennent désormais de décisions soigneusement informées sur le format et la structure de l’information, et exploitent ces éléments pour personnaliser le regroupement et la compression (on parle de modélisation). L’essentiel du temps cela nécessite de porter un regard clair et attentif sur le contenu pour déterminer quels types de redondances peuvent être exploitées intensivement. Voici quelques exemples pour lancer votre réflexion :

  • Pour des données textuelles, certains symboles peuvent être retirés du flux compressé, et restitués côté client plus tard (les espaces, par exemple), ce qui réduit la taille totale du fichier mais n’impacte pas trop la performance client.

    Avant
    Après
    “1,2,3,4,5,6,7,8,0,2,3,4,2,1,2”
    “123456780234212”
    Figure 3 - Exemple de retrait de texte connu et redondant. Dans cet exemple on sait que toutes les valeurs seront sur un seul caractère, on peut donc retirer les virgules et les restituer ultérieurement.
  • Si vous transmettez beaucoup de nombres à virgule flottante, la quantification de vos données est une excellente idée, dans la mesure où elle va probablement réduire le nombre de symboles uniques, mais aussi tronquer un niveau de précision qui n’était pas utile à votre fichier.

    Avant
    Après
    0.123, 1.2345, 21.2165, 21.999, 12.123
    0,0,20,20,10
    Figure 4 - Un exemple de compression à perte. Les valeurs sont quantifiées au plus proche multiple de 10 inférieur ou égal à elles. Cette transformation ne peut être inversée.
  • Souvent, les développeurs transmettent des tableaux d’indices, qui ont tendance à ne pas dépendre de leur ordre. Si vos infos d’index sont une liste consécutive (pas d’éléments à signification autre, idéalement toutes les valeurs de l’intervalle figurent dans la liste), vous pourriez trier l’information puis l’encoder en tant qu’écarts pour gagner de la place.

    Avant
    Après
    [8,2,1,5,3,7,6,3,2,9,0,4]
    sorted = [0,1,2,3,4,5,6,7,8,9]
    delta encoded = [0,1,1,1,1,1,1,1,1,1]
    Figure 5 - Un exemple de tri puis encodage delta. Chaque élément devient l’écart avec l’élément suivant. Remarquez que le texte résultat contient de nombreuses répétitions, qui se compressent mieux.
Récemment, le gourou de la compression Matt Mahoney a participé à une compétition qui visait à compresser des séquences d’ADN humain. Ses résultats étaient impressionnants, et reposaient principalement sur l’extraction, la modélisation et l’analyse du contenu exploité. La capacité à extraire des types de données similaires à partir d’un flux entrelacé de blocs homogènes optimise les algorithmes de compression grâce à ces informations locales et spécifiques au contexte, facilitant notamment la prédiction de symboles ultérieurs dans le flux sur base de quelques points de données indépendants.

Écrire ce genre de compression spécifique au contenu peut être compliqué et difficile à maintenir lorsqu’on manipule des fichiers quelconques, aux contenus de multiples natures. Heureusement pour nous, certaines personnes ont déjà fait de belles avancées dans cette direction :

  • XMILL est un système de compression spécifique au XML qui extrait des types hétérogènes de données, les regroupe et procède à diverses compressions plus classiques sur eux.
  • Une autre application géniale est JSZap, qui dissecte votre JavaScript pour produire un Arbre Syntaxique Abstrait, découpe les types de données similaires en flux distincts, et compresse chaque flux individuellement avec le compresseur le plus approprié.
  • On retrouve de nombreuses références à l’application de cette idée à des données JSON ; là encore, on peut pré-traiter des fichiers JSON avant de les passer à GZIP pour améliorer les réductions de taille.

Conclusion

Bien que les images continuent à occuper 60% de la bande passante pour la plupart des sites web, vous ne pouvez pas juste ignorer cet autre bloc de données qui arrive au format texte. Les fichiers JavaScript ne cessent de grossir, des données JSON circulent dans tous les sens, et de plus en plus d’utilisateurs sont en ligne au travers de connexions de piètre qualité. Alors assurez-vous de bien respecter cette checklist de compression textuelle chaque fois que vous déployez un build de votre site :

  1. Pensez mobile d’abord pour vos expériences utilisateurs.
    1. Que pèsent les ressources de votre page ? Pouvez-vous réduire ça ?
    2. Combien de temps vos utilisateurs vont-ils attendre que votre page soit chargée sur une connexion moyenne ?
  2. Minifiez tous les contenus qui peuvent l’être.
    1. Les minifieurs CSS et JavaScript sont puissants, simples d’emploi, et intégrables dans vos chaînes de build.
    2. Passez vos contenus à travers leurs préprocesseurs le plus en amont possible.
  3. Utilisez la compression GZIP pour toutes les ressources textuelles.
    1. Assurez-vous que votre serveur a la compression GZIP activée.
    2. Compressez davantage et en amont (mais GZIP) grâce à Zopfli ou 7zip.
  4. Si vous avez encore besoin de réduire, utilisez des codecs avancés tels que BZIP2 et LZMA.

Comments

0