Turbolog: eliminare il lampeggiamento
Svuotare la console prima di ogni frame la lasciava visibilmente vuota nel 70–81% dei ridisegni di DevTools. Accodare i frame ha ridotto il valore all’1%, interamente sul primo frame.
Ogni frame di Text.js è una chiamata a console.log. Fino alla versione 0.4, ogni frame era anche preceduto da console.clear(). Sotto carico, la console lampeggiava mostrando il vuoto tra un frame e l’altro.
Misurare il lampeggio
Per misurarlo, abbiamo creato bench/devtools. Avvia Chrome for Testing, apre il vero frontend di DevTools come pagina e lo strumenta:
- A ogni
requestAnimationFramenel frontend di DevTools, controlla se nel viewport della console è presente un frame completo di 32 righe. Le ridipinture che ne sono prive sono considerate vuote. - Decodifica poi i frame di
Page.startScreencastper confermare il risultato in pixel. - Ogni frame riporta
FRAME:<id> · T:<epochMs>nella riga 0, così il frontend può determinare quale frame sta mostrando e con quanto ritardo.
Con console.clear() prima di ogni frame, una dashboard 100×32 a 15 fps risultava vuota nel 70,6% delle ridipinture di DevTools. A 60 fps, nell’81,1%.
Il motivo
ConsoleView di DevTools gestisce le due chiamate in modo diverso:
console.clear()chiamaconsoleCleared(), che invalida il viewport in modo sincrono. Ogni messaggio scompare nello stesso task.console.log()chiamaaddConsoleMessage(), che si limita a pianificare un aggiornamento tramiteviewportThrottler, unThrottlercon un timeout di 50 ms.
Quindi ogni frame rimuove subito il vecchio frame e disegna quello nuovo più tardi. A 15 fps, per circa 50 ms ogni 66 ms non si vede nulla.
Accodare i frame
Dalla versione 0.4, Turbolog accoda i frame. DevTools resta in fondo alla console, quindi il nuovo frame sostituisce quello vecchio in una sola ridipintura. Uno span iniziale di larghezza zero con margin-top:100vh (la tenda) spinge i frame precedenti fuori dalla vista e termina la propria riga, così il link al sorgente di DevTools non restringe mai la riga 0.
Prima o poi la console deve comunque essere svuotata. Lo svuotamento avviene solo al primo frame, dopo invalidate(), durante l’inattività una volta accumulati 100 frame e al limite massimo di 300.
Adeguare il ritmo al throttler
Inviare frame più velocemente di quanto DevTools riesca a disegnarli non accelera nulla. Un log che arriva mentre il throttler è in attesa lo riavvia, quindi alcuni frame vengono scartati: a 60 fps DevTools ha disegnato 67 dei 295 frame emessi. Turbolog distanzia i frame di almeno 66 ms, circa 15 fps, e continua a renderizzare gli input discreti in modo sincrono quando l’intervallo lo consente.
Interrompere l’aggancio di fase
Gli svuotamenti aggiungevano anche circa 50 ms di ritardo a ogni frame successivo. Dopo uno svuotamento, DevTools inserisce una riga sintetica Console was cleared. Questa occupa lo slot immediato del throttler e sposta il frame successivo sul timer da 50 ms, e con una cadenza costante di 66 ms ogni frame successivo resta agganciato a quel timer.
Il renderer ora attende 110 ms una sola volta dopo ogni svuotamento. Da quel momento, ogni frame arriva più di 50 ms dopo il completamento dell’aggiornamento precedente e segue il percorso immediato di DevTools. Il ritardo mediano di visualizzazione è passato da 63 ms a 10,4 ms.
Risultati
bun bench/devtools/run.ts --strategy=native,clear --fps=15,60 --seconds=5, dashboard 100×32, vero frontend di DevTools:
| Driver | FPS | Ridipinture vuote | Disegnati / emessi | Ritardo p50 |
|---|---|---|---|---|
| Svuotamento a ogni frame | 15 | 70,6% | 70 / 75 | 60,4 ms |
| Accodamento (1.0) | 15 | 1,0% | 74 / 75 | 10,4 ms |
| Svuotamento a ogni frame | 60 | 81,1% | 45 / 300 | 9,8 ms |
| Accodamento, senza cadenza | 60 | 0,8% | 67 / 295 | 31,2 ms |
Il restante 1% è il primo frame, che svuota la console di proposito. Le sue ridipinture vuote sono la riga Console was cleared, non una console vuota.
Meno proprietà per span
Il sorgente di DevTools ha rivelato un altro costo. Copia ogni proprietà estesa di uno stile %c su ogni contenitore di riga, quindi le proprietà abbreviate font: e margin: si espandevano in circa 33 proprietà per span. Ora gli span usano solo proprietà estese, circa 7 ciascuno, con colori esadecimali.
La modalità di accodamento è stata introdotta in Text.js 0.4 e la correzione dell’aggancio di fase arriva nella versione 1.0. Nessuna delle due richiede modifiche al codice dell’applicazione.