Обратно в блог
  • mobile

Будьте в курсе всех возможностей

Разные методы оптимизации энергопотребления в мобильной разработке

Привет! Меня зовут Никита, я работаю в Яндекс Go, а именно руковожу командой скорости в приложении Про. Когда мы говорим о перформансе, обычно имеем в виду скорость запуска приложения, скорость отрисовки экранов или анимаций. Но есть ещё одна скорость — та, с которой тает заряд батареи. И это тоже очень важная часть пользовательского опыта, особенно в Про, которым исполнители могу пользоваться по несколько часов в сутки непрерывно.

Как и многие, мы измеряли производительность через метрику процессорного времени — CPU Ticks. Она хорошо показывает, сколько работы совершает приложение. Но однажды мы столкнулись с загадкой: на дашбордах всё было в порядке, процессорное время не росло, а вот батарея на устройствах пользователей стала разряжаться заметно быстрее.

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

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

Как заглянуть под капот: инструменты для измерения

Прежде чем оптимизировать энергопотребление приложения, нужно научиться это энергопотребление точно измерять. Без объективных данных любая работа превращается в гадание на кофейной гуще. К счастью, у нас, разработчиков, есть несколько подходов — от простых программных до высокоточных аппаратных.

Программный метод

Самый доступный способ — использовать Battery Counters, системные счётчики, доступные через стандартные API. В Android это в первую очередь:

  • HealthStats — API, которое даёт агрегированную информацию о работе компонентов в контексте вашего приложения. С его помощью можно узнать, например, как долго был активен экран или как часто включался GPS-модуль.
  • BatteryManager — предоставляет данные о состоянии самой батареи: остаток заряда в процентах и, на некоторых устройствах, мгновенное значение тока.

В чём их плюс? Они доступны из коробки и подходят для общего мониторинга в продакшене. В чём минус? Точность. Данные BatteryManager сильно зависят от конкретного чипа и его калибровки, а частота обновления информации может быть слишком низкой, чтобы отследить короткие всплески потребления. Это хорошая отправная точка, но для глубокого анализа её недостаточно.

Аппаратные методы

Когда программных методов не хватает, на помощь приходит старое доброе железо.

01.jpg
  • On Device Power Rails Monitor (ODPM). Это, по сути, специальный чип, встроенный в устройства Google Pixel начиная с 6-й модели. Он измеряет энергопотребление ключевых компонентов — дисплея, процессора, модема — напрямую. Получить эти данные можно через стандартный Power Profiler в Android Studio. Это мощный инструмент для детального анализа, который даёт объективную картину происходящего внутри устройства.
  • Monsoon Power Monitor. Это эталонный измерительный прибор, который используется в большинстве академических исследований. Он представляет собой внешний блок питания, который подключается к смартфону вместо аккумулятора. Это позволяет исключить влияние самой батареи на измерения и получить максимально точные данные. Вряд ли вы будете использовать его для повседневной отладки, но знать о нём полезно — когда вы читаете очередную статью об энергоэффективности, обращайте внимание, каким инструментом пользовались авторы.

Прямое и косвенное измерение — в чём разница?

Главное, что отличает эти подходы, — это способ получения данных.

  • Косвенное измерение требует проведения двух идентичных тестов в разных условиях. Например, чтобы измерить потребление дисплея, нужно запустить сценарий с включённым экраном, а затем повторить его с выключенным. Разница в энергопотреблении и будет искомым значением.
  • Прямое измерение, которое даёт ODPM, предоставляет разбивку по компонентам за один запуск. Это значительно упрощает анализ и повышает достоверность результатов.

Выбор инструмента всегда зависит от задачи. Для общего мониторинга достаточно программных методов. Но если вы хотите докопаться до истины и найти реальные источники проблем, без аппаратных измерений не обойтись.

Теперь, вооружившись правильными инструментами, давайте начнём анализ. И первый компонент, который традиционно попадает под подозрение, — это дисплей.

Первый подозреваемый: дисплей и его связь с CPU/GPU

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

Цвет имеет значение, особенно на AMOLED

Начнём с контента. Если ваше приложение работает на устройстве с AMOLED-экраном, цвет — это не просто элемент дизайна, а фактор энергоэффективности. В отличие от IPS-матриц, где подсвечивается весь экран, в AMOLED каждый пиксель светится самостоятельно. Чёрный цвет — это просто выключенный пиксель, который не потребляет энергию.

02.jpg

Исследования показывают, что разница между тёмной и светлой картинкой при одинаковой яркости экрана весьма существенна. Например, при отображении тёмных оттенков потребление дисплея может составлять 400–600 мВт, а при переключении на яркие, насыщенные цвета — вырастать до 1000 мВт и выше.

Наглядный пример из исследования погоды: приложение с тёмным фоном потребляло примерно на 20% меньше энергии, чем его аналог со светлым дневным небом.

03.jpg

Вывод очевиден: тёмная тема — это реальный инструмент для экономии заряда, особенно на AMOLED-экранах. Более того, наличие или отсутствие тёмной темы — это источник систематической ошибки в измерениях, который важно учитывать при анализе метрик скорости разряда батареи.

Управление частотой кадров — самый мощный рычаг

Ещё один и, пожалуй, самый действенный способ оптимизации — это работа с частотой отрисовки кадров. Современные дисплеи поддерживают 90, 120 и даже более высокие герцовки, но далеко не каждый сценарий в приложении требует такой плавности.

Снижая частоту генерации кадров, мы напрямую уменьшаем нагрузку на вычислительные ресурсы:

При переходе с 90 Гц на 30 Гц экономия на GPU может достигать ~45%, а на CPU — ~20%.

Но тут есть важный нюанс. Недостаточно просто начать реже генерировать кадры. Нужно сообщить об этом операционной системе, чтобы она в свою очередь могла снизить и частоту обновления самого дисплея. Например, в Android для этого можно использовать метод setFrameRate(). Если система сможет переключить экран хотя бы с 90 Гц на 60 Гц, это даст дополнительную экономию энергии самого дисплея — до 15%.

Особенно такая техника эффективна для приложений с видеоконтентом, картами или играми.

Аккуратная работа с анимациями

И наконец, мой любимый источник проблем — невидимые анимации. Это может быть что угодно: индикатор загрузки, скрытый за другим элементом, или любой другой объект, который продолжает триггерить обновление буфера SurfaceFlinger в Android, даже если пользователь его не видит. Такая, казалось бы, мелочь способна привести к огромной и совершенно бессмысленной утечке энергии. Поэтому любую проверку на аномальное энергопотребление стоит начинать именно с ревизии всех анимаций в приложении.

Итак, дисплей действительно может быть значимым потребителем, но его аппетит вполне себе можно усмирить. Оптимизация рендеринга, тёмная тема и грамотное управление частотой кадров — мощные инструменты в нашем арсенале.

Однако как показали наши дальнейшие исследования, даже идеально оптимизированный дисплей — это далеко не вся история. Настоящий энергетический вампир скрывается совсем в другом месте.

Кульминация: знакомьтесь, Cellular — настоящий энергетический вампир

Мы разобрались с дисплеем и научились управлять его потреблением. Но это не объясняло ту первоначальную аномалию, с которой всё началось: почему при стабильном процессорном времени батарея продолжала таять? Момент истины настал, когда мы увидели данные исследований популярных приложений с помощью ODPM на Google Pixel.

04.jpg

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

Чтобы понять, почему так происходит, нужно заглянуть в механику работы LTE/4G-сетей, а именно — в протокол управления радиоресурсами Radio Resource Control (RRC). Упрощённо у модема есть два основных состояния:

  • RRC_IDLE — режим ожидания с минимальным энергопотреблением (<15 мВт). Модем «спит», но готов проснуться.
  • RRC_CONNECTED — активный режим для передачи данных. Здесь потребление подскакивает до 1000–3500 мВт.

Но самое интересное кроется даже не в самих этих состояниях, а в переходе из активного состояния обратно в режим ожидания. Этот процесс не мгновенный. И здесь появляется наш главный виновник — так называемый хвост энергопотребления, или Tail Energy.

Как это выглядит на практике, хорошо иллюстрирует трассировка загрузки данных. Передача данных длилась 10 секунд, но после этого модем оставался в высокоэнергетическом состоянии ещё 10–11 секунд, потребляя около 1200 мВт абсолютно впустую. Только после этого хвоста он переходил в режим сна и наконец в RRC_IDLE.

05.jpg

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

Цена каждого запроса: группировка и частота

Насколько это критично на практике? Исследование, проведённое на iPhone с помощью Monsoon Power Monitor, даёт исчерпывающий ответ. Сравнивались три сценария:

  • Idle: приложение запущено, но никакой сетевой активности не происходит.
  • Batching: 1000 HTTP-запросов отправляются единовременно.
  • Periodic: те же 1000 запросов равномерно распределяются на 5 минут.

Результат для сети 4G был впечатляющим: группировка запросов оказалась в 2.5 раза энергоэффективнее. Всё потому, что в режиме Periodic каждый отдельный запрос активировал модем и провоцировал тот самый дорогой в энергетическом плане хвост.

Если копнуть ещё глубже и посмотреть на стоимость одного запроса в зависимости от частоты, картина становится ещё более драматичной. С увеличением интервала между запросами с 0.3 секунды до 10 секунд, стоимость энергии, потраченной на один-единственный запрос, вырастает в 33 раза.

И вишенка на торте: отправка данных примерно в 8 раз энергозатратнее, чем их получение. Теперь становится понятно, почему на графиках с ODPM у видеозвонка был такой огромный столбец потребления модема — он постоянно отправляет данные.

06.jpg

Какой вывод из всего этого можно сделать? Проблема не в том, что мы используем сеть, а в том, как мы это делаем. Неэффективная работа с сетью — особенно частые и не сгруппированные запросы — вот главная причина необъяснимого жора батареи. Теперь, когда мы знаем врага в лицо, самое время поговорить о том, как с ним бороться.

Что делать разработчику: практическое руководство по экономии

Мы выяснили, что главный энергетический вампир в современных клиент-серверных приложениях — это модем, а его аппетит напрямую зависит от стратегии сетевого взаимодействия. К счастью, у нас есть все рычаги, чтобы этот аппетит усмирить.

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

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

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

1. Группируйте запросы

Это самая эффективная оптимизация, которую вы можете сделать. Проблема Tail Energy делает частые одиночные запросы невероятно дорогими. Вместо того чтобы дёргать сеть по каждому мелкому поводу, объединяйте запросы в пачки.

Пример: у вас есть несколько независимых поллингов, которые ходят в сеть раз в минуту. Если они запускаются асинхронно, то в худшем случае вы будете будить модем каждые несколько секунд. Вместо этого спроектируйте систему так, чтобы все эти данные запрашивались одним батч-запросом раз в минуту. Экономия энергии будет существенной — как мы видели, до 2,5 раз на 4G.

Операционные системы и сами пытаются нам в этом помочь. Например, Android Doze Mode в лёгком режиме откладывает и группирует фоновые сетевые запросы, выполняя их в специальных окнах активности. Это прямое подтверждение того, что данный подход эффективен.

2. Предпочитайте получение отправке

Как показывают замеры, отправка данных обходится примерно в 8 раз дороже, чем их получение. Каждый байт, отправленный с устройства, стоит значительно больше энергии, чем байт, полученный на него.

Проведите ревизию того, что и как часто ваше приложение отправляет. Возможно, объём логов, аналитики или других служебных данных можно сократить или отправлять их реже и большими пачками.

3. Используйте современные протоколы

Протоколы вроде HTTP/2, HTTP/3 и WebSocket поддерживают постоянное соединение. Это избавляет от необходимости устанавливать новое TCP-соединение и проходить процедуру handshake для каждого запроса, как это было в HTTP/1.1. Отсутствие постоянных шейков — это прямая экономия энергии, потому что каждая установка соединения — это активная работа модема.

4. Сжимайте данные

Этот совет кажется очевидным, но его важность в контексте энергопотребления возрастает. Сжимая данные, вы не просто экономите трафик — вы сокращаете время активной работы модема. Чем быстрее данные будут переданы, тем быстрее модем сможет перейти в энергосберегающий режим. Это эффективный размен времени работы CPU на сокращение времени работы самого прожорливого компонента — модема.

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

Энергия как валюта и новый подход к проектированию

Когда пользователь нажимает на иконку вашего приложения, он совершает сделку. Но платит при этом не деньгами, а энергией своего смартфона. И от нас, разработчиков, зависит, будет ли эта сделка честной. Телефон может сильно нагреться или разрядиться быстрее, чем ожидалось, и в обоих случаях у пользователя останется неприятный осадок и гораздо меньше желания повторять этот опыт.

Наше расследование, начавшееся с простой аномалии в данных, привело нас к нескольким ключевым выводам.

Во-первых, энергия — это валюта. Современные устройства позволяют нам писать сложный UI и развесистые архитектуры, но всё это не бесплатно. Любой неэффективный код в конечном счёте разменивается на энергию.

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

И главный вывод, к которому мы пришли: энергосбережение — это не то, что можно прикрутить в конце как фичу, это архитектурное решение. Все методы, которые мы разобрали — от управления частотой кадров до группировки сетевых запросов — наиболее эффективно закладываются именно на этапе проектирования. Тогда это стоит дёшево и приносит максимальный результат.

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

  • mobile

Будьте в курсе всех возможностей

Ещё по этой теме