Движок Turbolog
Как кадры кодируются по длинам серий в один вызов console.log, а также CellBuffer, особенности Chromium и бенчмарки.
Turbolog превращает каждый коммит React в один вызов console.log или не делает вызовов, если ни одна ячейка не изменилась.
Цели
- Один вызов на изменённый кадр.
- Ноль вызовов при простое. Для неизменённого кадра выполнение завершается до построения строки.
- Мало аргументов стиля. 149 span с
%cдля 3200 ячеек плотно заполненной панели100×32.
Конвейер
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 сразу удаляет очищенные сообщения, но отрисовывает новые с ограничением частоты в 50 мс, поэтому очистка при каждом кадре вызывает мигание пустой консоли. Дописанные кадры сменяются за одну отрисовку, а span-завеса нулевой ширины скрывает предыдущий кадр. Прокрутите вверх, чтобы увидеть предыдущие кадры.
Кадры выводятся с частотой около 15 кадров/с. Более частые кадры сбрасывают таймер ограничения частоты DevTools и отбрасываются.
Кодирование длин серий
Совпадающие ячейки объединяются в один span с %c. Span продолжаются через переносы строк, а строки CSS интернируются.
console.log("%c▼ Text.js%c ready", "color:#ededed;font-weight:700", "color:#45a557");Особенности Chromium
- Символ
%экранируется как%%. - В URL и текст вида
path:lineдобавляется word joiner, чтобы DevTools не менял их стиль. - Span используют только полные свойства CSS. DevTools копирует каждое развёрнутое свойство в каждую строку.
- Span настроены так, чтобы фоны стыковались без зазоров.
Firefox и Safari
Chromium размещает каждую строку span с %c как строчный блок. Firefox и Safari оставляют span строчными, поэтому игнорируют вертикальные отступы и переносят длинные строки. CssConsoleDriver определяет браузер с помощью проверок поддержки CSS и для этих двух консолей задаёт высоту строки 14 пикселей без отступов, отключает переносы через text-wrap-mode: nowrap и скрывает предыдущие кадры с помощью завесы с line-height: 100vh. При подгонке под размер консоли остаётся место для столбца ссылок на исходный код в Firefox. Safari ставит Menlo первым в списке шрифтов, потому что его страницы не могут использовать установленный пользователем SF Mono, которым может отрисовывать текст Web Inspector. Передайте browser в createConsoleRoot, чтобы выбрать консоль самостоятельно.
Тесты производительности
packages/core/test/perf.test.ts на плотно заполненной панели 100×32, среднее по 1000 кадрам (Apple M5 Pro, Bun 1.4.4):
| Кадр | Время | Span с %c |
|---|---|---|
| Полная перерисовка (макет, отрисовка, кодирование) | 0,163 мс | 149 |
| Изменение одной строки | 0,011 мс | 149 |
| Простой | 0,000 мс | 0 |
Страница Состояние запускает тот же конвейер в вашем браузере. Выполните text.stats() в DevTools, пока работает демо из Playground.