Optimiser et accélérer les pages web

Apprenez à accélérer vos pages !

Avec ce tutoriel vous verez comment rendre votre site web plus rapide : de l’optimisation des requêtes, à l’affichage de la page, en passant par la génération des pages web, le cache disque, le cache serveur, l’optimisation des images et du code.

Pourquoi ?

Avoir des pages qui s'affichent rapidement n'a que des avantages. Si une page met trop longtemps à s'afficher, elle peut vous faire perdre des visiteurs.

De plus, une page plus rapide, c'est de la bande passante d'économisée, ce qui est toujours bon.

Vous verrez qu'il ne suffit que de quelques manips pour rendre le chargement de vos pages plus rapide.

Pour info, le poids moyen d’une page Web en 2013 était de 1,5 Mo. Ceci est énorme. Même avec une connexion haut débit à 20 méga, la temps de chargement est déjà à 1 seconde entière. Il est urgent de changer ça, d’autant plus que de plus en plus de visiteurs utilisent leur mobile pour surfer, et donc une connexion plus lente.

Avant de commencer…

W3C valid code Avant de commencer, sachez qu'il faut avoir un code HTML valide, sans erreurs. Pour ça, faites lui passer le test du validateur HTML & CSS.

Si il y'a des erreurs dans le code source, c'est le navigateur qui va tenter de les réparer, avec les effets secondaires que ça engendre, comme des déformations de page. Cette opération est aussi longue et gourmande en ressources système.

Il est aussi nécessaire que la page soit légère d'elle même avec un « code efficace » et optimisé : j'ai rapidement expliqué cela ici : Optimiser HTML & CSS.
En plus du code source, on peut jouer sur les images. J'ai là aussi un tutoriel : Optimiser les images pour le Web.

Ce tutoriel porte sur toutes les techniques qui rendent le chargement des pages encore plus rapide : contrôler le cache des fichiers, compresser les pages au moment de l'envoi, diminuer le nombre de requêtes…

Sommaire

Nos outils de diagnostic

Divers outils en ligne s'offrent à nous pour aider optimiser votre site. Ils analysent vos pages et donnent des solutions à mettre en place pour accélérer leurs chargement.

En voici une sélection :

Sommaire

Optimiser le Code Source.

J'ai déjà mentionné qu'il fallait avoir un code source valide W3C. Mais on peut encore faire mieux et avoir un code propre et efficace.

Sur le HTML

Il s'agit de réduire la quantité de code. Ne pensez pas que ce soit négligeable : on peut facilement gagner 10% sur le poids de la page.

Choisir les balises correctement est crucial. L'exemple typique, ce sont les titres. Pour faire un titre, il y'a des balises spéciales qui sont <h1>, <h2>, etc. et qu'il faut utiliser.
Certains utilisent à la place des <p> améliorés en CSS : surtout pas ! Ça fait du CSS en plus et le plan du document (donné par les h1, h2…) n'est alors plus là. Sémantiquement, c'est pas le top et c'est aussi assez mauvais pour le référencement.

Un autre moyen de réduire la code, c'est sur l'indentation. Préférez des tabulations plutôt que 4 espaces consécutifs. (une tabulation = 1 octet ; 4 espaces = 4 octets). (Le remplacement des 4 espaces par des tabulations est aussi (et surtout) valable pour le CSS).
Normalement, les éditeurs proposent un fonction pour choisir entre tabulations ou espaces ;

Désactiver les espaces/tabulations sur Gedit
Sur GEdit, on règle ça dans les préférences.

De plus, si vous faites comme moi et créez un site dans un éditeur de texte, évitez de mettre trop d'espaces dans le code source (même si aérer le code source reste essentiel, 15 espaces entre chaque paragraphe serait exagéré).

Sur le CSS

Le CSS en premier !

Le chargement du CSS peut se faire en parallèle avec le reste du document HTML. On peut donc faire en sorte qu’il se charge dès le début en mettant le CSS dans l’entête <head> de la page, plutôt que le charger en dernier.

Non seulement le chargement sera amélioré, mais le rendu de la page également.

Attention, ceci est valable pour le CSS, car pour le JavaScript, il est préférable de le placer en bas de la page (voir plus bas).

Séparer le squelette CSS du reste

Aujourd’hui, Google par exemple souhaite être encore plus efficace et propose de mettre dans le document HTML le squelette CSS et de mettre le reste dans un fichier séparé. Ceci permet de charger très rapidement les propriétés CSS importantes, et le reste (styles moins importants) un peu plus tard lors du chargement. Le rendu global de la page peut ainsi être amélioré.

Un fichier CSS externe

Si votre site possède plusieurs pages avec le même CSS, il faut faire un fichier CSS externe. Il sera mis en cache par le navigateur et ainsi ne sera téléchargé qu'une seule fois pour tout le site.

Gardez en tête que dans ce cas le fichier CSS externe ne doit contenir que le CSS commun à toutes les pages (le squelette). Les styles propres à une chaque page doivent être mis dans le <head> de la page concernée.
On évite ainsi que du CSS spécifique à une seule page ne soit téléchargée pour les autres pages inutilement.

Pas de CSS inutile !

Sachez aussi optimiser le CSS et regrouper ce qui peut l'être. Par exemple, à l'heure où les CSS3 sont maintenant bien implantés dans tous les navigateurs, il est devenu inutile de spécifier des styles propres aux navigateurs.

Voici un exemple pour les coins arondis, tout le monde utilise ceci :

border-radius: 10px;
-moz-border-radius: 10px; /* inutile */
-webkit-border-radius: 10px; /* inutile */
-khtml-border-radius: 10px; /* inutile */

À moins que vous ne vouliez vraiment avoir un support pour les navigateurs d’il y a 10 ans, il est préférable de se mettre à jour.

L’exemple avec border-radius est typique, mais il en va en fait de même pour les propriétés box-shadow, transition, animation ou linear-gradient. Vous pouvez supprimer les lignes avec -moz- ou -ms- sans problèmes. Certains préfixes pour certains navigateurs n’ont d’ailleurs jamais existés ! Je tiens une liste de préfixes inutiles ici : Les préfixes CSS obsolètes.

D'autres truc inutiles sont aussi à bannir absolument. Par exemple :

border: auto;
padding: auto; /* idiot de spécifier ce qui est déjà automatique */

font-size: 1em; /* La taille du texte est toujours de 1em. Voir la définition du "em" pour cela. */
font-size: 100%; /* Même remarque */

Combiner les fichiers CSS

Exemple à ne pas suivre :

combine css files to optimize page speed
Ici, il est urgent de regrouper les fichier CSS.

À chaque fichier que la page HTML doit importer, il y a :

Le temps d'une requête n'est pas optimisable, il dépend du serveur et de la connexion internet. Le moins il y en a, le mieux c’est !

Ce ne sont que des fractions de secondes, mais on peut les gagner !

Il est simple de regrouper des fichiers CSS ou JS entre eux… De même pour les images : pour les images de fond, la méthode des portes coulissantes réduit le poids de l'image, divise le nombre de requêtes, économise (souvent) du code CSS, améliore le rendu de la page.

Soyez précis sur les noms des éléments que vous cibler

Je l’explique dans la partie En jouant sur les CSS : quand vous ciblez un élement, il est mieux de spécifier un élement par son ID et sa classe plutôt qu’en remontant tout l’arbre DOM en faisant « html body div div ul li {…} » (plutôt donner une classe au lien : « a.class {…} ».

Plus vous êtes précis, plus le navigateur affichera la page vite.

Certains diront qu’il faut éviter les ID en CSS, car c’est dur à maintenir et que c’est moche, mais c’est qu’ils ne savent pas les utiliser. Car ce qu’ils ne disent pas, c’est que les ID sont très rapides pour le navigateur, du fait de leur caractère unique.
Il faut rester pragmatique : n’en mettez donc pas non plus partout, mais quand vous savez qu’un élément est et restera unique dans la page (entête, titre général, logo…) alors vous n’avez pas de raison de ne pas en faire usage.

Sur le JavaScript (JS)

Les scripts en dernier !

GTMetrix recommande de placer le code JS à la fin des pages.

Lors du chargement de la page, l'internaute est intéressé par le texte et les images, pas par les instructions destinées au navigateur. En plaçant le JS à la fin du document le “superflu” n'est chargé qu'à la fin.

De plus, il se trouve que (contrairement au CSS et aux images) le navigateur ne peut pas télécharger du JS en parallèle avec le reste du code. Si la page commence avec un pâté de 700Kio de JS, l'internaute devra attendre que le JavaScript ait fini de charger avant de voir apparaître la page elle même.

Pensez aussi à rapatrier les documents externes (bibliothèques JQuery par exemple) sur votre serveur, plutôt que de faire uen requête externe sur api.google.com/jquery, par exemple.

Et si l'internaute est ennuyé par la lenteur de vos pages, et ben il fait comme moi : il zappe et ne revient pas…

J’ai tout expliqué ça dans mon article Pourquoi mettre le JavaScript à la fin ?.

Compresser les scripts

Le code JS ne change que très rarement, alors le fait qu'il soit lisible n'est que peu important. On peut donc supprimer tous les espaces ainsi que les commentaires du code source. (Au pire, gardez une version indentée et aérée pour vous.)

Il y'a des sites pour réduire ces les pages JS : jslint.com est un exemple.

Fichier CSS/JS externe VS code intégré

Je me suis posé la question Faut-il mieux avoir des fichiers JS et CSS externes ou faut-il intégrer le code entre les balises <script> et <style> ?

J'en suis arrivé à la conclusion qu'il fallait mieux intégrer les petites fonctions JS directement (ça économise une requête HTTP pour les scripts tout petits) et qu'il fallait mieux mettre les scripts plus gros (JQuery…) et les fichiers CSS dans des fichier externes pour rendre possible la mise en cache par le navigateur.

Optimiser le code JS

En plus du téléchargement d’un script JS, ce dernier est éxécuté par votre navigateur : les scripts plus rapides contribuent donc à un affichage plus rapide.
En règle général, aujourd’hui, toutes les manipulations de code HTML ou CSS au moyen du JavaScript devraient être fait en utilisant les manipulations DOM et les Events plutôt que la manipulation de HTML. Faire ainsi rendra votre code beaucoup plus rapide.

On fera ainsi ceci :

var par = document.createElement("p");
document.getElementById("id").appendChild(p);

Plutôt que ceci :

document.getElementById("id").innerHTML = "<p></p>"

Pour donner un ordre d’idée, la manipulation DOM pour supprimer le code HTML d’un bloc est 94% plus rapide qu’utiliser le innerHTML.
De même, bien que la praticité de jQuery n’est plus à démontrer pour certaines applications, il est en revanche extrêmement lent pour d’autres applications, en particuliers celles qui ne necessitent que des manipulations JavaScript de base, ou des traitements purement mathématiques. Ne l’utilisez donc que pour des applications web complexes, et non pour des choses simples et anecdotiques.

N’hésitez pas non plus à utiliser plus de variables et une ligne de JS en plus, plutôt que de produire un code lent.
Il convient de remplacer cette pratique lente :

for (var i=0 ; i<array.length ; i++) {
	...
}

Par ceci :

var alength = array.length;
for (var i=0 ; i<alength ; i++) {
	...
}

Ou mieux encore, ceci (qui est la même chose, mais sur une seule ligne) :

for (var i=0, alength = array.length ; i<alength ; i++) {
	...
}

Dans le premier cas, la longueur du tableau est recalculée à chaque bouclage du for, ce qui prend du temps. Dans les autres cas, le calcul de la longueur du tableau n’est fait qu’une seule fois et le résultat est stocké dans une variable qui n’a pas besoin d’être recalculée.
Notez que cette pratique sur les tableaux est à utiliser de façon générale dans tous les langages de programation.

Les images

Quoi de plus banale qu'une image ? Pourtant, un très grand nombre de sites ne les intègrent pas correctement, entre les dimensions qui ne sont pas spécifiées (ralentissant le rendu de la page coté internaute) ; la balise alt qui est vide ou absente ou l'utilisation de formats dépréciés (*.bmp).

Ce sont des choses simples mais qui sont importantes, non seulement pour le chargement mais aussi pour les robots des moteurs de recherches, et donc votre SEO.

Et surtout : optimisez vos images ! J'ai tout expliqué dans ce tutoriel ! L'optimisation et le nettoyage des images peut tout changer.

Pensez aussi à utiliser des images JPG progressifs : ils chargent beaucoup plus vite dans le navigateur ou du moins, en donnent l’illusion. Ceci est important car il retient vos visiteurs de fuire un site qui est perçu comme trop lent.

Base 64

Les images sont des fichiers externes. Par conséquent, il faut que le navigateur envoie une requête au serveur pour pouvoir l'afficher. On peut optimiser l'image comme on veut, la durée de cette requête n'est pas réductible.

Avec la méthode suivante, on supprime justement cette requête : l'image ne sera pas un fichier externe mais complètement intégré au code HTML.

Il ne faut pas oublier que les fichier, que ce soient des images ou du HTML, ne sont toujours que des donnés binaires que l'on peut manipuler tel quel. Le système Base64 utilise ce fait pour transformer les images en "texte", et c’est ensuite le navigateur qui reconstitue l’image.

En pratique, c'est très simple à mettre en place. Au lieu de faire :

<img src="/dossier/image.png" alt="description" />

On mettra :

<img src="data:image/png;base64,f04Hsj7DDfhkj4mFFSfQ...[…]" alt="description"/>

Où le charabia en rouge après le "base64," correspond au codage de l'image dans le format de la base64. La conversion de l'image peut se faire avec des convertisseurs en ligne comme celui ci (d'autres ici).

Sous GNU/Linux, la commande base64 fichier permet de le faire directement aussi.

Voici un exemple (si vous voulez voir dans le code source) :

smyle smyle
image normale (338 octets) image affiché par base64 (453 octets)

On peut constater que l'image en base 64 est 33% plus lourde que l'image dans le fichier externe. Il faut faire ici un compromis entre la quantité de code en plus et le gain obtenu par la suppression d’une requête HTTP.

En pratique, pour de petites images et fichiers (inférieur à 1 kilooctet), le temps que prend une requête est largement supérieure au temps du téléchargement du fichier lui-même, cette méthode est donc très avantageuse pour ces fichiers là.

Nettoyer les SVG

Les SVG sont les images vectorielles.
Ces images sont définies avec des fonctions mathématiques, écrites en XML.

La plupart des logiciels de créations d'images SVG, comme Inkscape incluent des balises inutiles (pour le "created with" par exemple) dans le code généré. On peut beaucoup réduire la taille des SVG en supprimant tout ces balises inutiles.

Il existe un logiciel pour ça : scour. Téléchargez l'archive avec les fichiers .py (code sourcd de Python), puis si vous avez Python d'installé :

python scour.py -i image_in.svg -o image_out.svg

Sur les SVG, pensez à réduire le nombre de décimales dans les valeurs : inutile de spécifier des millipixels. Si vous avez 1000 valeurs dans un fichier SVG, alors donner les chiffres avec une décimale au lieu de six, c’est déjà réduire le fichier 5 ko…

Sommaire

Apache et le .htaccess

Améliorrer la vitesse de votre site se fait au niveau du code, (voir ci dessus) mais on peut aussi s'aider du serveur, qui possède des astuces en magasin…

le .htaccess ?

Le .htaccess est un fichier. Il contient des directives que Apache (le serveur) doit appliquer en fonction des requêtes de l'internaute. (interdire l'accès au fichier, le compresser, le mettre en cache en sont des exemples).

ATTENTION ! Chaque hébergeur n'ayant pas la même configuration d'Apache, il est indispensable de tester le code sur votre espace d'hébergement.

ATTENTION ! (bis) la syntaxe de ce fichier est extrêmement stricte ! La moindre faute provoquera une erreur 500 du serveur et un site inaccessible.

Enfin, notons que le fichier .htaccess est chargé à chaque requête. Essayez donc de l'adapter pour vos pages. (exemple : inutile de mettre 50 directives pour les fichiers *.swf si votre site n'héberge pas de fichiers Flash.)

Compresser avec Deflate

Apache est capable de compresser les fichiers avant des les envoyer à l'internaute. C'est ensuite au navigateur de décompresser ce qu'il reçoit. Au final, le temps de chargement diminue (drastiquement) et de la bande passante est économisée.

# compression avec MOD_DEFLATE
AddOutputFilterByType DEFLATE text/html text/plain text/xml text/css text/javascript application/atom+xml application/rss+xml application/xml application/javascript

# les proxys ne doivent pas décompresser le contenu à la place de l'internaute
Header append Vary User-Agent env=!dont-vary

Le cache-control

Ceci permet de forcer le navigateur à enregistrer les fichiers sur le disque dur. Non seulement les pages s'afficheront bien plus vite dans le futur (le fichiers sont déjà chez l'internaute) mais surtout ça diminue la bande passante consommée.
C'est certainement le moyen le plus efficace pour accélérer le chargement des pages, au moins pour les visiteurs réguliers.

Ajoutez simplement ceci dans votre fichier .htaccess :

<IfModule mod_headers.c>
  # Mise en cache des images et autres fichier statiques pour un mois
  <FilesMatch ".(ico|jpe?g|png|gif|swf|flv|css|js|gz|pdf)$">
   Header set Cache-Control "max-age=2592000"
  </FilesMatch>
# Mise en cache du html et xml pour 12 heures <filesMatch ".(html|htm|xml)$"> Header set Cache-Control "max-age=43200" </filesMatch>
# Désactive la mise en cache pour les fichier PHP et CGI <FilesMatch ".(php|cgi)$"> Header unset Cache-Control </FilesMatch> </IfModule>

Arrangez cela comme vous voulez (types de fichier avec la durée) en fonction de votre site : si vous modifiez le fichier CSS de votre site tout le temps, peut-être est-il bon de lui donner une durée de cache plus courte.

N'oubliez pas non plus d'adapter ce code pour votre site : j'ai mis les extensions swf, flv et pdf, mais il se peut que vous n'en ayez pas besoin.

Les « expires headers »

Il s'agit comme la mise en cache, de préciser au serveur que certains fichiers ne doivent pas être envoyés à l'internaute la prochaine fois qu'il reviendra, et qu'il les a déjà.
On spécifie ici aussi une durée par type de donnée :

<IfModule mod_expires.c>
  ExpiresActive On
  ExpiresDefault "access plus 7200 seconds"
  AddType image/x-icon .ico
  AddType application/x-font-woff .woff
  AddType application/x-font-woff2 .woff2
  ExpiresByType image/gif "access plus 1 year"
  ExpiresByType image/ico "access plus 1 year"
  ExpiresByType image/jpg "access plus 1 year"
  ExpiresByType image/png "access plus 1 year"
  ExpiresByType image/jpeg "access plus 1 year"
  ExpiresByType image/icon "access plus 1 year"
  ExpiresByType image/x-icon "access plus 1 year"
  ExpiresByType image/svg+xml "access plus 1 year"
  ExpiresByType text/css "access plus 1 year"
  ExpiresByType text/html "access plus 7200 seconds"
  ExpiresByType text/javascript "access plus 1 year"
  ExpiresByType application/xhtml+xml "access plus 7200 seconds"
  ExpiresByType application/x-javascript "access plus 1 year"
  ExpiresByType application/x-shockwave-flash "access plus 1 year"
  ExpiresByType application/x-font-woff "access plus 1 year"
  ExpiresByType application/x-font-woff2 "access plus 1 year"
</IfModule>

On peut mettre ce code en même temps que celui du cache-control, même si je pense qu'un des deux suffit. N'oubliez pas que le fichier .htaccess est sollicité entièrement à chaque requête, il ne doit donc pas être trop lourd.

Les ETAG

Les ETAG permettent d'identifier les versions d'un fichier. Si entre deux visites un fichier a été modifié sur votre site, le serveur et le navigateur auront des ETAG différents et le fichier sera renvoyé, sinon il ne renvoie pas.

GTMetrix conseille d'activer les ETAG.
Perso, je serais d'avis de faire comme expliqué ici et les désactiver : à chaque requête, le navigateur et le serveur doivent comparer les ETAG qu'ils ont pour un fichier, ce qui génère du trafic inutilement.

J'ai donc désactivé ça, considérant que la mise en cache suffisait.

Header unset ETag
FileETag none

Sommaire

Avec PHP

PHP génère nos pages… Peut-être peut-il nous aider, non ?

La compression GZip

GZip est un moyen de compression utilisé dans divers formats. PHP permet aussi de compresser le contenu (indépendamment de deflate) avant de l'envoyer au navigateur du visiteur.

Il suffit simplement de placer ce code PHP tout en haut de vos pages :

<?php 

function initOutputFilter() {
   ob_start('ob_gzhandler');
   register_shutdown_function('ob_end_flush');
}
initOutputFilter();

?>

Mettre les pages en cache de fichiers statiques

Récemment, mon site était très lent. J'ai donc mis en place un système de cache de fichiers statiques pour les pages les plus consultées. Le résultat est sans appel :

Avec/sans une mise en cache de pages statiques
Avant/après une mise en place de cette méthode sur ce site (plus la ligne est proche de zéro, mieux c'est)

On voit sur ce graphique que la charge du serveur a très fortement diminuée après la mise en place du cache de fichiers statiques pour les deux principales pages du site. Par ailleurs, le site en est devenu tout de suite très très rapide.

En fait, PHP génère le code HTML d'un page à partir de données brutes. Mais une fois que la page est créée et envoyée au visiteur, elle est oubliée. Si un autre visiteur arrive, il doit tout regénérer.
L'idée ici, c'est qu'une fois que la page est construite, on va la stocker dans un fichier au lieu de la perdre. Si un autre visiteur arrive, on ne va pas regénérer la page, mais on va lui envoyer le fichier sauvée. De cette manière PHP ne travaille presque plus : les lourdes et longues tâches de générations de la page sont remplacées par une seule et simple tâche de lecture d'un fichier.
Évidement, à intervals réguliers (chaque demi-heure par exemple), la page statique est supprimée, on la regénère pour prendre en compte d'éventuelles nouvelles données et on la restoke.
Ainsi, le serveur ne génère la page non plus à chaque visiteur, mais seulement 1 fois. C'est beaucoup mieux.

Bien sûr, cette méthode n'est à appliquer que sur des pages très demandées et/ou lourdes à générer : sur ce site, c'est le blog et le fichier RSS du blog qui sont de loin les pages les plus demandées. Ce sont les seules pages à bénéficier d'une mise en cache statique.

L'ensemble se fait au moyen de PHP (le code m'a été partagé par Sebsauvage) :

  
$fichierCache = 'cache.dat';
// si la page n'existe pas dans le cache ou si elle a expiré (30 minutes)
// on lance la génération de la page et on la stoke dans un fichier
if (@filemtime($fichierCache)<time()-(1800)) {
    // on démarre la bufferisation : rien n'est envoyé au navigateur
    ob_start();

    // C'est ici qu'il faut placer votre code qui consomme le plus de ressources

    // on recuperre le contenu du buffer
    $contenuCache = ob_get_contents();
    ob_end_flush(); // on termine la bufferisation
    $fd = fopen("$fichierCache", "w"); // on ouvre le fichier cache
    if ($fd) {
        fwrite($fd,$contenuCache); // on écrit le contenu du buffer dans le fichier cache
        fclose($fd);
    }

// sinon le fichier cache existe déjà, on ne génère pas la page
// et on envoie le fichier statique à la place
} else {
    readfile('cache.dat'); // affichage du contenu du fichier
    echo "\n".'<!-- Servi par le cache -->'; // et un petit message
}

Il n'est pas utile de placer tout le code PHP du script entre le ob_start() et le ob_end_flush(), mais seulement les lignes de fonctions qui sont les plus gourmandes en ressources.
Ainsi, dans le fichier index.php de Blogotext (le script utilisé sur le blog de ce site), ce sont les 3 lignes suivantes seulement dont le code produit est mis en cache :

afficher_calendrier($depart, date('m'), date('Y'));
$tableau = table_derniers($depart, $GLOBALS['max_bill_acceuil'], '1', 'public');
afficher_index($tableau);

Astuces pour le code PHP

Ce que suit sur le PHP sont des astuces toutes simples mais qui font gagner énormément de temps et diminuent aussi un peu la charge du serveur.

La taille des tableaux dans les boucles

D'après ce benchmark, si on doit faire une boucle sur un tableau il faut mieux calculer la dimension du tableau avant plutôt que de boucler tant que le tableau n'est pas fini. On divise ainsi le temps de traitement par 500, ce qui est énorme. :

<?php
// soit un tableau $tableau[] de 1000 cases.

   for ($i=0; $i < sizeof($tableau); $i++) {
      echo $tableau[$i];
   }

?>

↑ durée : 87 906 µs

<?php 
// soit un tableau $tableau[] de 1000 cases.

   $longueur = sizeof($tableau);

   for ($i=0; $i < $longueur; $++) {
      echo $tableau[$i];
   }

?>

↑ durée : 148 µs

(source : http://www.phpbench.com/)

Des fonctions plus gourmandes en ressources que d'autres

Certains tests comme celui ci montrent qu'il y'a clairement des différences entres deux fonctions équivalentes.

Je ne vais pas recopier tout le test, vous lirez si vous voulez, je vais juste reprendre les plus facile à mettre en place et ceux qui donnent le meilleur résultat.

Print() VS echo

L'exemple le plus parlant du test précédent, c'est certainement le print('') VS le echo '' :

echo 'Lorem ipsum dolor sit amet, consectetur adipisicing elit';
4,01 µs
print('Lorem ipsum dolor sit amet, consectetur adipisicing elit'); 20,80 µs

(source : http://www.estvideo.com/dew/pages/phpbench/)

Mes propres tests semblent aussi montrer que echo est préférable à print().

double quote (") VS simple quote (')

J'ai encore jamais vu de programmeurs se taper dessus ou troller à propos de cette question, mais chacun a ses habitudes pour programmer.
En revanche, c'est le serveur qui calcul, et lui il voit (d'après le test précédent) que les simples quotes sont un peu plus rapide : le texte n’est en effet pas parsé à la recherche d’une variable à remplacer (comme dans les double-quotes).

require(), require_once(), include(), include_once()

Plein de fonctions qui font la même chose.

La différence entre require() et require_once() c'est que le second n'importe un même fichier qu'une seule fois et évite à PHP de charger plusieurs fois un même fichier.
On pourrait penser que c'est bien, mais on a rarement le cas où l'on importe 500 fois le même fichier…
Coté test, il se trouve que require() est meilleur que require_once().

La différence entre require() et include(), c'est qu'en cas de problèmes, require() provoque une erreur et arrête le script, alors que include() ne donne qu'un avertissement et le reste du code PHP est exécuté.

Ici, include() donne un résultat meilleur que require()

Des fonctions consommatrices de ressources

Les fonctions comme la création et les redimensionnement d'images (imageresize()) sont très gourmandes en ressources ! D'ailleurs, il n'est pas rare que les hébergeurs désactivent cette fonction.

Si vous devez inclure une image faite en PHP dans une page, il faut mieux la créer une seule fois, la stoquer dans un dossier puis l'importer normalement. On va pas demander à PHP de refaire l'image à chaque fois, hein ?

Optimiser les algorithmes de génération des pages

Il s'agit de diminuer le nombre de calculs que PHP doit faire pour afficher une page. Exemple simple : mettre en PHP dans un tableau, tous les nombres paires, de 0 à 1 000 000. C'est très simple, y'a plusieurs solutions, mais certaines sont mieux que d'autres !

Solution 1 : incrémenter une boucle for de un et faire un test de parité :

<?php 

for ($i = 0 ; $i < 1000000 ; $i++) {
   if ($i % 2 == 0) {
      $j[] = $i;
   }
}

?>

PHP, sur mon ordinateur et en moyenne sur 10 tentatives, met 0,790 s

Maintenant, seconde solution : toujours une boucle for, mais on incrémente de 2. Pas besoin de test de parité donc :

<?php 

for ($i = 0 ; $i < 1000000 ; $i += 2) {
	$j[] = $i;
}

?>

Ici, le temps n'est plus que de 0,540 s, soit un gain de 30%.
Logique : il n'y a plus de test if() à faire, et y'a deux fois moins de bouclements pour le for().

Ce genre de modification du code peut parfois tout changer (le moteur de blog utilisé dans le blog de ce site a été accéléré par 20, simplement avec ce genre de corrections mineures.

Un autre exemple, bien plus intéressant : la course au nombre de décimales de Pi se fait avec d'énormes calculateurs. Un homme a réussi à trouver beaucoup plus de décimales avec son ordinateur de bureau (donc un appareil infiniment moins puissant que les super-calculateurs) simplement en changeant d'algorithme !

Sommaire

Améliorer l'affichage

Gné ? L'affichage ?

Pour lire une page, il y'a en effet le chargement de la page (du serveur vers le navigateur) et l'affichage à l'écran.

On peut bien avoir une page qui se charge en 0.3s, mais si le navigateur met 2minutes pour l'afficher, ça ne sert à rien.
N'oublions pas que l'on veut que l'internaute accède rapidement à l'information écrite dans vos pages.

Un exemple… J'ai réussis à créer de joli images en CSS/HTML uniquement. Pour faire cela, la page contient près de 5000 <div>. La page n'est pas très lourde en soi, mais l'affichage si ! C'est pour cela qu'il ne sera jamais question d'utiliser ce code pour faire un fond de page coloré.

J'ai aussi remarqué que l'utilisation abusive des propriétés CSS3 tels que box-shadow et text-shadow faisait laguer la page, et que le rendu était extrêmement lent.
Ces propriétés créent de jolis ombres, mais ces dernières doivent être calculées par le navigateur, et c'est très lourd.
Du coup, j'ai du revoir mes plans et j'ai supprimé ce qui est trop inutile.

Le rendu de la page est gérés par l'ordinateur de l'internaute, et tout le monde n'a pas un PC de Gamer dernier cri avec 12Gio de RAM et un processeur 16cœurs !

Qu'un internaute utilise encore un navigateur obsolète comme IE-6, c'est vraiment le faire exprès, et c'est pour ça que mes pages ne sont pas compatibles IE, d'autant plus que tous les navigateurs sont gratuits, mais tout le monde n'a pas les moyens de se payer un ordinateur puissant, et c'est pour ça qu'il faut faire attention à ne pas abuser des fonctions HTML/CSS et des scripts qui consomment trop de ressources.

Et c'est pour ça que je n'aime pas les sites 100% flash mal foutu : sur mon ordi, le flash rame et lague à mort.

En jouant sur les CSS

Nommez vos éléments !

J’ai découvert une vidéo : un navigateur, comment ça marche ?. Un intervenant de Mozilla explique comment se fait le processus de rendu des pages web. Pour un webmaster qui tape son code HTML et CSS c’est inutile de savoir ça. Néanmoins, j’estime personnellement qu’il est impossible de faire quelque chose bien sans en comprendre toutes les étapes. Je vous recommande donc de regarder cette vidéo (50 minutes). Ensuite, vous saurez pourquoi ce qui va suivre constituent une optimisation énorme pour une page web.

Vous-avez vu la vidéo ? Bien. Alors vous avez vu comme se fait le partage d’une page web. Vous savez donc pourquoi :

Il faut utiliser le plus possible d’attributs « class » et « id » sur les éléments et dans le HTML et utiliser le plus souvent possible des selecteurs précis (fils au lieu de simple déscendant) : les ID sont uniques dans une page web. Spécifiez un style à une ID et le navigateu saura exactement le style à appliquer à cet unique élément, et non à plusieurs qu’il doit trouver.

/* MAL : tout aussi précis, mais le parseur doit chercher tout le document pour les div, les re-div et les ul */
div.header div.top ul {
   (style)
}

/* UN PEU MIEUX : le parseur va un peu plus vite que précédement */
div.header > div.top > ul {
   (style)
}

/* BIEN : très précis, et le parsage est immédiat*/
#menu {
   (style)
}

Dans le CSS il est aussi totalement inutile de spécifier le nom de la balise pour un élement qui possède un ID. Vu que l’ID est forcément unique (sinon votre code n’est pas valide W3C), il est inutile de dire que c’est un <p> ou un <div>, et ça ne fera que remplir la mémoire dédiée au CSS Buckets (cf la vidéo)

/* INUTILE */
div#header div#top ul#menu {
   (style)
}

/* TOUT AUSSI INUTILE : un ID est unique et le navigateur sait déjà où il est. Mais il fera quand même le reparsage, car c’est une machine dénuée d’intelligence */
#header #top #menu {
   (style)
}

/* BIEN : c’est celà qu’il faut faire pour les ID */
#menu {
   (style)
}

C’est à peu près tout ce que dit la vidéo, mais rendez-vous compte : chaque gain de quelques microsecondes gagné par instruction, ça se chiffre en dizaines de millisecondes pour le rendu. Donc même si c’est peu, ces simples pratiques peuvent accéllérrer le rendu de 10 voire 20%, et réduire d’autant la charge CPU, parfois encore limitée sur les petits smartphones (pour lequel vous vous êtes donné tant de mal à faire un thème…).

Animez correctement le CSS

CSS3 permet de faire des transitions et des animations. C’est assez puissant et très joli. Sauf que là également il y a possibilité de faire pas mal d’optimisation.

Un des exemples est l’optimisation de l’animation des ombres : quand on fait une boîte avec une ombre portée, et qu’on anime l’ombre lors du hover, chaque détail de l’ombre provoque un nouveau reflow de la page, et donc du temps CPU de perdu.
L’astuce pour diminuer la consommation en ressources, c’est de créer l’ombre une fois mais de jouer sur son opacité. En effet, si l’ombre est dessinée par le CPU, ils ne sont pas optimisés par ce dernier. En revanche, tout ce qui concerne les couleurs et l’opacité est optimisé au moyen de l’accélération matérielle. En appliquant la transition CSS sur l’opacité plutôt que sur l’ombre, alors la transition sera grandement optimisée. C’est bon pour le CPU, pour la fluidité de votre animation et surtout pour l’autonomie si votre site web est affiché sur un mobile.

En terme de code, c’est très simple : plutôt que d’animer l’ombre avec ça :

.box {
  box-shadow: 0 1px 2px rgba(0,0,0,0.15);
  transition: box-shadow 0.3s ease-in-out;
}

.box:hover {
  box-shadow: 0 5px 15px rgba(0,0,0,0.3);
}

On va utiliser un ::after, la placer sous la .box et dessiner l’ombre dessus. Ensuite, le hover sur la .box va rendre le ::after plus ou moins opaque et son ombre avec :

.box {
  position: relative;
  z-index: 1;
  border-radius: 5px;
}

.box:after {
  content: "";
  position: absolute;
  z-index: -1;
  top: 0; left: 0;
  width: 100%;
  height: 100%;
  box-shadow: 0 5px 15px rgba(0, 0, 0, 0.3);
  opacity: 0;
  transition: opacity 0.6s ease-in-out;
}

.box:hover:after {
    opacity: 1
}

Vous voyez que la transition ne se fait que sur l’opacité.

(source)

Sommaire

Qu'en est-il vraiment ?

Biensûr, il peut être intéressant de se demander combien on gagne vraiment. Je vais donc donner les chiffres pour mes pages.

Je précise donc que mes pages sont 100% codés à la main et qu'il n'y a donc pas de problèmes de « code sale » de certains éditeurs WYSIWYG, qui, s'ils avaient été présents auraient donné des gains encore plus grands.

Réduction du poids de la page

On a donc un gain de plus de 50% sur le poids de la page. On peut supposer que la page chargera 2 fois plus vite simplement avec cela.
Voyons maintenant un peu ce que l'on gagne en utilisant la mise en cache des fichiers, en diminuant le nombre de requêtes (regroupement des fichiers) et tout le reste. Les données sont donnés par YSlow.

Réduction du temps de chargement

Maintenant que la page est légère, générée rapidement côté serveur, il faut l’optimiser pour le rendu côté client :

Réduction du temps d’affichage

Je crois que c'est clair : la quantité de donnés à télécharger est divisée par 10, le temps de rendu peut-être divisé par 2.

YSlow me donne en moyenne 103ms par image et 40ms pour les fichier JS/CSS comme temps de réponse. Comme j'ai 8 images et 2 fichiers qui sont mis en cache, on gagne 904ms (pas tout à fait puisque le serveur doit quand même traiter la demande, mais d'un autre coté, le donnés d'Yslow ne tiennent pas compte du serveur DNS puisque je suis en local. Dans les faits, la mise en cache fait s'afficher la page “seulement” 2 fois plus vite.)

Bon, même si je gagne 1 seconde au chargement, cette valeur est à prendre avec des pincettes car la configuration matérielle et logiciel n'est pas celle d'un serveur. Aussi, je ne tient pas compte de la vitesse de connexion qui peut tout changer.
En revanche, les valeurs de réduction du poids de la page sont réelles, elles.

Optimisation du programme source

Mes pages n'utilisent que peu de PHP. Par contre, la page de mon blog utilise le logiciel Blogotext dont je suis l'actuel développeur.

Par quelques modifications très mineures sur les premières versions de ce script, telles que des inversion de sens d'imbrication d'une boucle, utilisation d'une fonction plus appropriée, etc. j'ai pu diminuer le temps de génération de la page par 20.

En local, le temps de chargement est passé de 6 500ms à 270ms. Bien-sûr, sur un gros serveur la différence ne se fait que peu sentir, mais elle est là quand même.

Depuis, l’optimisation du code PHP n’a jamais cessée. N’oubliez pas non plus que certaines bases de données proposent des options de mise en cache, des indexs ou d’autres méthodes d’effectuer des requêtes beaucoup plus rapidement (là aussi mes propres scripts ont pu être optimisés de façon spectaculaires).

Conclusion

Optimiser les fichiers eux mêmes m'a fait gagner 50% sur le poids de la page, surtout au niveau des images ; et la mise en cache des fichiers grâce aux lignes dans le .htaccess a permis de réduire le temps de chargement par 2.

Une petite analyse du code source m'a permis de diviser la durée de génération des pages par 20. (Cette analyse du code source afin de diminuer le nombre d'opérations nécessaires à un programme pour s'exécuter est valable pour tous les programmes, pas seulement du PHP sur un site web.)

Vous voyez qu'il en faut très peu finalement pour accélérer vos pages de manière considérables. C'est facile à mettre en place (surtout le cache, la compression GZip et l'optimisation des images).
On comprend mieux pourquoi Google veut « sanctionner » les webmasters qui ne font pas le strict minimum pour accélérer un peu leurs pages.

Sommaire

lehollandaisvolant.net/tuto/html.php
Mon tutoriel pour optimiser les images pour le web

www.seomix.fr/web/developpement/guide-htaccess-performances-et-temps-de-chargement/
J'ai pris pas mal d'infos concernant le Cache Control et les Expires Headers ici.

www.presse-citron.net/compresser-ses-fichiers-css-pour-optimiser-le-temps-daffichage-de-ses-pages-web
Quelques règles pratiques pour réduire ses fichiers CSS.

www.webrankinfo.com/dossiers/webmastering/site-rapide
Un autre tutoriel, comme le mien, découvert un peu tard…

www.webou.net/communaute/index.php/topic,3876.0.html
Un usage supplémentaire du cache-control.

developer.yahoo.com/performance/rules.html#js_bottom
Pourquoi il faut mettre le JavaScript à la fin des pages web.

www.takeitweb.fr/blog/configurer-etags.html
La configuration des ETag's dans le .htaccess.

developer.yahoo.com/performance/rules.html#gzip
D'autres infos sur la compression GZip des pages html.

code.google.com/speed/page-speed/docs/rendering.html
Plein d'infos sur le site de Google.

www.smushit.com/ysmush.it
Smush it, pour optimiser et diminuer le poids des images.

jslint.com
Un outil pour optimiser/vérifier le code Javascript

Optimiser son site web de A à Z
En complément de ma page.

Sommaire

Page crée en juin 2010 - Dernière mise à jour le lundi 26 juin 2016
Adresse de la page : http://lehollandaisvolant.net/tuto/pagespd