Turbolog: eliminowanie migotania
Czyszczenie konsoli przed każdą klatką pozostawiało ją widocznie pustą w 70–81% odmalowań DevTools. Dopisywanie klatek zmniejszyło ten udział do 1%, wyłącznie przy pierwszej klatce.
Każda klatka Text.js to jedno wywołanie console.log. Do wersji 0.4 każdą klatkę poprzedzało też console.clear(). Pod obciążeniem konsola między klatkami na moment stawała się pusta.
Pomiar migotania
Aby to zmierzyć, stworzyliśmy bench/devtools. Uruchamia Chrome for Testing, otwiera rzeczywisty frontend DevTools jako stronę i dodaje do niego instrumentację:
- Przy każdym
requestAnimationFramewe frontendzie DevTools sprawdza, czy w obszarze widocznym konsoli znajduje się pełna klatka o 32 wierszach. Odmalowania bez niej to puste odmalowania. - Następnie dekoduje klatki
Page.startScreencast, aby potwierdzić wynik na poziomie pikseli. - Każda klatka zawiera
FRAME:<id> · T:<epochMs>w wierszu 0, dzięki czemu frontend rozpoznaje wyświetlaną klatkę i jej opóźnienie.
Przy console.clear() przed każdą klatką dashboard 100×32 przy 15 fps był pusty w 70,6% odmalowań DevTools. Przy 60 fps było to 81,1%.
Przyczyna
Komponent ConsoleView w DevTools obsługuje te dwa wywołania inaczej:
console.clear()wywołujeconsoleCleared(), które synchronicznie unieważnia obszar widoczny. Wszystkie komunikaty znikają w tym samym zadaniu.console.log()wywołujeaddConsoleMessage(), które jedynie planuje odświeżenie przezviewportThrottler, instancjęThrottlerz limitem czasu 50 ms.
Każda klatka usuwa więc starą klatkę od razu, a nową odmalowuje później. Przy 15 fps przez około 50 z każdych 66 ms nic nie widać.
Dopisywanie klatek
Od wersji 0.4 Turbolog dopisuje klatki. DevTools utrzymuje widok przy dole konsoli, więc nowa klatka zastępuje starą w jednym odmalowaniu. Początkowy span o zerowej szerokości z margin-top:100vh (kurtyna) wypycha wcześniejsze klatki poza widok i kończy własny wiersz, dzięki czemu link do źródła w DevTools nigdy nie zwęża wiersza 0.
Konsolę nadal trzeba w końcu opróżnić. Czyszczenie następuje tylko przy pierwszej klatce, po invalidate(), w bezczynności po zgromadzeniu 100 klatek oraz po osiągnięciu sztywnego limitu 300.
Dostosowanie tempa do mechanizmu ograniczającego
Wysyłanie klatek szybciej, niż DevTools może je odmalować, niczego nie przyspiesza. Wpis, który dociera, gdy mechanizm ograniczający czeka, uruchamia go ponownie, więc klatki są pomijane: przy 60 fps DevTools odmalowało 67 z 295 wysłanych klatek. Turbolog zachowuje odstępy co najmniej 66 ms między klatkami, czyli około 15 fps, i nadal renderuje pojedyncze zdarzenia wejściowe synchronicznie, gdy odstęp na to pozwala.
Usuwanie blokady fazowej
Czyszczenie dodawało też około 50 ms opóźnienia do każdej kolejnej klatki. Po wyczyszczeniu DevTools wstawia syntetyczny wiersz Console was cleared. Zajmuje on miejsce na natychmiastowe wykonanie w mechanizmie ograniczającym i przesuwa następną klatkę na timer 50 ms, a przy stałym rytmie 66 ms każda kolejna klatka pozostaje zsynchronizowana z tym timerem.
Renderer czeka teraz jednorazowo 110 ms po każdym wyczyszczeniu. Następnie każda klatka dociera ponad 50 ms po zakończeniu poprzedniego odświeżenia i korzysta z natychmiastowej ścieżki DevTools. Mediana opóźnienia wyświetlania spadła z 63 ms do 10,4 ms.
Wyniki
bun bench/devtools/run.ts --strategy=native,clear --fps=15,60 --seconds=5, dashboard 100×32, rzeczywisty frontend DevTools:
| Tryb | FPS | Puste odmalowania | Odmalowane / wysłane | Opóźnienie p50 |
|---|---|---|---|---|
| Czyszczenie co klatkę | 15 | 70,6% | 70 / 75 | 60,4 ms |
| Dopisywanie (1.0) | 15 | 1,0% | 74 / 75 | 10,4 ms |
| Czyszczenie co klatkę | 60 | 81,1% | 45 / 300 | 9,8 ms |
| Dopisywanie bez regulacji tempa | 60 | 0,8% | 67 / 295 | 31,2 ms |
Pozostały 1% przypada na pierwszą klatkę, która celowo czyści konsolę. Jej puste odmalowania to wiersz Console was cleared, a nie pusta konsola.
Mniej właściwości na span
Kod źródłowy DevTools ujawnił jeszcze jeden koszt. Kopiuje każdą właściwość składową stylu %c do każdego elementu opakowującego wiersz, więc skrócone zapisy font: i margin: rozwijały się do około 33 właściwości na span. Spany używają teraz wyłącznie właściwości składowych, około 7 na każdy, z kolorami w zapisie szesnastkowym.
Tryb dopisywania pojawił się w Text.js 0.4, a poprawka blokady fazowej trafia do wersji 1.0. Żadna z tych zmian nie wymaga zmian w kodzie aplikacji.