5 minutes
10Ko Challenge #7 [Bonus] Compression manuelle
Rappel du challenge
Le but de cette série d’articles est de démonter concrètement que réaliser un site internet restant sous la barre des 10Ko tout compris est une entreprise compliquée mais réalisable si nous savons opérer les bons sacrifices.
Et aujourd’hui un bonus ! Une méthode rigolote qui permet de compresser vos pages
La compression par substitution
Historique
C’est durant le visionnage d’une conférence dont j’ai malheureusement perdu le nom que j’ai eu cette idée avant même de démarrer le challenge 10Ko. L’idée présentée était : comment permettre à une canadienne de regarder une page web via le protocole SMS ceci afin de réduire sa facture de data (oui les canadiens ne sont pas vernis côtés tarif 4G avec du 175$ le 100Go / mois facile.)
Sauf que de nombreuses problématiques s’enchainent et nécessitent à l’oratrice d’opérer tout une série d’optimisation avec notamment la compression par substitution aussi appelée par dictionnaire
Et c’est exactement ce mécanisme que je vais utiliser.
Voir +
Mécanisme dans la théorie
Le principe de la compression par substitution d’une page web est simple, vous prenez le code source, vous identifiez les patterns qui se répètent et vous les remplacez par un identifiant qui “représentera” ce pattern. Vous complétez en simultané votre dictionnaire : identifiant => pattern et c’est tout.
Il reste alors à
- Envoyer votre page web réduite
- Envoyer Le dictionnaire
- Permettre au navigateur / client de se dépatouiller tout seul en reconstituant sa page comme un grand.
Fin de l’histoire.
Dans la pratique
Pour mes tests j’ai écrit un script js à jouer sur mes pages déjà en ligne via la console de Chrome ou de Firefox.
Ce script prend le contenu d’une page web existante en entrée et ressort ce contenu compressé associé à un dico et à un script js inline capable de restituer la page de lui-même.
Je n’ai pas poussé le vice à automatiser Hugo pour que ce dernier exécute la substitution mais ça aurait pu être envisagé.
Une fois le rendu ré-uploadé on peut avoir ce résultat. Pensez à regarder le code source de la page :)
Optimisation
La mécanique derrière cette compression est assez simple mais nécessite tout de même prendre gare aux caractères substituant. En effet, utiliser dans les entrées du dico un caractère déjà présent dans la page risque de faire capoter la restitution.
C’est pour cette raison que j’ai initialement utilisé les caractères Grecs pour m’assurer d’un maximum de compatibilité.
var g = ["«","»","“","”","ʹ","͵","·",";","Α","Β","Γ","Δ","Ε","Ϛ","Ϝ","Ζ","Η","Θ","Ι","Κ","Λ","Μ","Ν","Ξ","Ο","Π","Ϟ","Ρ","Σ","Τ","Υ","Φ","Χ","Ψ","Ω","Ϡ","α","β","ϐ","γ","δ","ε","ϛ","ϝ","ζ","η","θ","ι","κ","λ","μ","ν","ξ","ο","π","ϟ","ρ","σ","ς","τ","υ","φ","χ","ψ","ω","ϡ"]
Le résultat est flagrant, la page passe d’un poids à l’origine de 13.26ko
à 12.07ko
avant transmission en Gzip par le serveur.
Alors évidement, avec ce genre d’opération je peux dire adieu au référencement car les moteurs de recherches ne vont pas comprendre que ma page signifie autre chose que du charabia mais … c’est drôle alors je persiste.
En creusant l’écart constaté entre le nombre de caractère et son poids je me rend compte que l’alphabet Grec utilisé, de l’unicode en réalité, est encodé nativement en prenant plus de place1 que des caractères plus usuels tels que ceux utilisés en français : les caractères latin-1. Du coup je tente une seconde optimisation en proposant un second dictionnaire, latin-12 cette fois ci.
var g = ["¡","¢","£","¤","¥","¦","§","¨","©","ª","«","¬","®","¯","°","±","²","³","´","µ","¶","·","¸","¹","º","»","¼","½","¾","¿","À","Á","Â","Ã","Ä","Å","Æ","Ç","È","É","Ê","Ë","Ì","Í","Î","Ï","Ð","Ñ","Ò","Ó","Ô","Õ","Ö","×","Ø","Ù","Ú","Û","Ü","Ý","Þ","ß","à","á","â","ã","ä","å","æ","ç","è","é","ê","ë","ì","í","î","ï","ð","ñ","ò","ó","ô","õ","ö","÷","ø","ù","ú","û","ü","ý","þ","ÿ"]
Après upload le résultat est légèrement mieux.
13.26ko
Pour la page de base12.07ko
Pour la page optimisée avec de l’unicode11.88ko
Pour la page optimisée avec latin-1 (-10%)
J’imagine que je peux encore optimiser ce traitement simpliste mais je m’arrête là car ce n’est pas le but du challenge de redévelopper un algo de zéro.
Utilité ?
Aucune, nada, tout ce que vous venez de lire est inutile. Oui je sais, de rien, vous êtes les bienvenues.
Alors qu’est-ce qu’il se passe ?
Et bien il se trouve que tout le travail effectué pour tenter de réduire la taille du code source est une redite importante d’un des deux composants de l’algorithme gzip nommé LZ77 qui fonctionne lui-même en compression par substitution (la seconde composante de gzip étant Huffman)
Et si gzip ne vous parle pas, sachez simplement que c’est la compression par défaut de la majorité des serveurs web modernes.
Bref nous venons de recoder en moins bien un algo déjà utilisé par défaut par mon serveur web pour servir mes pages. Pour ne rien arrangé, nous avons ajouté une couche de traduction compression -> décompression grâce à JS dont la simple présence rajoute du poids !
Si je reprends les mesures des poids de mes pages, voici le résultat complet :
13.26ko
Pour la page de base compressé en gzip à5.54ko
12.07ko
Pour la page optimisée avec de l’unicode compressé en gzip à6.65ko
11.88ko
Pour la page optimisée avec latin-1 (-10%) compressé en gzip à6.66ko
On se rend bien compte que ne rien faire en fait c’était mieux que de tenter de compresser soi-même.
Cela dit cette exploration a eu un double avantage et c’est pourquoi j’ai poursuivi l’expérience que je savais inutile dès le départ :
- Ce genre d’algo peut avoir son utilité pour de la compression sur des serveurs n’ayant pas de gzip ou pour toute compression manuelle
- C’était fun, et c’est aussi cela que je cherchais au travers du Challenge 10Ko
A voir :
- Comparatif de perfs des méthodes de compression
- Testez la présence de Gzip sur un site facilement ici
Bilan
Voilà j’ai fini de faire le tour de la question, j’espère que ces articles vous auront plus autant que j’ai pris plaisir à les écrire.
See you !
Unicode = 16bits par caractères soit 2 octets ↩︎
Latin-1 = 8bits par caractères soit 1 octet ↩︎