Comment économiser des ressources dans le navigateur et ne pas casser le Web. Rapport Yandex

Malgré la croissance des performances des appareils, le Web devient de plus en plus exigeant en mémoire et en processeur. Un rendu correct et une allocation intelligente des ressources entre les onglets est un élément important pour résoudre ce problème. Konstantin Kramlih PurplePowder a consacré son discours à la conférence «I Frontend» aux algorithmes qui améliorent la productivité et économisent des ressources à la fois dans le projet Chromium et dans Yandex.Browser.

Certains d'entre eux - par exemple, la technologie Hibernate - nous les avons déjà triés dans un article séparé . Le rapport Bones couvre le problème plus largement: non seulement du point de vue de la commutation des onglets, mais également en tenant compte des méthodes de rendu du contenu, des tuiles et des couches de page.

Vers la fin, les développeurs d'interfaces Web peuvent apprendre à identifier et à résoudre les problèmes de performances des sites Web.


- Je m'appelle Kostya, je suis le chef du groupe de développement des composants internes de l'équipe Yandex.Browser. Dans le navigateur, je fais différentes choses depuis un peu plus de cinq ans: du décodage dans le navigateur, de toutes les vidéos HTML5 au rendu, au rendu et à d'autres processus similaires.

Depuis un an et demi à deux ans, je participe à des projets d'économie de ressources dans le navigateur: CPU, mémoire, batterie.

Vous dites - Kostya, dans la cour en 2019, où sont les problèmes de ressources? Vous pouvez acheter n'importe quel appareil que vous voulez avec toutes les ressources. Mais si nous nous tournons vers les statistiques ouvertes de Mozilla, nous verrons que la moitié des utilisateurs ont 4 Go de mémoire ou moins. Et de nombreux utilisateurs qui ont un ou deux cœurs physiques, ils constituent une proportion considérable de votre audience. Dans ce monde, nous vivons.



Combien d'entre vous voient souvent cet onglet? C'est exactement ce qui se passe dans le cas des utilisateurs ordinaires qui ont peu de RAM et de vieux ordinateurs.



Que faire Le problème n'est un secret pour personne, ils ont commencé à lutter activement contre lui il y a environ trois ans. Depuis lors, les écrous ont été progressivement resserrés de diverses manières. Permettez-moi de vous montrer un exemple qui a contourné Stack Overflow vers 2016. Voici un extrait assez simple. Cette chose met à jour le titre et définit le temps écoulé depuis le dernier lancement de cette fonction. Que faut-il obtenir idéalement? Toutes les 100 ms dans le titre doivent être écrites + -100. Quelle chance.



Mais que faire si nous ouvrons et faisons cela? Quelqu'un a-t-il rencontré cela? La question est allée à Stack Overflow: qu'est-ce qui fait que Cookie Clicker cesse de fonctionner dans mes onglets d'arrière-plan? Ce fut l'une des premières initiatives Chromium visant à réduire l'utilisation du processeur dans le navigateur. L'idée était que si l'utilisateur n'utilisait pas l'onglet maintenant, il n'en avait pas besoin maintenant - mettons JS dessus.

Le navigateur essaie de maintenir la charge du processeur sur cet onglet à environ 1% - il commence à suspendre toutes sortes de minuteries, l'exécution de JS, etc. C'est l'une des premières étapes d'un avenir radieux.



Après un certain temps dans le navigateur, vous obtiendrez une situation où les onglets d'arrière-plan cesseront de fonctionner. C'est le brillant avenir dont je parle. Selon les plans de Chromium, qu'ils ont exprimés lors du dernier BlinkOn, en 2020, ils prévoient de le faire: laissez l'onglet se charger et s'il est en arrière-plan, il ne fera rien. Vous devez toujours vous y préparer.



Dans Yandex.Browser, nous avons également pris soin d'un tel problème, mais nous l'avons résolu de manière moins catégorique et n'avons pas cassé l'ensemble du Web. Nous avons créé un mode d'économie d'énergie qui désactive le décodage sur le processeur et ne laisse que le décodage sur la carte vidéo, et réduit également le FPS et désactive certaines animations qui ne sont pas nécessaires pour le moment et au lieu desquelles l'utilisateur doit économiser la batterie. Cela nous a donné environ une heure d'autonomie supplémentaire. Tout le monde peut vérifier, ixbt, par exemple, vérifié .



Je pense que certains diront: Kostya, vous avez «cassé» le Web, aidé certains utilisateurs, mais n’avez rien trouvé de plus intelligent. Ajoutez du hardcore! Comment les navigateurs dessinent-ils des pages?



Le concept de couches, en bref, est lorsque le navigateur essaie de diviser la page en couches et de les dessiner séparément. Ceci est fait pour que certaines animations soient effectuées et ne forcent pas à redessiner ce qui est statique. Le navigateur le fait sur différentes heuristiques. Par exemple - essayer de sélectionner un élément vidéo dans un calque séparé, qui, évidemment, se redessine rapidement et souvent. Et s'il est affiché quelque part, vous n'avez pas besoin de tout redessiner en dessous.

De plus, chaque couche est divisée en de telles tuiles - des rectangles de 256 par 256. Dans l'inspecteur, vous pouvez voir quelque chose comme ça. Il y a un cadre qui est divisé en un tas de tuiles.





Qu'est-ce que c'est et pourquoi est-il nécessaire? Tout d'abord, pour prioriser le rendu. Si nous avons une énorme saucisse, sur laquelle nous dessinons tous, alors pourquoi devons-nous dessiner tout cela si maintenant l'utilisateur ne voit que ce qu'il a maintenant dans ViewPort?



En utilisant cette approche, nous dessinons d'abord uniquement ce que l'utilisateur voit en ce moment dans ViewPort, puis une tuile autour, puis dans le sens du défilement. Si l'utilisateur fait défiler vers le bas - tirer vers le bas, si vers le haut. Tout le reste ne sera dessiné que si nous avons un quota pour ces tuiles et que nous pouvons les dessiner, après quoi l'utilisateur les verra jamais. Ou peut-être jamais.



Cela aide également beaucoup de personnes handicapées. Supposons qu'un utilisateur ouvre une page, sélectionne un morceau et que nous n'ayons pas besoin de tout redessiner. Nous pouvons laisser la plupart du rendu précédent. Nous redessinerons six tuiles ici, et tout ira bien.



Juste à ce niveau, plusieurs optimisations très réussies ont été faites. Par exemple, Chromium a réalisé une telle optimisation vers 2017.



Si nous n'avons qu'un petit rendu, nous le faisons seulement. Ensuite, le curseur clignote et nous redessinons uniquement la zone du curseur, mais pas toute la zone de cette tuile. Nous économisons beaucoup de CPU pour ne pas tout redessiner.



Cela permet également d'économiser de la mémoire. Quel est le problème ici? Rectangles blancs entiers. Imaginez que ce soit une texture de 256 x 256, quatre octets par pixel. Bien qu'il semble que cette zone puisse être codée avec seulement cinq chiffres: coordonnées, largeur, hauteur et couleur.



Dans Chrome, l'optimisation des zones monochromes a été effectuée. Si le navigateur comprend qu'il n'y a pas de rendu dans ce rectangle, qu'il est complètement monochrome, non transparent et remplit d'autres conditions, alors nous disons simplement à la carte vidéo - dessinez un rectangle blanc, ne sélectionnez pas la texture entière.

Quoi d'autre peut être optimisé ici? Si vous regardez les tuiles restantes - elles ont un peu de contenu et une énorme zone de couleur blanche. Chez Yandex.Browser, nous y avons réfléchi et créé un mécanisme que nous avons appelé le carrelage adaptatif.



Il y a un petit rectangle, une tuile. Il y a peu de contenu au milieu de la tuile. Nous le sélectionnons et - seulement en dessous - la texture. Tout le reste est également divisé en plusieurs zones, dont nous parlons de la carte vidéo: il suffit de dessiner en blanc ici cette taille.



La page commence également à enregistrer tout ce qui est surligné en rouge. Sur des pages plus complexes, cela ressemble à ceci.



Il est important de comprendre qu'il existe encore un tas de couches et que chaque couche est dessinée comme ceci. Sur chaque couche, vous pouvez économiser une certaine quantité de mémoire. Cette approche nous a permis d'économiser environ 40% de mémoire vidéo en moyenne pour tous les utilisateurs.

Plus hardcore! Ils ont sauvé un peu de mémoire ici, ils ont «cassé» le Web - pourquoi ne pas «briser» encore le Web?

Dans Chromium, il y a quelque chose comme une politique: si l'utilisateur n'utilise pas d'onglets d'arrière-plan, s'il les a laissés, alors il n'en a pas besoin. Si nous sommes maintenant sans mémoire et que le navigateur est en train de perdre du temps, prenons l'onglet le plus ancien que l'utilisateur n'a pas utilisé depuis longtemps et supprimons-le. Elle restera dans l'interface, mais le processus ne sera plus là, tous les JS mourront. Est-ce que ça va ou pas? Il est étrange de poser une telle question à la partie frontale et d'attendre une réponse autre que "Que faites-vous?"



Ensuite, cela ne s'est pas beaucoup passé. Voici les vrais commentaires du blog Chromium: vous avez cassé toutes mes applications pour moi, il y avait une sorte de jeu - et hop, il n'a pas d'état. Il est important de comprendre que le gestionnaire de déchargement n'a pas fonctionné, comme si nous avions simplement mis cet onglet. L'utilisateur y revient ensuite, et nous le rechargeons depuis le réseau, comme si de rien n'était.

Ensuite, cette approche a été temporairement abandonnée et a proposé une idée sérieuse plus réfléchie. Ils l'ont appelée défausse.



À quoi ça sert? Il s'agit du même abattage d'onglets, uniquement contrôlé. C'est ce qu'on appelle le mot intelligent Page Lifecycle API . Si vous avez un onglet et que l'utilisateur ne l'a pas vu depuis longtemps, il peut passer à l'état figé. Le navigateur dit à travers l'événement: je vais vous geler maintenant. Après le traitement de l'événement, rien ne sera effectué du tout. Faites ce dont vous avez besoin, préparez-vous.

Ensuite, il peut soit quitter l'état gelé via l'événement de reprise, mais rien ne s'est passé. Ou, si le navigateur a vraiment besoin de libérer de la mémoire maintenant, il suffit de le tuer. Mais si l'utilisateur revient dans cet onglet, nous le rechargerons et définirons le champ a été ignoré pour le document.



En ce moment, vous pouvez déjà vous abonner à ces événements et les attraper, en quelque sorte les traiter. Si l'onglet est vraiment détruit, vous pouvez vérifier le champ a été supprimé. Cela signifie que vous avez été restauré après une défausse. Vous pouvez restaurer l'état précédent.



Chez Yandex.Browser, nous avons pensé il y a quelques années: pourquoi ne pas utiliser une approche cardinale complexe. Ils l'ont appelé Hibernate.



À quoi ça sert? Il existe plusieurs onglets, une sorte de JS est en cours d'exécution, une sorte d'état. Un processus distinct est créé pour chaque onglet: ici une vidéo peut être lue, ici vous laissez quelque chose dans le formulaire. Hibernate arrive - et il n'y a aucun processus. Nous les avons tous. Mais si nous revenons maintenant à ces onglets, le processus reprendra et tout l'état sera en place, la vidéo continuera à jouer au bon moment, tout le texte dans les champs restera en place.



Qu'avons-nous fait? Trois choses les plus importantes vivent à l'intérieur de chaque rendu: V8, qui exécute l'intégralité du JS, Blink, qui stocke l'intégralité du DOM, et une sorte de liaison de navigateur, qui aide à tout connecter ensemble, avec des onglets et tout le reste.



Par exemple, considérons un échantillon. Ici, nous attendons que l'onload se produise et ajoutons un nouvel élément div à l'arborescence DOM. Pour le navigateur, cela ressemble à ceci.



Naturellement, il y a un arbre DOM, il a des champs, des objets associés et il y a une telle entité.



Dans V8, l'état de chaque nœud est stocké et ces nœuds sont associés à des objets clignotants via une couche de liants. Qu'avons-nous fait? Nous avons pris le sérialiseur de la V8, sérialisé la totalité de l'état du V8, trouvé tous les objets associés dans Blink, écrit plus des sérialiseurs qui enregistrent la totalité de l'arborescence DOM, la sérialisent, puis écrivent sur le disque, compressés et chiffrés. Et nous avons appris au navigateur à récupérer à partir d'un tel instantané. Autrement dit, lorsque l'utilisateur accède à un tel onglet, nous le développons, le déchiffrons et le montrons à l'utilisateur, le restaurons complètement. ( Un article séparé sur Hibernate - environ.)

À l'heure actuelle, Hibernate est publié pour tout le monde dans stable et permet à chaque utilisateur d'enregistrer en moyenne un ou deux onglets. Autrement dit, il a en moyenne un onglet toujours enregistré, ou peut-être deux. Cela économise de la mémoire pour les utilisateurs qui ont plus de 10 onglets - comme nous le faisons avec vous, mais nous ne sommes pas représentatifs.

J'ai expliqué comment le navigateur essayait d'aider, mais maintenant chacun de vous peut faire quelque chose pour accélérer le site, améliorer ses performances. Vous pouvez venir bien faire aujourd'hui.

Vous devez d'abord comprendre s'il y a des problèmes de mémoire.



Il existe certains symptômes: soit le site commence à se dégrader, soit une décoloration apparaît. Habituellement, cela signifie que le garbage collector est déclenché, le monde entier est gelé et rien n'est dessiné. Ou que le site ralentit tout simplement constamment - cela arrive aussi.

Vous devez comprendre s'il y a un problème. Nous regardons ce qui se passe avec la mémoire JS.



Si elle saute d'avant en arrière ou croît continuellement, ce n'est pas un bon symptôme. Ou en croissance continue.



Dans ce cas, vous pouvez toujours prendre un instantané via DevTools. Quelqu'un a-t-il même dérangé des fuites dans JS? Si quelqu'un ne sait pas, dans JS, il est tout à fait possible de faire une fuite.



Par exemple, vous avez une variable globale dans laquelle vous ajoutez des nœuds qui ne sont pas insérés dans l'arborescence. Vous les oubliez, puis il s'avère qu'ils mangent des centaines de kilo-octets, voire des mégaoctets.



À strictement parler, on ne sait pas quoi faire avec ces nœuds détachés. Vous pouvez les retirer de l'arbre, puis les réinsérer. Parce que généralement l'état de l'image ou quelque chose d'autre leur reste. Ainsi, vous pouvez les trouver et traiter le problème.



Vous pouvez également consulter l'allocation de mémoire sur la chronologie. Si vous avez une décharge systématique et ne nettoyez jamais, c'est aussi un mauvais signe, je pense que tout le monde le comprend.



Et vous pouvez jouer avec les calques de votre onglet. Le navigateur fonctionne généralement avec des heuristiques à cet égard - il essaie de séparer les objets qu'il considère fréquemment changer en couches distinctes. Mais parfois, cela ne fonctionne pas très bien, et il s'avère que vous avez beaucoup de couches qui consomment beaucoup de mémoire. Vous pouvez toujours voir si vous avez une telle situation, éliminer certaines couches et savoir combien de mémoire cette couche occupe.



Une autre couche de problèmes est la performance. setTimeout est une mauvaise idée pour l'animation. Cela ne fonctionne pas comme le navigateur souhaite afficher la page. Cela peut ne pas fonctionner en phase: le navigateur a demandé une image, mais il n'y a pas encore d'animation, car setTimeout n'a pas fonctionné. Ou cela peut fonctionner même lorsque vous n'avez pas besoin de dessiner quoi que ce soit. L'utilisateur est parti, il reste encore un peu de travail en arrière-plan, mais nous travaillerons avec lui via setTimeout.

Voici l'approche correcte que votre rappel n'appellera que lorsque le navigateur devra dessiner cette page.



Je tiens également à vous rappeler que si le navigateur souhaite dessiner 60 images par seconde, cela signifie que pour chaque image, il lui faut environ 16 ms.



Et si vous occupez le flux principal plus de 16 ms, vous avez alors un saut de trame garanti, ce qui est plutôt désagréable pour l'utilisateur: le site commence à se branler. Par conséquent, tout le travail acharné correctement mis dans les threads d'arrière-plan, dont vous retournez simplement le résultat.



Ou une autre approche consiste à utiliser le microtâche. Créez une file d'attente de tâches, traitez-la après un certain temps, jusqu'à ce que le quota soit dépassé, et laissez le navigateur dessiner calmement la page.



Naturellement, vous pouvez ajouter de nouveaux calques. Le navigateur fonctionne sur l'heuristique et essaie de sélectionner la couche où il le juge nécessaire. Mais si vous savez qu'en ce moment cet élément sera animé, il est préférable de dire explicitement au navigateur que vous devez sélectionner cet élément dans un calque séparé. Ensuite, il sera dessiné plus efficacement.



La dernière approche intéressante - se plaindre. Si vous avez des problèmes, il est toujours utile de venir parler. Peut-être que ce problème peut être facilement traité, et nous nous entraiderons.

Un peu de fond. Cette fonctionnalité est en cours d'élaboration. Nous avons une équipe de pages de développement de moteurs de recherche. Ils ont une métrique telle que le moment du rendu du premier extrait. Si l'utilisateur voit précédemment le premier lien sur lequel vous devez cliquer et qui lui donne probablement la bonne réponse, il est préférable de le dessiner le plus rapidement possible. Ils sont venus et ont demandé s'il était possible d'accélérer d'une manière ou d'une autre cette fois, car ils s'étaient tous évincés.

Nous avons enquêté, effectué des tests de performance, réalisé un prototype et il s'est avéré que nous pouvons le faire si le site nous dit quoi dessiner en premier. Nous avons testé et constatons que cela améliore nos performances. Maintenant, nous testons cela en production pour un petit public. Nous regardons ce qui en sortira. Restez à l'écoute.

Le monde ne reste pas immobile. L'utilisateur commence à en vouloir de plus en plus, les navigateurs changent, le web change. C'est normal. Nous essayons non seulement de créer des fonctionnalités d'épicerie, mais aussi d'aider l'utilisateur de toutes les manières possibles à obtenir la meilleure expérience en termes de consommation de contenu. Et vous devez toujours être préparé aux changements dans n'importe quel navigateur populaire. Naturellement, s'il y a des problèmes - venez, nous sommes prêts à discuter et à nous entraider.

Ici, j'ai rassemblé divers liens utiles:

Source: https://habr.com/ru/post/fr442456/


All Articles