Trotz des Wachstums der Geräteleistung wird das Web immer anspruchsvoller für Speicher und Prozessor. Das richtige Rendern und die intelligente Zuordnung von Ressourcen zu Registerkarten ist ein wichtiger Bestandteil bei der Lösung dieses Problems. Konstantin Kramlih
PurplePowder widmete seine Rede auf der
Konferenz „I Frontend“ Algorithmen, die die Produktivität verbessern und Ressourcen sowohl im Chromium-Projekt als auch in Yandex.Browser sparen.
Einige davon - zum Beispiel die Hibernate-Technologie - haben wir bereits in einem
separaten Beitrag sortiert. Der Bones-Bericht behandelt das Problem umfassender: Nicht nur unter dem Gesichtspunkt des Tabswechsels, sondern auch unter Berücksichtigung der Methoden zum Rendern von Inhalten, Kacheln und Seitenebenen.
Gegen Ende können Webinterface-Entwickler lernen, wie sie Leistungsprobleme bei Websites identifizieren und lösen können.
- Mein Name ist Kostya, ich bin der Leiter der internen Komponentenentwicklungsgruppe im Yandex.Browser-Team. Im Browser mache ich seit etwas mehr als fünf Jahren verschiedene Dinge: von der Dekodierung im Browser über alle HTML5-Videos bis hin zum Rendern, Rendern und ähnlichen Prozessen.
In den letzten eineinhalb bis zwei Jahren war ich an Projekten zum Einsparen von Ressourcen im Browser beteiligt: CPU, Speicher, Batterie.
Sie sagen - Kostya, auf dem Hof im Jahr 2019, wo sind die Probleme mit den Ressourcen? Sie können jedes gewünschte Gerät mit beliebigen Ressourcen kaufen. Wenn wir uns jedoch den offenen Mozilla-Statistiken zuwenden, werden wir feststellen, dass die Hälfte der Benutzer über 4 GB Speicher oder weniger verfügt. Und viele Benutzer mit einem oder zwei physischen Kernen machen einen erheblichen Teil Ihres Publikums aus. In dieser Welt leben wir.

Wie viele von Ihnen sehen diese Registerkarte oft? Dies ist genau das, was bei normalen Benutzern mit wenig RAM und alten Computern passiert.

Was zu tun ist? Das Problem ist für niemanden ein Geheimnis, sie haben vor etwa drei Jahren begonnen, aktiv damit zu kämpfen. Seitdem wurden die Muttern auf verschiedene Weise schrittweise angezogen. Lassen Sie mich Ihnen ein Beispiel zeigen, das sich um Stack Overflow um 2016 drehte. Hier ist ein ziemlich einfacher Ausschnitt. Dieses Ding aktualisiert nur den Titel und legt die Zeit fest, die seit dem letzten Start dieser Funktion vergangen ist. Was sollte idealerweise erhalten werden? Alle 100 ms im Titel sollte + -100 geschrieben werden. Wie viel Glück.

Aber was ist, wenn wir öffnen und dies tun? Ist jemand darauf gestoßen? Die Frage ging an Stack Overflow: Was zum Teufel funktioniert Cookie Clicker nicht mehr in meinen Hintergrundregistern? Dies war eine der ersten Chromium-Initiativen zur Reduzierung der CPU-Auslastung im Browser. Die Idee war, dass wenn der Benutzer die Registerkarte jetzt nicht verwendet, er sie jetzt nicht benötigt - lassen Sie uns JS darauf setzen.
Der Browser versucht, die CPU-Auslastung auf dieser Registerkarte bei etwa 1% zu halten. Er pausiert alle Arten von Timern, JS-Ausführung usw. Dies ist einer der ersten Schritte in der glänzenden Zukunft.

Nach einiger Zeit im Browser wird die Situation angezeigt, dass die Hintergrundregisterkarten überhaupt nicht mehr funktionieren. Dies ist die glänzende Zukunft, von der ich spreche. Nach den Plänen von Chromium, die sie beim letzten BlinkOn geäußert haben, planen sie dies im Jahr 2020: Lassen Sie die Registerkarte laden, und wenn es Hintergrund ist, wird es nichts tun. Darauf müssen Sie immer vorbereitet sein.

In Yandex.Browser haben wir uns ebenfalls um ein solches Problem gekümmert, es jedoch weniger kategorisch gelöst und nicht das gesamte Web beschädigt. Wir haben einen Energiesparmodus erstellt, der die Dekodierung auf dem Prozessor deaktiviert und nur die Dekodierung auf der Grafikkarte belässt. Außerdem werden die FPS gesenkt und einige Animationen deaktiviert, die derzeit nicht benötigt werden und stattdessen sollte der Benutzer besser Batterie sparen. Dies gab uns ungefähr eine Stunde zusätzliche Akkulaufzeit. Jeder kann prüfen, ixbt zum Beispiel
prüfen .

Ich denke, einige werden sagen: Kostya, Sie haben das Web "kaputt gemacht", einigen Benutzern geholfen, aber nichts Klügeres gefunden. Füge Hardcore hinzu! Wie zeichnen Browser Seiten?

Kurz gesagt, das Konzept der Ebenen besteht darin, dass der Browser versucht, die Seite in Ebenen aufzuteilen und diese separat zu zeichnen. Dies geschieht, damit einige Animationen ausgeführt werden und nicht erzwungen wird, statische Elemente neu zu zeichnen. Der Browser macht dies auf verschiedenen Heuristiken. Beispiel: Versuchen Sie, ein Videoelement in einer separaten Ebene auszuwählen, die offensichtlich schnell und häufig neu gezeichnet wird. Und wenn es irgendwo angezeigt wird, müssen Sie nicht alles darunter neu zeichnen.
Außerdem ist jede Ebene in solche Kacheln unterteilt - 256 x 256 Rechtecke. Im Inspektor sehen Sie so etwas. Es gibt einen Rahmen, der in mehrere Kacheln unterteilt ist.


Was ist das und warum wird es benötigt? Zunächst einmal, um das Rendern zu priorisieren. Wenn wir eine riesige Wurst haben, auf die wir alle zeichnen, warum müssen wir dann all dies zeichnen, wenn der Benutzer jetzt nur das sieht, was er jetzt in ViewPort hat?

Mit diesem Ansatz zeichnen wir zuerst nur das, was der Benutzer gerade in ViewPort sieht, dann eine Kachel herum und dann in Richtung der Schriftrolle. Wenn der Benutzer nach unten scrollt - nach unten, wenn nach oben - nach oben. Alles andere wird nur gezeichnet, wenn wir ein Kontingent für diese Kacheln haben und sie zeichnen können, wonach der Benutzer sie jemals sehen wird. Oder vielleicht nie.

Es hilft auch sehr bei Behinderungen. Angenommen, ein Benutzer öffnet eine Seite, wählt ein Stück aus und wir müssen nicht alles neu zeichnen. Wir können den größten Teil des vorherigen Renderings verlassen. Wir werden hier sechs Kacheln neu zeichnen, und alles wird gut.

Gerade auf dieser Ebene wurden mehrere sehr erfolgreiche Optimierungen vorgenommen. Zum Beispiel hat Chromium eine solche Optimierung um 2017 vorgenommen.

Wenn wir nur ein kleines Rendering haben, machen wir es nur. Dann blinkt der Cursor und wir zeichnen nur den Cursorbereich neu, aber nicht den gesamten Bereich dieser Kachel. Wir sparen sehr viel CPU, um nicht alles neu zu zeichnen.

Es hilft auch, Speicherplatz zu sparen. Was ist das Problem hier? Ganze weiße Rechtecke. Stellen Sie sich vor, es wäre eine 256 x 256-Textur, vier Bytes pro Pixel. Obwohl es scheint, dass dieser Bereich mit nur fünf Zahlen codiert werden kann: Koordinaten, Breite, Höhe und Farbe.

In Chrom wurden monochrome Bereiche optimiert. Wenn der Browser versteht, dass dieses Rechteck kein Rendering enthält, dass es vollständig monochrom und nicht transparent ist und einige andere Bedingungen erfüllt, teilen wir der Grafikkarte einfach mit: Zeichnen Sie ein weißes Rechteck, wählen Sie nicht die gesamte Textur aus.
Was kann hier noch optimiert werden? Wenn Sie sich die verbleibenden Kacheln ansehen, haben sie einen kleinen Inhalt und einen großen weißen Bereich. Bei Yandex.Browser haben wir darüber nachgedacht und einen Mechanismus entwickelt, den wir als adaptives Kacheln bezeichnen.

Es gibt ein kleines Rechteck, eine Kachel. In der Mitte der Kachel befindet sich wenig Inhalt. Wir wählen es und - nur darunter - Textur. Alles andere ist auch in mehrere Bereiche unterteilt, von denen wir über die Grafikkarte sprechen: Zeichnen Sie hier einfach Weiß in dieser Größe.

Die Seite beginnt auch, alles zu speichern, was rot hervorgehoben ist. Auf komplexeren Seiten sieht es ungefähr so aus.

Es ist wichtig zu verstehen, dass es immer noch eine Reihe von Ebenen gibt und jede Ebene so gezeichnet wird. Auf jeder Ebene können Sie etwas Speicherplatz sparen. Mit diesem Ansatz konnten wir durchschnittlich 40% des Videospeichers für alle Benutzer einsparen.
Mehr Hardcore! Sie haben hier ein wenig Erinnerung gespeichert, sie haben das Web "gebrochen" - warum nicht das Web weiter "brechen"?
In Chromium gibt es so etwas wie eine Richtlinie: Wenn der Benutzer keine Hintergrundregisterkarten verwendet, diese verlassen hat, benötigt er sie nicht. Wenn wir jetzt keinen Speicher mehr haben und der Browser sich jetzt hinlegt, nehmen wir die älteste Registerkarte, die der Benutzer seit langem nicht mehr verwendet hat, und beenden sie. Sie wird in der Schnittstelle bleiben, aber der Prozess wird nicht mehr da sein, alle JS werden sterben. Ist es in Ordnung oder nicht in Ordnung? Es ist seltsam, eine solche Frage auf der Front-End-Party zu stellen und eine andere Antwort als "Was machst du?" Zu erwarten.

Dann ging es nicht viel. Hier sind die wirklichen Kommentare aus dem Chromium-Blog: Sie haben alle meine Anwendungen für mich gebrochen, es gab eine Art Spiel - und Hop, es hat keinen Status. Es ist wichtig zu verstehen, dass dort der Entladehandler nicht funktioniert hat, als hätten wir einfach diese Registerkarte gelöscht. Der Benutzer kehrt dann dorthin zurück und wir laden es aus dem Netzwerk neu, als wäre nichts passiert.
Dann wurde dieser Ansatz vorübergehend aufgegeben und kam auf eine nachdenklichere ernsthafte Idee. Sie nannten sie wegwerfen.

Was ist der Punkt? Dies ist das gleiche Töten von Tabs, nur kontrolliert. Es wird als Smart Word
Page Lifecycle API bezeichnet . Wenn Sie eine Registerkarte haben und der Benutzer sie lange nicht gesehen hat, kann sie in den eingefrorenen Zustand versetzt werden. Der Browser sagt während des Ereignisses: Ich werde Sie jetzt einfrieren. Nach der Verarbeitung des Ereignisses wird überhaupt nichts ausgeführt. Mach was du brauchst, mach dich bereit.
Dann kann es entweder den eingefrorenen Zustand durch das Wiederaufnahmeereignis verlassen, angeblich ist nichts passiert. Oder wenn der Browser jetzt wirklich Speicher freigeben muss, nimmt er ihn einfach und beendet ihn. Wenn der Benutzer jedoch zu dieser Registerkarte zurückkehrt, wird sie neu geladen und das Feld "Wurde verworfen" für das Dokument festgelegt.

Im Moment können Sie diese Ereignisse bereits abonnieren und sie irgendwie abfangen. Wenn die Registerkarte wirklich beendet ist, können Sie überprüfen, ob das Feld verworfen wurde. Dies bedeutet, dass Sie nach einem Verwerfen wiederhergestellt wurden. Sie können den vorherigen Status wiederherstellen.

Wir bei Yandex.Browser haben vor einigen Jahren gedacht: Warum nicht einen komplexen Kardinalansatz verwenden? Sie nannten ihn Winterschlaf.

Was ist der Punkt? Es gibt mehrere Registerkarten, eine Art JS wird ausgeführt, eine Art Status. Für jede Registerkarte wird ein separater Prozess erstellt: Hier kann ein Video abgespielt werden, hier belassen Sie etwas im Formular. Der Ruhezustand kommt an - und es gibt keine Prozesse. Wir haben sie alle. Wenn wir jetzt zu diesen Registerkarten zurückkehren, wird der Vorgang fortgesetzt und der gesamte Status wird angezeigt. Das Video wird ab dem richtigen Moment weiter abgespielt. Der gesamte Text in den Feldern bleibt erhalten.

Was haben wir getan In jedem Renderer befinden sich drei wichtige Dinge: V8, in dem der gesamte JS ausgeführt wird, Blink, in dem das gesamte DOM gespeichert ist, und eine Art Browserbindung, mit der alles miteinander verbunden werden kann, mit Registerkarten und allem anderen.

Betrachten Sie zum Beispiel ein Beispiel. Hier warten wir auf das Laden und fügen dem DOM-Baum ein neues div-Element hinzu. Für den Browser sieht es ungefähr so aus.

Natürlich gibt es einen DOM-Baum, der einige Felder und zugehörige Objekte enthält, und es gibt eine solche Entität.

In V8 wird der Status jedes Knotens gespeichert, und diese Knoten werden durch eine Schicht von Bindemitteln mit Blinkobjekten verknüpft. Was haben wir getan Wir haben den Serializer von V8 übernommen, den gesamten V8-Status serialisiert, alle zugehörigen Objekte in Blink gefunden, plus Serializer geschrieben, die den gesamten DOM-Baum speichern, serialisieren und dann auf die Festplatte schreiben, komprimiert und verschlüsselt. Und wir haben dem Browser beigebracht, sich von einem solchen Schnappschuss zu erholen. Das heißt, wenn der Benutzer zu einer solchen Registerkarte wechselt, erweitern wir sie, entschlüsseln sie und zeigen sie dem Benutzer an, stellen sie vollständig wieder her. (
Ein separater Beitrag über den Ruhezustand - ungefähr.)
Im Moment ist Hibernate für alle im Stall veröffentlicht und ermöglicht es jedem Benutzer, durchschnittlich ein oder zwei Registerkarten zu speichern. Das heißt, er hat im Durchschnitt immer einen Tab gespeichert, oder vielleicht zwei. Dies spart Speicherplatz für Benutzer mit mehr als 10 Registerkarten - wie wir es bei Ihnen tun, aber wir sind nicht repräsentativ.
Ich habe erzählt, wie der Browser versucht zu helfen, aber jetzt kann jeder von Ihnen etwas tun, um die Website zu beschleunigen und ihre Leistung zu verbessern. Sie können heute kommen und es richtig machen.
Zuerst müssen Sie verstehen, ob es Speicherprobleme gibt.

Es gibt bestimmte Symptome: Entweder beginnt sich die Stelle zu verschlechtern, oder es tritt ein Verblassen auf. Normalerweise bedeutet dies, dass der Garbage Collector ausgelöst wird, die ganze Welt eingefroren ist und nichts gezeichnet wird. Oder dass die Seite einfach ständig langsamer wird - so passiert es auch.
Sie müssen verstehen, wenn es ein Problem gibt. Wir schauen uns an, was mit JS-Speicher passiert.

Wenn es hin und her springt oder kontinuierlich wächst, ist dies kein gutes Symptom. Oder kontinuierlich wachsen.

In diesem Fall können Sie jederzeit einen Schnappschuss über DevTools erstellen. Hat sich jemand überhaupt mit Lecks in JS beschäftigt? Wenn jemand es nicht weiß, ist es in JS durchaus möglich, ein Leck zu machen.

Beispielsweise haben Sie eine globale Variable, in der Sie Knoten hinzufügen, die nicht in den Baum eingefügt werden. Sie vergessen sie und dann stellt sich heraus, dass sie Hunderte von Kilobyte oder sogar Megabyte essen.

Genau genommen ist nicht klar, was mit solchen getrennten Knoten zu tun ist. Sie können sie aus dem Baum ziehen und dann wieder einsetzen. Denn normalerweise bleibt der Zustand des Bildes oder etwas anderes bei ihnen. So können Sie sie finden und das Problem behandeln.

Sie können sich auch die Speicherzuordnung auf der Timeline ansehen. Wenn Sie systematisch entlassen werden und nie aufräumen, ist dies auch ein schlechtes Zeichen. Ich denke, jeder versteht das.

Und Sie können mit den Ebenen auf Ihrer Registerkarte spielen. Der Browser arbeitet in dieser Hinsicht im Allgemeinen mit Heuristiken - er versucht, Objekte zu trennen, die er häufig in separate Ebenen ändert. Aber manchmal funktioniert es nicht sehr gut, und es stellt sich heraus, dass Sie viele Ebenen haben, die viel Speicher verbrauchen. Sie können immer sehen, ob Sie eine solche Situation haben, einige Ebenen entfernen und herausfinden, wie viel Speicher diese Ebene belegt.

Eine weitere Problemschicht ist die Leistung. setTimeout ist eine schlechte Idee für Animationen. Es funktioniert nicht so, wie der Browser die Seite rendern möchte. Möglicherweise funktioniert es nicht in der Phase: Der Browser hat einen Frame angefordert, aber es gibt noch keine Animation, da setTimeout nicht funktioniert hat. Oder es kann auch funktionieren, wenn Sie nichts zeichnen müssen. Der Benutzer ist gegangen, es bleiben noch einige Hintergrundarbeiten, aber wir werden mit ihm über setTimeout arbeiten.
Hier ist der richtige Ansatz, den Ihr Rückruf nur dann aufruft, wenn der Browser diese Seite zeichnen muss.

Ich möchte Sie auch daran erinnern, dass wenn der Browser 60 Bilder pro Sekunde zeichnen möchte, dies bedeutet, dass er für jedes Bild ungefähr 16 ms benötigt.

Und wenn Sie den Hauptstrom länger als 16 ms belegen, haben Sie einen garantierten Frame-Skip, was für den Benutzer eher unangenehm ist: Die Site beginnt zu ruckeln. Daher wird die ganze harte Arbeit korrekt in die Hintergrund-Threads eingefügt, die Sie einfach als Ergebnis zurückgeben.

Oder ein anderer Ansatz ist die Verwendung von Mikrotasking. Erstellen Sie eine Aufgabenwarteschlange, verarbeiten Sie sie nach einiger Zeit, bis das Kontingent abgelaufen ist, und lassen Sie den Browser die Seite ruhig zeichnen.

Natürlich können Sie neue Ebenen hinzufügen. Der Browser arbeitet mit Heuristiken und versucht, die Ebene auszuwählen, auf der er dies für erforderlich hält. Wenn Sie jedoch wissen, dass dieses Element jetzt animiert wird, sollten Sie dem Browser ausdrücklich mitteilen, dass Sie dieses Element in einer separaten Ebene auswählen müssen. Dann wird es effizienter gezeichnet.

Der neueste interessante Ansatz - beschweren. Wenn Sie Probleme haben, ist es immer hilfreich, zu kommen und zu sprechen. Vielleicht kann dieses Problem leicht behandelt werden, und wir werden uns gegenseitig helfen.
Ein kleiner Hintergrund. Diese Funktion wird derzeit ausgearbeitet. Wir haben ein Team für Suchmaschinenentwicklungsseiten. Sie haben eine Metrik wie den Zeitpunkt des Renderns des ersten Snippets. Wenn der Benutzer zuvor den ersten Link sieht, auf den Sie klicken müssen und der ihm höchstwahrscheinlich die richtige Antwort gibt, ist es viel besser, ihn so schnell wie möglich zu zeichnen. Sie kamen und fragten, ob es diesmal möglich sei, etwas zu beschleunigen, weil sie alle herausgequetscht waren.
Wir haben nachgeforscht, Perfektionstests durchgeführt, einen Prototyp erstellt, und es stellte sich heraus, dass wir dies tun können, wenn die Site uns sagt, was wir zuerst zeichnen sollen. Wir haben getestet und festgestellt, dass dies unsere Leistung verbessert. Jetzt testen wir dies in der Produktion für ein kleines Publikum. Wir schauen, was daraus wird. Bleib dran.
Die Welt steht nicht still. Der Benutzer beginnt immer mehr zu wollen, Browser ändern sich, das Web ändert sich. Es ist in Ordnung. Wir versuchen nicht nur, Funktionen für Lebensmittel zu entwickeln, sondern dem Benutzer auch auf jede erdenkliche Weise zu helfen, die bestmögliche Erfahrung hinsichtlich des Inhaltsverbrauchs zu erzielen. Und Sie müssen immer auf Änderungen in einem gängigen Browser vorbereitet sein. Wenn es Probleme gibt, kommen wir natürlich gerne miteinander und helfen uns gegenseitig.
Hier habe ich verschiedene nützliche Links zusammengestellt: