simulation, qui permet de tester le comportement d’un input avec de la latence.
Imaginez également la situation suivante, vous avez investi beaucoup de temps pour obtenir un bon temps de chargement. Vous avez également consacré beaucoup d’énergie pour avoir une super UI et UX. L’utilisateur arrive donc sur la page, avec un téléphone dont le CPU est modeste, il commence à scroller et à naviguer et là c’est la catastrophe, de gros ralentissements commencent à apparaître. Tous vos efforts précédents sont gâchés. Nous allons donc voir ensemble comment éviter cette situation.
La métrique utilisée dans ce cas est le taux de rafraîchissement (ou FPS). La valeur normale admise, pour que les interactions soient perçues comme fluides par l’utilisateur, est de 60 images par seconde, soit environ 16.7 ms par frame.
Vous pouvez visualiser cette information en temps réel avec les Chrome DevTools (cette information est également disponible dans les DevTools Firefox). Dans le cadre de cet article on utilisera les Chrome DevTools.
Paramètres des devtools |
Fenêtre d’accès rapide aux fonctions des devtools |
Vous devriez maintenant avoir en haut à gauche un cadre avec l’indicateur de FPS ainsi que d’autres informations.
Indicateur de frame rate |
Pour afficher chaque frame à l’écran le navigateur va effectuer ce que l’on appelle le pipeline de pixels. Il y a 5 étapes consécutives importantes, sur lesquelles on peut intervenir, qui vont se succéder pour aboutir à la construction de la frame et de l’affichage des pixels à l’écran.
Représentation du pipeline de pixel (source : https://developers.google.com/web/fundamentals/performance/rendering/) |
Le navigateur décompose la construction d’une frame à travers ces étapes, dans un premier temps intéressons nous à chacune d’entre elles :
Visualisation du site Octo en couches avec les Chrome DevTools |
L’avantage de peindre l’image en plusieurs couches est d’isoler les changements pour éviter le plus possible de devoir tout repeindre à chaque fois.
Technique : si vous pensez qu’une zone de votre interface sera repeinte régulièrement vous pouvez indiquer au navigateur de lui créer une couche à part avec la propriété CSS will-change: transform. Cette propriété est a appliquer sur le sélecteur de l’élément parent de la zone concernée.
Attention toutefois car créer une couche consomme de la mémoire, il faut que celle-ci soit justifiée.
Pour en revenir à l'enchaînement des étapes, celles-ci ne seront donc pas toujours toutes exécutées en fonction du changement à effectuer sur la page. On distingue 3 scénarios :
Le comportement de l’ensemble des propriétés CSS est disponible à cette adresse : https://csstriggers.com
On connaît maintenant notre métrique à mesurer, le FPS, et la façon dont le navigateur construit chaque frame. Notre objectif à présent va être de trouver des chutes de FPS pendant la navigation sur notre interface, de voir quelles phases le navigateur a effectué pendant ces chutes et enfin essayer d’établir une corrélation avec notre code.
Pour cela, on va utiliser l’onglet “Performance” des Chrome DevTools. Ouvrez votre navigateur en navigation privée et assurez-vous qu’il n’y pas d’extension activée pour ne pas avoir de bruits dans vos données. Activez également une limitation sur le CPU (4x slowdown) pour faire ressortir vos éventuels problèmes de performances. Ensuite, lancez l’enregistrement et effectuez des actions sur votre page (scroll, click, …).
Sur le résultat que vous allez obtenir, il y aura beaucoup d’informations affichées en même temps. On va se concentrer sur les plus importantes et voir comment les analyser. Prenons le site d’Octo comme exemple.
Vue global de l’onglet performance après un profiling |
Indicateurs importants après un profiling |
La première ligne correspond aux FPS évoqués précédemment. Les chutes de FPS provoquant un ralentissement visible par l’utilisateur sont normalement indiquées par des petits rectangles rouges au-dessus de la ligne. Ce sont ces zones que l’on va investiguer en priorité.
La deuxième ligne correspond à l’activité du CPU sur le pipeline de pixel évoqué également auparavant. Le code couleur est le même que sur la photo de la section précédente (jaune pour JavaScript, violet pour les phases de style et layout et enfin vert pour les phases de paint et composite).
En dessous vous avez la ligne qui correspond à chaque frame produite par le navigateur. En passant votre souris sur chaque frame vous allez pouvoir voir le temps qu’a pris celle-ci pour être produite. On rappelle que l’on s’attend à un temps de 16.7 ms environ pour être proche des 60 FPS. Si vous trouvez des frames plus longues que d’autres sur cette ligne, elles devraient correspondre à des chutes de FPS par rapport à la première ligne.
Enfin vous aurez la ligne “Main” pour Main Thread. Il s’agit d’un graphique appelé Flamechart qui représente la pile d’appels effectués par le thread principal pour le navigateur. Ce qui va nous intéresser sur ce diagramme, c’est en priorité les cases avec des rectangles rouges en haut à droite (voir image ci-dessous). En effet, le profiler est capable de détecter les appels qui semblent anormaux. On va également rechercher tout évènement anormalement long, un évènement avec une profondeur importante ou encore un déclenchement d’étapes du pipeline de pixel suspect.
Dans l’exemple ci-dessous, on va investiguer des problèmes mis en avant par le profiler. Ici, le profiler nous dit que des étapes du pipeline de pixel ont été déclenchées à tort. Ce problème s’appelle un forced reflow. Les étapes du pipeline de pixel se déroulent dans l’ordre de manière synchrone puis affichent la frame, il est néanmoins possible de déclencher l’étape de style ou de layout prématurément avec du JavaScript. Cela se produit généralement quand on modifie la taille d’un élément sur la page et qu’on essaye de faire une lecture de la nouvelle valeur juste après.
Waterfall du main thread | Résumé d’un événement par le profiler |
En allant vérifier la ligne de code mise en avant sur le profiler on retrouve bien la situation décrite ci-dessus. En effet, le code ci dessous va essayer de lire une valeur d’un élément qui a été modifié juste avant. Dans ce cas, un enfant de l’élément a été retiré, le navigateur doit donc refaire un layout pour pouvoir donner la valeur offsetHeight de celui-ci.
Code à l’origine d’un forced reflow |
À noter également qu'après avoir effectué un enregistrement avec le profiler, les temps d'exécution de votre code seront affichés à côté de chaque ligne. Cette information peut être très intéressante pour mettre en avant des problèmes de nature plus algorithmique.
Temps d'exécution dans les sources après un profiling |
Vous savez maintenant que les performances de rendu sont tout aussi importantes que les performances de chargement afin d’avoir la meilleure expérience utilisateur sur votre interface. Il faut également garder à l’esprit que toutes les analyses que vous allez faire le seront avec le CPU de votre machine, d’où l’intérêt de limiter celui-ci si vous êtes sur une machine puissante.
Récapitulatif des techniques que vous pouvez utilisez dès maintenant pour améliorer vos performances de rendu :
En espérant que le profiler vous effraie moins qu’avant. Bonne chance pour votre chasse aux bottlenecks !
https://developers.google.com/web/fundamentals/performance/rendering/
https://developers.google.com/web/tools/chrome-devtools/evaluate-performance/
https://developer.mozilla.org/fr/docs/Outils/Performance/Waterfall
https://developer.mozilla.org/fr/docs/Outils/Performance/Frame_rate
https://github.com/joshwcomeau/talk-2019