Производительность CSS-анимаций: нерассказанная история (с комментариями Пола Айриша)

Перевод статьи CSS animations performance: the untold story с сайта greensock.com, c разрешения автора — Джека Дойла.

У использования CSS-анимаций есть несколько интересных (и удивительных) последствий для производительности, о которых не все знают. Я столкнулся с некоторыми из этих последствий, пока тестировал их для клиента в рекламной индустрии, который настаивал на том, что GSAP принят в качестве стандарта, поэтому я записал скринкаст, поясняющий что именно я обнаружил. Я решил, что было бы полезно поделиться этим:

Резюме

  • Шкала времени записи в инструментах разработчика Chrome не показывает загрузку процессора, связанную с CSS-анимацией трансформаций,  поэтому люди часто неверно истолковывают (отсутстствующие) данные. Записи выглядят «чистыми» при CSS и «грязными» при JS, что приводит к ошибочным выводам о производительности.
  • Согласно диспетчеру задач Chrome, CSS-анимациии трансформаций нагружают процессор вдвое сильнее по сравнению с JS.
  • CSS-анимации вызывали больше задержек главного потока, чем JavaScript-анимации. Взаимодействие с пользователем обычно обрабатывается в главном потоке, и для пользователя это воспринимается как подтормаживание. Это особенно значимо, если вы анимируете трансформации одновременно с почти любым другим свойством.
  • У Webkit-браузеров есть проблемы синхронизации.
  • JavaScript был быстрее чем CSS-анимации на каждом устройстве, на котором я запускал этот тест – единственным исключением было анимирование трансформаций в Webkit-браузерах (а тут как раз проявляются эти накладные расходы в главном потоке и проблемы синхронизации).
  • Для того, чтобы самостоятельно контролировать время/замедление компонентов трансформации (поворот, масштабирование, наклон, позиция) в CSS, вы должны создать DOM-ноду для каждого компонента, что отрицательно влияет на производительность. С JavaScript в таких обходных путях нет необходимости (см. примечание ниже).
  • Я люблю Dev Tools – я вовсе не придираюсь к ним. Эти вещи просто сложно измерить.
  • Делайте ваши собственные тесты! Не сильно доверяйте Dev Tools или моим тестам. Верьте собственным глазам, потому что в конечном итоге именно восприятие имеет самое важное значение для конечного пользователя. Важны как плавное движение, так и отзывчивый пользовательский интерфейс.

Ссылки

Обновление: После записи видео, я сделал ещё несколько тестов, которые показали, что наибольший вклад в замедление в варианте с чистым CSS внесло то, что пришлось создать много вложенных элементов, чтобы добиться независимого управления компонентами трансформации. Другими словами, раздельное регулирование времени начала/конца (или темпа) поворота, масштабирования и позиции практически невозможно в чистом CSS, пока вы не вкладываете вещи таким вот образом, но это дается ценой существенных потерь в быстродействии. Когда вложенности можно было  избежать, анимация одних трансформаций на чистом CSS выглядела более плавной в webkit-браузерах при сильной нагрузке и в основном не отличалась от оптимизированной JS-анимации при любых других уровнях нагрузки.

Важные комментарии к оригинальной статье

Комментарий Пола Айриша

Привет, Джек! Я поклонник GSAP и рекомендую его для многих случаев, но, к сожалению, кое-что в этом видео не совсем верно. Из-за того, что на видео было так много деталей, я отвечу про каждую из них по очереди.

Диспетчер задач. Вы показали только лишь один процесс браузера, а хотелось бы увидеть процесс отрисовки для отдельной вкладки (и GPU). Ваши результаты имеют смысл, потому что браузер берёт на себя обработку большинства анимаций вместо блокировки главного потока. Когда вы рассматриваете другие процессы, то оба подхода покажут практически одинаковые цифры.

FPS-счётчик. Это сложно, потому что мы полагаемся на счетчик FPS, написанный на JS, который работает в главном потоке, но нас-то интересует то, как часто пиксели выводятся на экран. Поскольку кадры при композиции слоев (в видеопроцессоре) и кадры в основном потоке — разные вещи, requestAnimationFrames не может измерять производительность CSS-анимаций. (API синхронизации кадров решит эту проблему). Я бы рекомендовал использовать FPS-счётчик во вкладке «Отображение» в инструментах разработчика.

Шкала времени, не показывающая затраты на CSS-анимацию. Да, это достаточно известный баг. Посмотрите, как хорошо всё выглядит в Canary. Вы увидите тяжёлые процессы пересчёта стилей в главном потоке.

Синхронизация. Это отличная демонстрация того, что случится, если у вас есть анимация при композиции слоев и анимация в главном потоке, работающие одновременно на одном элементе, и вы постоянно не укладываетесь в лимиты для одного кадра в одном из потоков. Такого рода вещи на практике случаются не очень часто, но это то, за чем мы должны следить.

Анимирование elem.style.top. Мы с вами знаем, что 1) анимирование свойства «top» не является эффективным выбором и 2) значения top не могут быть субпиксельными, поэтому они всегда округляются до целого числа. Как раз это и приводит к более заметным рывкам анимации, чем в случае транформаций. Однако, в вашем демо ни одна из этих причин на не является определяющим фактором, поэтому это скорее обострение проблем, описанных далее…

Это неправильный бенчмарк.

(Вы заметили это позже, когда закончили видео, но… вот оно.) В CSS-варианте есть ПЯТЬ div’ов, представляющих каждый красный бокс. В вашем GSAP-варианте только один. Это различие оказывает огромное влияние на цифры; размер DOM быстро раздувает затраты на перерасчет стилей.

Вам необязательно нужны пять div’ов на один бокс для CSS, хотя я признаю, что не очень изящно независимо обрабатывать анимации только лишь с одним элементом и одним блоком @keyframes. Несмотря на это, задача решаема.

Я (быстренько) немного подправил CSS, чтобы использовать один элемент, и призываю вас взглянуть на результат: http://codepen.io/paulirish/full/9712e8fb6a451e0ee7393d01e7f59f53/

Теперь, когда оба варианта используют только один элемент на бокс, производительность между ними полностью сопоставима. Попробуйте применить translate+scale+rotate или translate+top. Между CSS и GSAP нет существенной разницы.

В конечном итоге, в этом бенчмарке производительность CSS и GSAP оказалась одинаковой. У GSAP есть преимущество в разработке и манипулировании различными свойствами трансформации независимо, а преимущество CSS в том, что для них требуется меньше байтов.

Я по-прежнему рекомендую GSAP для максимального контроля и удобства разработки; он может воплощать самые смелые задумки в анимации, и явно лучший в своем классе.

Ответ Джека Дойла

Какой продуманный ответ, Пол! Спасибо за все разъяснения. Позвольте мне ответить вам тоже:

FPS-счётчик.

Цель не имела ничего общего с точным измерением того, как часто пиксели перекрашивались (я пытался объяснить это на видео, но, должно быть, плохо справился, и, видимо, название «FPS» для этого добавило путаницы). Единственной целью этого индикатора fps было измерение производительности главного потока, поскольку JS работает только там. Я хотел показать, что могут быть затраты в главном потоке, даже когда из CSS-анимаций работают только трансформации (которые, как считается, выполняются в другом потоке, так что логично ожидать, что производительность главного потока станет лучше, а не хуже). Если я бы мог сделать видео заново, я бы объяснил это лучше. Сожалею об этом.

Анимирование elem.style.top

Совершенно верно – спасибо, что помогли разобраться. Учитывайте, что моей целью было показать, что анимирование чего-либо кроме transform/opacity вместе с ними может вызвать проблемы с синхронизацией.  Это может быть backgroundColor, padding, что угодно. «top» просто легче всего увидеть в тестах, вот и всё. Я согласен – в 98% лучше/быстрее анимировать положение, используя трансформацию (я видел случаи, когда top/left являлись гораздо более производительными, но это не показатель).

Это неправильный бенчмарк.

Вы сказали, что задача независимо контролировать компоненты трансформации (x/y/масштабирование/поворот) на одиночном элементе при помощи CSS-анимации выполнима – мне очень любопытно увидеть это. Ваш пример на codepen не решает эту задачу. Вместо этого, он работает со всеми компонентами одновременно. Вы не могли бы показать пример, который разделяет их должным образом? Это очень важно и является именно тем, что изначально побудило меня сделать весь этот тест – парень, написавший мне на почту, сообщил, что CSS-анимации могут делать всё то, что делает GSAP с точки зрения независимого контроля, но это требует вложенности, а я возражал, что вложенность, наверное, дается не даром, за нее приходится платить производительностью. Заметьте, что x/y анимируются в течение целых 10 секунд, но поворот происходит только в середине анимации (в течение 6-х секунд), а изменение масштаба только в конце (в течение 4-х секунд). Насколько мне известно, это просто невозможно при помощи CSS-анимаций (без вложенности). Разве я неправильно понимаю?

Ещё раз спасибо за то, что присоединились к беседе, Пол. Я испытываю огромное уважение лично к вам и всей команде Google. Вы, ребята, делаете потрясающие вещи для прогресса веба, особенно на фронтах борьбы за производительность.

Ответ Пола Айриша

FPS-счётчик ~= Производительность основного потока.

Понял вас. Да, имеет смысл. Мы обсуждали доступность времени простоя/загруженности процессора в API синхронизации кадров по схожим причинам.

Бенчмарк композиции слоёв

Я оговорился на счёт независимого управления компонентами трансформации.

Теперь я понимаю, что независимое управление трансформацией было требованием этих тестов с самого начала, но это не было ясно из них, т.к. они слишком сосредоточены на загрузке главного потока и FPS. Но да, низкая производительность CSS-анимаций – напрямую вытекающее следствие вложенности элементов.

Для меня главные выводы заключаются в следующем:

Если вам необходимо независимое управление трансформацией, JS существенно выигрывает, поскольку при подходе с чистым CSS количество элементов будет раздуваться. Если же нет, то производительность будет сравнима (предполагая, что оба подхода идеально реализованы).

P.S. Это тоже может быть интересно:

Оставить комментарий

Ваш e-mail не будет опубликован. Обязательные поля помечены *

Можно использовать следующие HTML-теги и атрибуты: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong>

Получать новые комментарии по электронной почте. Вы можете подписаться без комментирования.