Движок Turbolog

Как кадры кодируются по длинам серий в один вызов console.log, а также CellBuffer, особенности Chromium и бенчмарки.

Turbolog превращает каждый коммит React в один вызов console.log или не делает вызовов, если ни одна ячейка не изменилась.

Цели

  1. Один вызов на изменённый кадр.
  2. Ноль вызовов при простое. Для неизменённого кадра выполнение завершается до построения строки.
  3. Мало аргументов стиля. 149 span с %c для 3200 ячеек плотно заполненной панели 100×32.

Конвейер

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 сразу удаляет очищенные сообщения, но отрисовывает новые с ограничением частоты в 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.

Изменить эту страницу на GitHub