← وبلاگ

⁦Turbolog⁩: حذف چشمک‌زدن

پاک کردن کنسول پیش از هر فریم باعث می‌شد در ⁦70–81%⁩ از دفعات ترسیم DevTools، کنسول آشکارا خالی بماند. افزودن فریم‌ها به انتها این میزان را به ⁦1%⁩ رساند که تمام آن مربوط به فریم اول بود.

Adam

هر فریم Text.js یک فراخوانی console.log است. تا نسخهٔ 0.4، پیش از هر فریم console.clear() هم فراخوانی می‌شد. زیر بار، کنسول بین فریم‌ها لحظه‌ای خالی می‌شد.

اندازه‌گیری چشمک‌زدن

برای اندازه‌گیری آن، bench/devtools را ساختیم. این ابزار ⁦Chrome for Testing⁩ را اجرا می‌کند، رابط واقعی DevTools را به‌صورت یک صفحه باز می‌کند و آن را برای اندازه‌گیری مجهز می‌کند:

  • در هر requestAnimationFrame در رابط DevTools، بررسی می‌کند که آیا یک فریم کامل 32 ردیفی در محدودهٔ دید کنسول قرار دارد. ترسیم‌هایی که چنین فریمی ندارند، ترسیم خالی هستند.
  • سپس فریم‌های Page.startScreencast را رمزگشایی می‌کند تا نتیجه را در سطح پیکسل تأیید کند.
  • هر فریم در ردیف 0 حاوی FRAME:<id> · T:<epochMs> است تا رابط بتواند تشخیص دهد کدام فریم را نمایش می‌دهد و چقدر تأخیر دارد.

با فراخوانی console.clear() پیش از هر فریم، یک داشبورد ⁦100×32⁩ با نرخ 15 فریم بر ثانیه در ⁦70.6%⁩ از ترسیم‌های DevTools خالی بود. با نرخ 60 فریم بر ثانیه، ⁦81.1%⁩.

علت

کامپوننت ConsoleView در DevTools این دو فراخوانی را متفاوت پردازش می‌کند:

  1. فراخوانی console.clear() تابع consoleCleared() را فراخوانی می‌کند که محدودهٔ دید را به‌صورت همگام نامعتبر می‌کند. همهٔ پیام‌ها در همان تسک ناپدید می‌شوند.
  2. فراخوانی console.log() تابع addConsoleMessage() را فراخوانی می‌کند که فقط از طریق viewportThrottler، یک Throttler با مهلت 50 ms، به‌روزرسانی را زمان‌بندی می‌کند.

بنابراین هر فریم، فریم قبلی را همین حالا حذف می‌کند و فریم جدید را بعداً ترسیم می‌کند. با نرخ 15 فریم بر ثانیه، در حدود 50 میلی‌ثانیه از هر 66 میلی‌ثانیه چیزی نمایش داده نمی‌شود.

افزودن فریم‌ها به انتها

از نسخهٔ 0.4، Turbolog فریم‌ها را به انتها می‌افزاید. DevTools در پایین کنسول می‌ماند، بنابراین فریم جدید در یک ترسیم جای فریم قبلی را می‌گیرد. یک span با عرض صفر در ابتدا و با سبک margin-top:100vh (پرده)، فریم‌های قبلی را از دید خارج می‌کند و خط خودش را پایان می‌دهد تا پیوند منبع DevTools هرگز عرض ردیف 0 را کم نکند.

کنسول همچنان باید در نهایت خالی شود. پاک‌سازی فقط در فریم اول، پس از فراخوانی invalidate()، در زمان بیکاری پس از انباشته شدن 100 فریم و در سقف قطعی 300 فریم انجام می‌شود.

تنظیم آهنگ با محدودکنندهٔ نرخ

ارسال فریم‌ها سریع‌تر از توان ترسیم DevTools هیچ‌چیز را سریع‌تر نمی‌کند. گزارشی که هنگام انتظار محدودکنندهٔ نرخ برسد، آن را از نو راه می‌اندازد؛ بنابراین فریم‌ها حذف می‌شوند: با نرخ 60 فریم بر ثانیه، DevTools از 295 فریم ارسال‌شده، 67 فریم را ترسیم کرد. Turbolog فریم‌ها را با فاصلهٔ حداقل 66 ms، حدود 15 فریم بر ثانیه، ارسال می‌کند و همچنان وقتی این فاصله اجازه دهد، ورودی گسسته را به‌صورت همگام رندر می‌کند.

شکستن قفل فاز

پاک‌سازی‌ها به هر فریم بعدی حدود 50 ms تأخیر هم اضافه می‌کردند. پس از پاک‌سازی، DevTools یک ردیف ساختگی Console was cleared درج می‌کند. این ردیف نوبت فوری محدودکنندهٔ نرخ را می‌گیرد و فریم بعدی را به زمان‌سنج 50 ms می‌فرستد، و با آهنگ ثابت 66 ms، هر فریم بعدی به آن زمان‌سنج قفل می‌ماند.

رندرکننده اکنون پس از هر پاک‌سازی، یک بار 110 ms منتظر می‌ماند. پس از آن، هر فریم بیش از 50 ms پس از تکمیل به‌روزرسانی قبلی می‌رسد و مسیر فوری DevTools را طی می‌کند. میانهٔ تأخیر نمایش از 63 ms به 10.4 ms رسید.

نتایج

bun bench/devtools/run.ts --strategy=native,clear --fps=15,60 --seconds=5، داشبورد ⁦100×32⁩، رابط واقعی DevTools:

راه‌اندازفریم بر ثانیهترسیم‌های خالیترسیم‌شده / ارسال‌شدهتأخیر ⁦p50⁩
پاک‌سازی در هر فریم15⁦70.6%⁩70 / 7560.4 ms
افزودن به انتها (1.0)15⁦1.0%⁩74 / 7510.4 ms
پاک‌سازی در هر فریم60⁦81.1%⁩45 / 3009.8 ms
افزودن به انتها، بدون تنظیم آهنگ60⁦0.8%⁩67 / 29531.2 ms

⁦1%⁩ باقی‌مانده مربوط به فریم اول است که عمداً پاک‌سازی می‌کند. ترسیم‌های خالی آن، ردیف Console was cleared هستند، نه یک کنسول خالی.

ویژگی‌های کمتر در هر span

کد منبع DevTools هزینهٔ دیگری را هم نشان داد. این کد هر ویژگی مستقل از سبک %c را روی پوشانندهٔ هر خط کپی می‌کند، بنابراین صورت‌های کوتاه font: و margin: به حدود 33 ویژگی در هر span گسترش می‌یافتند. اکنون spanها فقط از ویژگی‌های مستقل، حدود 7 ویژگی برای هر کدام، با رنگ‌های شانزده‌شانزدهی استفاده می‌کنند.

حالت افزودن به انتها در ⁦Text.js 0.4⁩ عرضه شد و اصلاح قفل فاز در نسخهٔ 1.0 عرضه می‌شود. هیچ‌کدام به تغییر کد برنامه نیاز ندارند.