class: center, middle, cover # Un profileur, comment ça marche ? ## perf.html: un profileur pour Firefox --- # Qui suis-je ? .flex[ ![Julien](images/me.jpg) * Julien Wajsberg [@jwajsberg](https://twitter.com/jwajsberg) * à Mozilla depuis plus de 5 ans * développeur sur [perf.html](https://github.com/devtools-html/perf.html/) depuis plus d'un an ] --- # De quoi va-t-on parler ? 1. Qu'est-ce qu'un
sampling profiler
? 2. Les arbres d'appels (
Call Tree
) 3. Les markers 4. Analyser un profile --- # Un _sampling_ profiler * l'utilisateur démarre le profiler * on réalise un échantillonnage: chaque milliseconde on arrête Firefox et on regarde la stack courante (sur chaque thread) * lorsque l'utilisateur arrête le profiler, on capture l'ensemble ces mesures et on les rassemble dans une structure JSON: le profil * à partir de ces mesures et toutes ces stacks, on peut créer des visualisations, en particulier l'arbre d'appels. --- # Un petit exemple ? ```javascript function a() { b(); } function b() { c(); } function c() { doWork(); } function doWork() { for (let i = 0; i < 1e7; i += Math.random()) {} } a(); ``` --- # Un échantillon chaque milliseconde ``` 1ms 2ms 3ms 4ms 5ms 6ms ----- ----- ----- ----- ----- ----- a a a a a a ↓ ↓ ↓ ↓ ↓ ↓ b b b b b b ↓ ↓ ↓ ↓ ↓ ↓ c c c c c c ↓ ↓ ↓ ↓ ↓ ↓ doWork doWork doWork doWork doWork doWork ``` --- # Un arbre d'appels très simple ``` a (Running time: 6ms) (Self time: 0ms) ↓ b (Running time: 6ms) (Self time: 0ms) ↓ c (Running time: 6ms) (Self time: 0ms) ↓ doWork (Running time: 6ms) (Self time: 6ms) ``` --- # Affichage dans perf.html .center[ .with-caption[ ![Un arbre d'appels très simple](images/a-simple-call-tree.png) ] [Essayez pour de vrai !](examples/a-simple-call-tree.html) ] --- # Un autre exemple ? ```javascript function a() { b(); } function b() { c(); } function c() { for (let i = 0; i < 1e7; i += Math.random()) { doNothing(); } } function doNothing() { } a(); ``` --- # Un arbre encore plus simple ! ``` a (Running time: 6ms) (Self time: 0ms) ↓ b (Running time: 6ms) (Self time: 0ms) ↓ c (Running time: 6ms) (Self time: 6ms) ``` ??? On passe trop peu de temps dans doNothing, donc on ne le voit pas au moment où on arrête Firefox pour l'échantillonnage. C'est statistique: on peut en avoir un parfois, par chance. --- # Un exemple un peu différent .smaller[ ```javascript function a() { b(); } function b() { c(); } function c() { for (let i = 0; i < 1e7; i += Math.random()) { doWork(); } } function doWork() { for (let i = 0; i < 100; i += Math.random()) { } } a(); ``` ] --- # Cette fois, le même arbre qu'au début ``` a (Running time: 6ms) (Self time: 0ms) ↓ b (Running time: 6ms) (Self time: 0ms) ↓ c (Running time: 6ms) (Self time: 0ms) ↓ doWork (Running time: 6ms) (Self time: 6ms) ``` ??? Cette fois on passe assez de temps dans doWork. Un point à remarquer: même si on l'appelle plusieurs fois, on ne le voit qu'une fois dans l'arbre. --- # Un exemple plus complexe .smallest[ ```javascript function a() { b(); } function b() { c(); h(); } function c() { d(); f(); } function d() { e(); } function e() { for (let i = 0; i < 1e7; i += Math.random()) {} } function f() { for (let i = 0; i < 1e7; i += Math.random()) {} g(); } function g() { for (let i = 0; i < 1e7; i += Math.random()) {} } function h() { f(); } a(); ``` ] --- # Plus d'échantillons ! .center[ ![Les échantillons pour cet exemple](images/branched-samples.png) ] ??? À chaque milliseconde on a une nouvelle stack. --- # Un arbre avec des branches ! .center[ .with-caption[ ![Un arbre d'appels avec des branches](images/branched-call-tree.png) ] [Essayez par vous-mêmes !](examples/a-branched-call-tree.html) ] ??? On fusionne les fonctions pour créer une visualisation sous forme d'un arbre d'appels. --- # Regardons cet exemple de plus près .smallest[ ```javascript function a() { b(); } function b() { c(); h(); } function c() { d(); f(); } function d() { e(); } function e() { for (let i = 0; i < 1e7; i += Math.random()) {} } function f() { * for (let i = 0; i < 1e7; i += Math.random()) {} g(); } function g() { for (let i = 0; i < 1e7; i += Math.random()) {} } function h() { f(); } a(); ``` ] --- # On passe du temps dans `f` .center[ ![f est au bout d'une stack](images/branched-samples-with-selftime.png) ] --- # Le résultat en terme de timings ``` a (5ms / 0ms) ↓ b (5ms / 0ms) ↓---------------------------------- ↓ ↓ c (3ms / 0ms) h (2ms / 0ms) ↓----------------- ↓ ↓ ↓ ↓ d (1ms / 0ms) f (2ms / 1ms) f (2ms / 1ms) ↓ ↓ ↓ ↓ ↓ ↓ e (1ms / 1ms) g (1ms / 1ms) g (1ms / 1ms) ``` --- # Dans perf.html .center[ ![Capture de perf.html pour ce profil](images/branched-call-tree-in-perf.png) ] ??? Regardez particulièrement les "Running times": on passe clairement du temps dans `f`. --- # Inversion de l'arbre d'appels ## aussi appelé "Vue Bottom-up" .with-caption[ .center[ ![Capture de perf.html après inversion](images/branched-call-tree-in-perf-inverted.png) ] [Consulter ce profil en ligne](https://perfht.ml/2Jf40XV) ] ??? Inverser l'arbre permet de mettre en évidence les fonctions où on passe le plus de temps. --- # Une autre visualisation: le flame graph .center[ ![Le Flame Graph](images/flame-graph.png) ] --- # Chrome aussi a son flame graph .center[ ![Le Flame Graph de chrome](images/flame-graph-chrome.png) ] --- # Les markers .smaller[ * Puisqu'on contrôle le code de Gecko, on peut l'_instrumenter_ aux bons endroits. * Et les développeurs JS peuvent aussi _instrumenter_ leur code avec `performance.mark`. ] .center[.with-caption[ ![Marqueurs React](images/markers.png) [Voir ce profil en ligne](https://perfht.ml/2LbZxXN) ] ] --- # Dans Chrome aussi .center[![Marqueurs React dans Chrome](images/markers-chrome.png)] --- # Les markers et l'arbre d'appels * ils sont complémentaires * on a toujours tous les marqueurs (alors que l'arbre se base sur des échantillons statistiques) * on a des marqueurs pour les événements importants: reflows, garbage collection, événements DOM... * on a aussi un marqueur spécial pour quand la boucle d'événement est bloquée --- # Utiliser le Gecko Profiler ## un peu de pub... .smaller[ ### Solution 1 1. Allez sur https://perf-html.io 2. Installez l'extension proposée 3. Enregistrez à partir de la nouvelle icône ### Solution 2 1. Activez le nouveau panneau de performance depuis les paramètres des DevTools 2. Open the panneau _Performance_ des DevTools. 3. Contrôlez l'enregistrement depuis ce panneau. ] ??? La solution 2 souffre encore de l'overhead provoqué par les autres outils, mais c'est la seule solution pour pouvoir profiler sur mobile. --- # D'autres analyses * [mutations DOM](examples/dom-mutation.html) * [Garbage collection](examples/merge-sort.html) --- # Merci Vous pouvez toujours trouver cette présentation [en ligne sur github](https://github.com/julienw/webperf-meetup-2018-perf-html-talk) et même [la lire en ligne](https://julienw.github.io/webperf-meetup-2018-perf-html-talk/) Merci à tous mes collègues [Greg](https://twitter.com/TatumCreative/), [Markus](https://twitter.com/markusstange), [Ola](https://twitter.com/misprintedtype/), [Nazim](https://twitter.com/canaltinova) et j'en passe. _QUESTIONS ?_