Turbologエンジン

フレームをランレングス符号化して1回のconsole.log呼び出しにまとめる仕組みと、CellBuffer、Chromiumの詳細、ベンチマーク。

TurbologはReactの各コミットを1回の console.log 呼び出しに変換し、セルに変更がなければ呼び出しません。

目標

  1. 変更されたフレームごとに1回の呼び出し。
  2. アイドル時の呼び出しはゼロ。 変更のないフレームは文字列を組み立てる前に処理を戻します。
  3. 少数のスタイル引数。 密度の高い 100×32 のダッシュボードの3,200セルに対し、149個の %c スパンを使用します。

パイプライン

React ─▶ Yoga ─▶ CellBuffer ─▶ row-hash gate ─▶ RLE ─▶ console.log

CellBuffer

フレームは、文字、色、属性を格納するフラットな型付き配列に保持され、配列は一度だけ確保されます。

変更検出ゲート

各行のハッシュを計算します。変更がなければ、ドライバーは早期に処理を戻します。

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.163ms149
1行の編集0.011ms149
アイドル0.000ms0

ステータスページは、同じパイプラインをお使いのブラウザーで実行します。Playgroundのデモを実行している間に、DevToolsで text.stats() を実行します。

GitHubでこのページを編集