
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
лучше применять точечно для улучшение плохой ситуации с перфомансом непосредственно перед грядущей перерисовкой, и по возможности его выключать после отрисовки. Помимо количества слоёв, можно снижать размеры слоёв. Чем меньше, тем лучше.