open atlas
↑ К треку
Браузер и фронтенд-рантайм WEB · 02 · 02

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

Что определяет цену каждой стадии, как процесс рендерера делит работу между потоками, и почему главный поток — бутылочное горлышко.

WEB Middle ◷ 14 min
Уровень
ОсновыJuniorMiddleSenior

Ваша страница парсится быстро на 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) полностью пропускают главный поток — именно поэтому они на порядок дешевле инвалидирующих компоновку. Теперь, когда вы видите кадр в 28 мс с Layout-полосой в 18 мс, вы знаете: драйвер — глубина DOM или зависимости коробок, а не JS и не отрисовка, — и знаете, какой рычаг тянуть.

Практика

Начни сверху. Задачи идут от простого к сложному: вспомнить факт, применить к случаю, затем senior-уровень. Открой, попробуй, потом открой ответ.

вспомнитьприменитьуглубить0 из 5 завершено
Связанные уроки
встречается в169

Что-то непонятно?

Задай вопрос по этому уроку. Вопросы анонимны и попадают напрямую автору — урок станет лучше.

хоткеи развернуть
поиск
K
пред. пьеса
k
след. пьеса
j
тиры
t
это меню
?
sources3
expand
  1. 01
  2. 02
  3. 03

Trademarks belong to their respective owners. Editorial reference only.