company_banner
28 мая 2015 в 11:20

Оптимизация рендеринга веб-страницы

image

Из-за давления бизнеса, мы стремимся сделать всё быстрее. От этого страдает планирование и многие вещи не учитываются. Например, легко забыть о производительности и через какое-то время столкнуться с тем, что на более слабых машинах и планшетах обилие движущихся элементов страшно тормозит и дёргается в конвульсиях. Посмотрим, что можно сделать, если вы столкнулись с такой проблемой или хотели бы её избежать.

Начнём издалека, с той штуки с которой вы всё это читаете. Большинство экранов обновляют изображение примерно 60 раз в секунду и это создаёт иллюзию движения. При каждом обновлении изображение незначительно меняется, поэтому нам кажется что все преобразования происходят плавно. По сути речь идёт о кадрах, как в киноплёнке.

Если изображение (в нашем случае веб-страница) не успевает отрисоваться, мы можем различить что оно дёргается, плавность пропала, и мы пропускаем кадры. Чтобы показать 60 кадров в течении 1 секунды (1000мс) нам необходимо показывать новый кадр примерно за 16,6 мс. Иначе говоря, если мы видим скачки движения — новое изображение не успевает отрисоваться за 16 миллисекунд.

Чтобы определить проблему, нам надо рассмотреть этапы того как браузер создаёт страницу и понять чем занят процессор вместо полезной и нужной деятельности. Подробно весь процесс сборки страницы описан в работе Тали Гарсиель “Как работает браузер”. Если упрощать, то при загрузке страницы браузер разбирает html и css на узлы, формирует из них деревья, объединяет их, и рассчитывает то, как должен выглядеть каждый узел.

Далее происходят два процесса:
  • Layout — рассчитывается положение и размер элементов.
  • Paint — применяются стили и непосредственно отрисовываются элементы.


Когда вы изменяете что-то на странице, браузер должен снова рассчитать и обновить изменившиеся элементы. Само по себе перерисовывание элементов дорогостоящая в процессорном времени операция и её нужно избегать без лишней необходимости.

Мы можем убедиться в этом заглянув в Chrome Dev Tools и воспользовавшись Timeline (на май 2015 он выглядит примерно так).

image

Timeline позволяет записать разные активности браузера во время взаимодействия с ним и оценить сколько они занимают процессорного времени. Если речь идёт о рендеринге, то можно оценить сколько стоит процессорного времени перерисовать тот или иной элемент. Не все они при перерисовке имеют одинаковую стоимость. Какие-то более дорогостоящие и сложные, другие менее. Определяется это эмпирическим путём, записывая и изучая таймлайн.

В Dev Tools также есть опция Show paint rectangles, которая позволяет определить происходит ли перерисовка в данный момент. Элемент, который перерисовывается, подсвечивается. Включается эта опция во вкладке Rendering.

image

Рассмотрим простой пример. Допустим у нас есть некий блок, который мы анимировали достаточно простым способом — сменой свойства left у абсолютно позиционированного элемента.

.elem{
      width: 200px;
      height: 100px;
      background-color: lightgray;
      color: white;
      position: absolute;
      left: 100px;
      top: 100px;
      transition: 1s ease;
}
.elem--active{
      left: 400px;
}


Если мы рассмотрим это действие с включенной выше опцией, мы увидим что в процессе движения перерисовка идёт постоянно. По факту в этом блоке ничего не меняется и его не нужно перерисовывать. Следовательно, это ненужные действия от которых надо избавиться чтобы уложиться в 16 миллисекунд.

image

Как мы можем это сделать? Если посмотреть спецификацию (http://www.w3.org/TR/css3-transforms/#transform-property), то там в описании свойства translate написано, что если у свойства есть значение отличное от пустого, то это создаст отдельный контекст, и элемент будет выведен в новый слой.

Исторически, браузеры объединяли всю веб-станицу в один слой, однако со временем возникла потребность выделять отдельно некоторые элементы и управлять ими при помощи графического процессора (GPU). Это значительно сокращает нагрузку на центральный процессор.

Перепишем наш CSS для использования с transform: translateX().

.elem--active{
      transform: translateX(400px);
}


А теперь и попробуем вызвать тоже самое взаимодействие.

image

Область перестала постоянно перерисовываться, и был создан отдельный слой. Анимация стала более плавной за счёт того что transform использует субпиксельную точность (техника обработки изображения для улучшения качества его отображения). Свойство left привязано к пиксельной сетке и движения в первом варианте были более «дёрганными».

Стоит отметить, что отдельные слои создаются также при ряде других условий. Если вспомнить о том, как и для чего появились слои, то о списке этих условий вполне можно догадаться — это все элементы предназначение которых активно изменяться и перерисовываться. Например: теги
Автор: @ianbrode
Rambler&Co
рейтинг 112,32
Похожие публикации

Комментарии (16)

  • +10
    У данного подхода, тем не менее, есть отрицательная сторона.

    Их гораздо больше :) И если их не учитывать — получим обратный эффект: сильное снижение производительности.

    1. Отдельный GPU-слой почти всегда занимает отдельную память, которую можно посчитать (в байтах) по довольно приблизительной формуле width × height × 4. То есть чем больше слоёв (и чем больше физические размеры слоя), тем больше расход памяти. Неумелым использованием слоёв можно очень быстро вырубить браузер на мобилках.
    2. В Webkit-браузерах (десктоп и мобильные) слой перерисовывается целиком. Это значит, что если где-то внутри слоя поменяется хотя бы один пиксель, браузер сделает repaint всего слоя, а также потратит время на перенос его в GPU. Хотя Blink и научился оптимизировать такие вещи, за этим всё равно нужно следить с помощью описанных в статье инструментов.
    3. Если GPU-слой по z-index будет ниже, то и этот слой тоже будет вынесен на GPU. Со всеми последствиям, описанными выше и с возможными артефактами отрисовки.


    Вообще стоит понимать, что GPU-слои (или compositing, как это называется в Blink) — это один большой хак, который сами разработчики браузеров пытаются от нас скрыть и заставить браузер работать так, будто никаких GPU и не-GPU слоёв нет. Поэтому нет никакого «официального» описания как этим правильно пользоваться. Более того, поведение браузеров постоянно меняется: например, Blink далеко не всегда принудительно вынесет слой на GPU даже если ему указать transform: translateZ(0), так он пытается оптимизировать работу с памятью и железом. Иногда это к лучшему, а иногда приходится искать варианты обхода этой оптимизации, чтобы браузер не делал лишний repaint перед анимацией.

    Поэтому когда собираетесь выносить что-то на GPU, нужно чётко представлять себе все возможные последствия и уметь правильно пользоваться инструментами для отладки. Я таким образом уже несколько сайтов спас от крэшей на мобильных устройствах :)
    • НЛО прилетело и опубликовало эту надпись здесь
      • +3
        Вроде через chrome://tracing/ это можно отловить, хотя не так удобно, как через Timeline
    • 0
      Поэтому нет никакого «официального» описания как этим правильно пользоваться.


      Вообще-то есть. CSS свойство will-change позволяет сообщить браузеру об изменениях, которые будут применены к элементу.
      • +3
        Да, есть такое свойство. С помощью которого вы всего лишь говорите браузеру «я планирую менять вот такие свойства, оптимизируй это как-нибудь». А как браузер это оптимизирует — вынесет отдельным слоем на GPU, склеит с другими блоками и вынесет отдельным слоем или вообще оставит на основном холсте — вы не знаете. Вы не можете с помощью этого свойства сказать «сразу вынеси этот слой на GPU, потому что потом я буду анимировать кучу блоков и мне не нужен лишний repaint перед стартом анимации; я даю отчёт всем своим действиям и принимаю на себя все возможные риски». Я очень долго на последнем визульано-сложном проекте искал возможность сделать именно так, и никакой will-change тут не помогал. Я пытался именно обойти браузерную оптимизацию и контролировать весь процесс самостоятельно для достижения нужного соотношения производительности и расхода памяти.
      • НЛО прилетело и опубликовало эту надпись здесь
    • 0
      Спасибо за дополнение. :)

      GPU-слои (или compositing, как это называется в Blink) — это один большой хак


      У меня тоже такое ощущение складывается. Реализация могла бы быть более красивой и облегчила бы жизнь разработчикам, но это потребует существенных архитектурных изменений.
  • +3
    Оптимизация это конечно хорошо, но ничто не может быть лучше полного отказа от анимации:

    -Если изображение (в нашем случае веб-страница) не успевает отрисоваться
    Значит нафиг не надо использовать такую анимацию. не мучайте пользователя, не садите ему аккумулятор лишними расчетами. Большинство устройств, под которые вы оптимизируете код «чтобы везде работало» и так слишком слабые, чтобы отображать такую веб-анимацию. Как не оптимизируй, а всегда найдется более слабое устройство, на котором «будет тормозить». А на устройствах которые достаточно мощные для анимации — они даже и не почувствуют была ли тут оптимизация, или нет.

    Прошу за весь интернет: отключайте анимации! Уговаривайте руководство отказаться от анимации, Приводите доводы, Показывайте на разных устройствах (не только на айфонах 6). Если хоть где-то тормозит, то это будет минус х10 ваших потенциальных пользователей. Фух накипело… яркий пример «крутой анимации» — сайт альфы(на хабре уже обсуждали) alfabank.ru
    • +4
      Оптимизация для слабаков, давайте жить в статичном, черно-белом мире, без анимаций :)

      Прошу за весь интернет: не отключайте анимации, ибо скучно и занудно!
      • –2
        Хотите анимации — смотрите мультфильмы. Но не надо на сайт добавлять крутилки-вертелки, фейерверки. Большинство пользователей будет долго втыкать в интерфейс, вторые будут ждать пока всё отвиснет, так и не поняв что произошло, третие вообще ничего не увидят, т.к. у них браузер в телефоне просто завис. Веб не подходит для нормальной анимации, увы. Даже у того же ютуба слегка подлагивают элементы, а хочется чтобы всё было плавно как в обычных программах
      • 0
        Смотря какой ресурс. Если нужно произвести вау-эффект, то анимации необходимы, а во всех остальных случаях лаги из-за и вылетания анимаций мешают пользоваться сайтом по назначению: получать информацию.
  • +2
    У автора опечатка в коде статьи:
    translate: transform3d…
    вместо
    transform: translate3d
  • –3
    Как прекрасно, что есть Firefox и NoScript.
    • НЛО прилетело и опубликовало эту надпись здесь
      • –2
        Как хорошо, что мало кто умеет делать css-анимации :)
  • НЛО прилетело и опубликовало эту надпись здесь

Только полноправные пользователи могут оставлять комментарии. Войдите, пожалуйста.

Самое читаемое Разработка