Turbologエンジン
フレームをランレングス符号化して1回のconsole.log呼び出しにまとめる仕組みと、CellBuffer、Chromiumの詳細、ベンチマーク。
TurbologはReactの各コミットを1回の console.log 呼び出しに変換し、セルに変更がなければ呼び出しません。
目標
- 変更されたフレームごとに1回の呼び出し。
- アイドル時の呼び出しはゼロ。 変更のないフレームは文字列を組み立てる前に処理を戻します。
- 少数のスタイル引数。 密度の高い
100×32のダッシュボードの3,200セルに対し、149個の%cスパンを使用します。
パイプライン
React ─▶ Yoga ─▶ CellBuffer ─▶ row-hash gate ─▶ RLE ─▶ console.logCellBuffer
フレームは、文字、色、属性を格納するフラットな型付き配列に保持され、配列は一度だけ確保されます。
変更検出ゲート
各行のハッシュを計算します。変更がなければ、ドライバーは早期に処理を戻します。
if (frame === null) return SKIPPED; // 0 console callsconsole.log(frame.format, ...frame.args);フレームの切り替え
Turbologはフレームを追加し、最初のフレーム、invalidate() の後、100フレームが積み重なった後のアイドル時、および300フレームに達した時にのみコンソールをクリアします。DevToolsはクリアされたメッセージを即座に削除しますが、新しいメッセージの描画は50ms間隔に制限するため、フレームごとにクリアすると空のコンソールが一瞬表示されます。追加されたフレームは1回の描画内で切り替わり、幅ゼロのカーテンスパンが前のフレームを表示領域から隠します。以前のフレームを見るには上にスクロールします。
フレームの更新頻度は約15fpsに調整されます。それより速いフレームはDevToolsのスロットリングをリセットし、破棄されます。
ランレングス符号化
一致するセルは1つの %c スパンにまとめられます。スパンは改行をまたいで続き、CSS文字列はインターン化されます。
console.log("%c▼ Text.js%c ready", "color:#ededed;font-weight:700", "color:#45a557");Chromiumの詳細
- リテラルの
%は%%としてエスケープされます。 - URLと
path:lineのテキストには、DevToolsがスタイルを変更しないようにワードジョイナーが挿入されます。 - スパンはCSSの個別指定プロパティのみを使用します。DevToolsは展開後の各プロパティをすべての行にコピーします。
- 背景が隙間なく並ぶようにスパンを調整しています。
FirefoxとSafari
Chromiumは %c スパンの各行をインラインブロックとしてレイアウトします。FirefoxとSafariはスパンをインラインのまま扱うため、上下のマージンを無視し、長い行を折り返します。CssConsoleDriver はCSS機能のチェックでブラウザーを検出し、この2つのコンソールではマージンなしで行の高さを14pxに設定し、text-wrap-mode: nowrap で折り返しを無効にし、line-height: 100vh のカーテンで以前のフレームを表示領域の外に押し出します。コンソールに合わせたサイズ調整では、Firefoxのソースリンク列のための余白を確保します。Safariでは、Web Inspectorが描画に使う場合のあるユーザーインストールのSF Monoをページでは使えないため、フォントスタックの先頭にMenloを配置します。コンソールを自分で選ぶには、createConsoleRoot に browser を渡します。
ベンチマーク
密度の高い 100×32 のダッシュボードで packages/core/test/perf.test.ts を実行した、1,000フレームの平均値です(Apple M5 Pro、Bun 1.4.4)。
| フレーム | 時間 | %c スパン数 |
|---|---|---|
| 全体の再描画(レイアウト、描画、エンコード) | 0.163ms | 149 |
| 1行の編集 | 0.011ms | 149 |
| アイドル | 0.000ms | 0 |
ステータスページは、同じパイプラインをお使いのブラウザーで実行します。Playgroundのデモを実行している間に、DevToolsで text.stats() を実行します。