HeadlinesBriefing favicon HeadlinesBriefing.com

Browser-Hauptthread-Leistung Optimierung

Hacker News •
×

Was kommt Ihnen in den Sinn, wenn Sie „Frontend-Optimierung“ hören? Für die meisten von uns sind es Dinge wie die Reduzierung von Netzwerkanfragen, das Verkleinern des Bundles oder die gute Nutzung des Caches. Darüber hinaus vielleicht die Reduzierung von Re-Renders oder das Timing beim Laden von Ressourcen. Der Hauptthread kommt normalerweise nicht zur Sprache, und dafür gibt es einen Grund: Auf den meisten Bildschirm wird er nie zum Problem.

Aber auf Bildschirmen mit viel Interaktion, wo Daten live hereinströmen und Scrollen, Animation und Eingabe alle miteinander verknüpft sind, ändert sich das Bild. Wie viel Sie auch an Netzwerk und Bundle-Größe sparen, der Bildschirm friert in dem Moment ein, in dem der Hauptthread blockiert wird. Sie sind wahrscheinlich schon auf eine Website gestoßen, wo das Scrollen ab und zu ruckelt, ein Button leicht verzögert reagiert oder die Buchstaben, die Sie in ein Suchfeld tippen, einen halben Schlag hinterherhinken.

Es ist nicht schlimm genug, um ärgerlich zu sein, aber es geht einem subtil auf die Nerven. Genau diese Art von Jank ist das, was ein blockierter Hauptthread aussieht. Wenn wir als Entwickler auf solchen Jank stoßen, ist die übliche Reaktion, sich zu fragen „ist mein Code langsam?“ und anzufangen, Algorithmen auseinanderzunehmen oder nach verschwendeter Berechnung zu suchen.

In den meisten Fällen ist jedoch die Geschwindigkeit des Codes nicht das Problem. Der Code ist nicht langsam. Er ist einfach der Code, der den Hauptthread besetzt.

Der Browser hat mehrere Threads, aber fast alles, was wir vom Code aus anfassen können, ist auf dem Hauptthread konzentriert. Berechnung, Rendering, Event-Handling, Netzwerk-Antwort-Handling und die Internals Ihres Frameworks werden alle dort verarbeitet. Eine Ressource, ein Berg Arbeit.

Der Hauptthread des Browsers ist teuer. Die meiste Zeit verursacht er keine Probleme, aber sobald Sie versuchen, etwas Ambitioniertes zu tun, wird der Umgang mit dem Hauptthread zum wichtigen Teil. Dieser Artikel handelt davon, wie man mit dieser teuren Ressource umgeht.

Fangen wir an mit dem, was der Hauptthread eigentlich macht. Seine Arbeit fällt in zwei große Kategorien. Die erste ist das Ausführen von JavaScript.

Der Code, den wir schreiben, zusammen mit Event-Handlern, Timern, Netzwerk-Antwort-Callbacks und den Internals des Frameworks, läuft alles hier. Diese Tasks werden in der Reihenfolge ausgeführt, in der sie in die Queue eintreten, wann immer eine Lücke ist, ohne Bezug zum Bildschirm-Aktualisierungszyklus. Die zweite ist das Zeichnen des Bildschirms.

Wenn sich DOM oder Styles ändern und der Bildschirm aktualisiert werden muss, durchläuft der Browser ungefähr diese Schritte, in der Reihenfolge, um einen Frame zu produzieren. requestAnimationFrame-Callbacks ausführen — JavaScript, registriert, um kurz vor dem Zeichnen des Frames zu laufen Style-Berechnung — finale CSS-Werte für jedes Element berechnen Layout — Position und Größe jedes Elements berechnen (auch Reflow genannt) Paint — Paint-Befehle erzeugen, die beschreiben, was in welchen Farben gezeichnet werden soll Wenn sich nichts geändert hat, werden diese Schritte komplett übersprungen, also laufen sie nicht unbedingt jeden Frame. Nur der finale Compositing-Schritt, der die produzierte Ausgabe nimmt und sie auf dem Bildschirm zusammenbaut, wird an den Compositor-Thread übergeben. Mit anderen Worten: Der größte Teil der vorderen Hälfte der Pipeline, die den Bildschirm zeichnet, ist die Verantwortung des Hauptthreads.

Die Rendering-Pipeline zur Aktualisierung des Bildschirms Damit der Bildschirm flüssig aussieht, müssen Frames mit der Bildwiederholrate des Displays gezeichnet werden. Beim gängigsten 60Hz-Display bedeutet das 60 Frames pro Sekunde, also etwa 16,6 Millisekunden pro Frame. Und Sie können nicht alles davon nutzen.

Einmal der eigene Verarbeitungskosten des Browsers abgezogen, wird das praktische Budget meist auf etwa 10 Millisekunden geschätzt, und bei einem 120Hz-Gerät wird das Budget selbst halbiert. Das Problem ist, dass die beiden oben genannten Arbeitsarten in einer einzigen Reihe auf demselben Thread stehen. JavaScript wurde um ein Single-Threaded-Event-Loop-Modell herum entworfen.

Der Hauptthread verarbeitet einen Task zur Zeit, und solange dieser Task läuft, kann nichts anderes passieren. Wenn eine JavaScript-Funktion 200 Millisekunden läuft, dann kann der Browser in diesen 200 Millisekunden weder den Bildschirm neu zeichnen noch einen Klick vom Benutzer empfangen. Gegen ein Frame-Budget von etwa 10 Millisekunden ist das eine fatale Zeitspanne.

Ein Task, der ru...