awesome-everything EN
↑ Обратно к восхождению

Браузер и фронтенд-рантайм

Цена стадий и модель процесса рендерера

Суть Что определяет цену каждой стадии, как процесс рендерера делит работу между потоками, и почему главный поток — бутылочное горлышко.
Высота — путь к senior
НольJuniorMiddleSenior
Ты на middle-высоте — в небе
◷ 14 min

Ваша страница парсится быстро на M2 MacBook. На среднем Android тормозит. Бутылочное горлышко никогда не бывает «CPU медленный» — оно всегда в том, какой поток делает какую работу и сколько её приходится на поток, который нельзя распараллелить.

Модель процесса рендерера

Современный браузер семейства Chromium запускает каждую вкладку в отдельном процессе рендерера. Внутри этого процесса:

  • Главный поток — исполняет парсинг HTML, построение CSSOM, стили, компоновку, подготовку отрисовки и ваш JavaScript. Он однопоточный по дизайну: DOM, CSSOM и состояние JS могут изменяться только одним контекстом исполнения за раз, иначе согласованность невозможна.
  • Поток композитора — собирает дерево слоёв и решает, каким слоям нужны новые bitmap-ы.
  • Растровые рабочие потоки — параллельно растеризуют отдельные тайлы.
  • GPU-процесс — берёт растеризованные тайлы, загружает их как текстуры, выполняет финальный композит-и-показ.

Firefox использует похожее разделение (Quantum CSS для параллельных стилей, WebRender для GPU-driven композитинга); Safari/WebKit разделяет между WebContent-процессом и GPU-процессом. Имена разные, архитектурная рифма универсальна.

Внутренности процесса рендерера (Chromium)

Главный поток

Парсинг HTML → CSSOM → Стили → Компоновка → Подготовка отрисовки

+ ваш JavaScript

Поток композитора

Собирает дерево слоёв, решает какие тайлы грязные

Растровые воркеры (N)

Параллельно растеризуют тайлы

GPU-процесс

Загружает текстуры, компонует и отображает

Почему главный поток — бутылочное горлышко

Любая работа на главном потоке — парсинг JSON-блоба, десериализация Redux-стейта, компоновка, обработчик клика — конкурирует за то же окно в 16.67 мс. Поток композитора и растровые потоки существуют именно для того, чтобы кусок работы рендеринга мог уйти с главного потока и работать параллельно.

Это архитектурное обоснование того, что transform/opacity-анимации «бесплатные»: они достигают GPU, не трогая узкое горло главного потока.

Усилители цены по стадиям

У каждой стадии есть типовые рычаги, раздувающие её цену.

Парсинг HTML масштабируется с байтами документа. SSR-страница 500 КБ парсится быстрее, чем 2 МБ. Синхронные тэги <script> блокируют парсер — современная практика ставит defer или async на всякий внешний скрипт, не строго нужный для первой отрисовки.

CSSOM растёт с байтами стилей и количеством правил. Неиспользуемый CSS-фреймворк на 800 правил тратит время парсинга, даже если ноль правил матчатся.

Пересчёт стилей стоит примерно размер DOM × селекторы. DOM 5 000 нод плюс 2 000 правил — 10 миллионов проверок мэтчинга. Большинство отсеивается bloom-фильтром, но :has(), descendant combinators без anchor-предка и универсальные селекторы пробивают фильтр.

Компоновка — примерно глубина DOM × зависимости между коробками. Глубоко вложенный flexbox с auto-размерами требует нескольких проходов измерения; плоский grid с явными размерами ячеек — одного.

Отрисовка — площадь × количество paint-операций. box-shadow с большим blur radius и filter-свойства тяжелы для отрисовки: каждый пиксель требует мульти-пиксельной выборки.

Композитинг — количество слоёв × площадь пикселей слоя. Дешёвые стадии дешевле на порядки — но только если верхние стадии не инвалидируют нижние.

Почему это работает

Почему DOM вообще однопоточный? Потому что два контекста исполнения, пишущие в один DOM-узел одновременно без локировки, потребовали бы полноценного concurrent garbage collector и всё равно оставляли бы тонкие race-окна. Java пробовала это с правилом UI-потока в Swing; браузер унаследовал то же ограничение. Однопоточность — намеренный компромисс ради корректности, а не упущение.

Викторина

Вы меняете свойство `top` у div в цикле rAF. Какие стадии пайплайна перезапускаются на каждом кадре?

Викторина

Вы меняете `transform: translateX(...)` у div, у которого уже есть свой слой композитора. Какие стадии работают на главном потоке?

Проследи
1/4

Performance-панель DevTools показывает кадр в 28 мс. Внутри: 1 мс Parse HTML, 2 мс Recalculate Style, 18 мс Layout, 4 мс Paint, 1 мс Composite Layers, 2 мс idle. Страница скроллит список из 5000 сообщений чата. Куда уходит время?

1
Step 1 of 4
Компоновка доминирует — 18 мс. Скорее всего: каждая видимая строка перемеряется, потому что выше по дереву что-то поменяло ширину
2
Locked
Доминирует отрисовка — 4 мс
3
Locked
Композитинг — 1 мс
4
Locked
Idle в 2 мс означает голод страницы
Посчитай

DOM имеет 5000 нод. Стилевой файл — 2000 правил. Сколько примерно проверок мэтчинга селекторов выполняет пересчёт стилей?

проверок
Вспомните перед уходом
  1. 01
    Какие четыре потока/процесса использует процесс рендерера в Chromium?
  2. 02
    Почему главный поток однопоточный?
  3. 03
    Что является драйвером цены для пересчёта стилей?
Итог

Процесс рендерера имеет четыре игрока: главный поток, поток композитора, растровые воркеры и GPU-процесс. Пять из шести стадий пайплайна исполняются на одном главном потоке — том же самом, что и JavaScript — так что каждая длинная задача конкурирует с рендерингом. Цены стадий предсказуемы: парсинг масштабируется с байтами, пересчёт стилей — с DOM × правила, компоновка — с глубиной DOM × зависимости коробок, отрисовка — с площадью × операции, композитинг — с количеством слоёв × пикселей. Composite-only анимации (transform, opacity) полностью пропускают главный поток — именно поэтому они на порядок дешевле инвалидирующих компоновку.

Связанные уроки
встречается в143
Продолжить восхождение ↑Инвалидация, dirty-биты и contain
хоткеи развернуть
поиск
K
пред. пьеса
k
след. пьеса
j
тиры
t
это меню
?
sources3
expand
  1. 01
  2. 02
  3. 03

Trademarks belong to their respective owners. Editorial reference only.