Event loop и рендер в браузере, часть 3


Как оптимизировать Render

Чем меньше операций производится браузером, тем лучше производительность. При первоначальной загрузке сайта браузер выполяет все этапы RequestAnimationFrame > Style > Layout > Paint > Composite.

Минимизировать количество layout

После загрузки страницы уже не каждая операция в JS обязательно проходит по всему циклу рендера.

Например, если изменить что-то из свойств, затрагивающих лейаут страницы (размеры, отступы, позиционирование) или считать их в JS, то сработает layout, а за ним нужно будет снова перерисовать элементы в paint и сложить в слои в composition.

Получается довольно затратная по производительности цепочка операций.

А если, к примеру, для перепозиционирования элементов изменять свойство transform, то браузеру не требуется перестраивать сетку страницы и делать перерисовку элементов, и поэтому этап layout и paint не запускается повторно. Работа идёт только на этапе composite, поэтому операций становится существенно меньше, и они уже не такие тяжёлые.

Также «тяжёлый» этап layout пропускается, если изменяетя только внешний вид элемента, например, фоновое изображение, цвет текста или тень, а не его размеры. В этом случае выполняется только paint и composite.

Использовать requestAnimationFrame

Если приходить к браузеру в нужный момент, когда он готов начинать отрисовку следующего кадра, то тем самым мы не прерываем форсированно работу JS-тасок или микротасок.

Использовать CSS-анимации

Анимации, сделанные на JS, вдобавок к исполнению JS-таски будут в лучшем случае прождать процесс composition (в худшем ещё и style, layout, paint). Поэтому если делать анимации в CSS, количество тасок для Event Loop будет меньше, так как мы не создаём JS-таски.

Кроме того, все CSS-анимации и процесс composition браузер выполняет с задействованием GPU, а не только с помощью CPU. Если браузер выясняет, что изменяемые свойства не влияют на layout и paint, то он отрисовывает слои в картинки (делает ещё раз repaint) и вместе с информацией о слоях передаёт их в GPU. Процессинг в GPU не зависит от CPU, и поэтому разгружает основной поток исполнения задач. Преимущетсво именно в параллельности, так как когда, например, слоёв мало или нет драйвера для видеокарты, то браузер не включает GPU, а запускает composition в отдельном CPU-треде и получается прирост произовдительности.

Также отдельные composition-слои могут создаваться неявно, если анимируемый элемент находится «под» другими слоями. Так для каждого вышележащего слоя будет создаваться отдельный composition-слой.

У CSS-анимаций есть ограничения.

Чем больше composition-слоёв формируются и чем они больше по размеру, тем дольше происходит передача слоёв с CPU на GPU и может появиться «мигание».

Кроме того, каждый слой в GPU занимает место в памяти видеокарты. Размер памяти тоже ограничен, как и вычислительное время процессора.

Использовать will-change

Использование этого свойства может подсказать браузеру сделать необходимую подготовку, например, заранее создать отдельный слой для процесса composition. Когда дело дойдёт до изменения указанных в will-change свойств, браузеру не нужно будет делать подготовку, так как он её произвёл заранее, в свободный момент.

Отдельный composition-слой будет создаваться браузером, если задавать will-change для opacity, transform, top, left, bottom, right.

Но задавать will-change для всего сразу тоже вредно, так как это может создать нагрузку на GPU:

  • на обработку GPU будут передаваться слишком большие картинки, что будет тратить время на передачу и загружать память видеокарты (на мобильных девайсах это особенно критично, так как этой памяти немного);
  • картинок будет слишком много, что тоже скажется на скорости процесса передачи и на количестве потраченной памяти.

Поэтому will-change лучше применять точечно для улучшение плохой ситуации с перфомансом непосредственно перед грядущей перерисовкой, и по возможности его выключать после отрисовки. Помимо количества слоёв, можно снижать размеры слоёв. Чем меньше, тем лучше.