Turbolog: ביטול ההבהוב
ניקוי המסוף לפני כל פריים הותיר אותו ריק לעין ב-70–81% מפעולות הציור של DevTools. הוספת פריימים לסוף הורידה את הנתון ל-1%, כולו בפריים הראשון.
כל פריים של Text.js הוא קריאת console.log אחת. עד 0.4, לפני כל פריים הייתה גם קריאת console.clear(). תחת עומס, המסוף הבהב והתרוקן בין פריימים.
מדידת הבהוב
כדי למדוד זאת, בנינו את bench/devtools. הוא מפעיל את Chrome for Testing, פותח את ממשק DevTools האמיתי כדף ומוסיף לו כלי מדידה:
- בכל
requestAnimationFrameבממשק DevTools, הוא בודק אם פריים שלם בן 32 שורות נמצא באזור התצוגה של המסוף. ציורים ללא פריים כזה הם ציורים ריקים. - לאחר מכן הוא מפענח פריימים של
Page.startScreencastכדי לאמת את התוצאה בפיקסלים. - כל פריים נושא את
FRAME:<id> · T:<epochMs>בשורה 0, כדי שהממשק יוכל לזהות איזה פריים הוא מציג ומה ההשהיה שלו.
עם console.clear() לפני כל פריים, לוח מחוונים בגודל 100×32 בקצב של 15 fps היה ריק ב-70.6% מהציורים של DevTools. בקצב של 60 fps, ב-81.1%.
הסיבה
ConsoleView של DevTools מטפל בשתי הקריאות באופן שונה:
console.clear()קוראת ל-consoleCleared(), שמבטלת את תוקף אזור התצוגה באופן סינכרוני. כל ההודעות נעלמות באותה משימה.console.log()קוראת ל-addConsoleMessage(), שרק מתזמנת רענון דרךviewportThrottler, שהואThrottlerעם זמן המתנה של 50 ms.
כך כל פריים מסיר את הפריים הישן עכשיו ומצייר את החדש מאוחר יותר. בקצב של 15 fps, במשך כ-50 מתוך כל 66 ms לא מוצג דבר.
הוספת פריימים לסוף
מאז 0.4, Turbolog מוסיף פריימים לסוף. DevTools נצמד לתחתית המסוף, ולכן הפריים החדש מחליף את הישן בציור יחיד. span מוביל ברוחב אפס עם margin-top:100vh (הווילון) דוחף פריימים קודמים אל מחוץ לתצוגה, ומסיים את השורה שלו כך שקישור המקור של DevTools לעולם אינו מצר את שורה 0.
עדיין צריך לרוקן את המסוף בסופו של דבר. ניקוי מתבצע רק בפריים הראשון, אחרי invalidate(), בזמן חוסר פעילות לאחר שהצטברו 100 פריימים, ובסף קשיח של 300.
התאמת הקצב למנגנון ההשהיה
שליחת פריימים מהר יותר מכפי ש-DevTools יכול לצייר אותם אינה מאיצה דבר. הודעת יומן שמגיעה בזמן שמנגנון ההשהיה ממתין מפעילה אותו מחדש, ולכן פריימים נושרים: בקצב של 60 fps, DevTools צייר 67 מתוך 295 פריימים שנשלחו. Turbolog שומר על מרווח של 66 ms לפחות בין פריימים, כ-15 fps, ועדיין מרנדר אירועי קלט בדידים באופן סינכרוני כשהמרווח מאפשר זאת.
שבירת נעילת המופע
פעולות הניקוי הוסיפו גם השהיה של כ-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 אמיתי:
| מנגנון | FPS | ציורים ריקים | צוירו / נשלחו | השהיה p50 |
|---|---|---|---|---|
| ניקוי בכל פריים | 15 | 70.6% | 70 / 75 | 60.4 ms |
| הוספה לסוף (1.0) | 15 | 1.0% | 74 / 75 | 10.4 ms |
| ניקוי בכל פריים | 60 | 81.1% | 45 / 300 | 9.8 ms |
| הוספה לסוף, ללא ויסות קצב | 60 | 0.8% | 67 / 295 | 31.2 ms |
1% הנותרים הם הפריים הראשון, שמנקה את המסוף במכוון. הציורים הריקים שלו הם שורת Console was cleared, ולא מסוף ריק.
פחות מאפיינים לכל span
קוד המקור של DevTools חשף עלות נוספת. הוא מעתיק כל מאפיין מפורש של סגנון %c לכל מעטפת שורה, כך שהקיצורים font: ו-margin: התרחבו לכ-33 מאפיינים לכל span. כעת רכיבי span משתמשים רק במאפיינים מפורשים, כ-7 לכל אחד, עם צבעים הקסדצימליים.
מצב ההוספה לסוף יצא ב-Text.js 0.4, והתיקון לנעילת המופע יוצא ב-1.0. אף אחד מהם אינו דורש שינויים בקוד היישום.