Каждое большое приложение однажды сталкивается с задачей — увеличить скорость запуска. Не обошла она и приложение 2ГИС на Android. Расскажу, как команда тестирования искала причины медленного запуска.

Первые проблемы производительности 

В ноябре 2021-го на тестовом Huawei Mate 10 приложение запускалось за две секунды, а потом ещё 10 секунд рисовалась карта. То есть пользователю приходилось ждать 12 секунд, чтобы начать использовать 2ГИС — это катастрофически долго.

Проблема оказалась во включённом слое «Метро»: при запуске слоя приложение накладывает дополнительные стили, немного перерисовывая карту. То есть пользователь ждал, пока мы сначала отрендерим карту с одними стилями, а потом их перересуем.

ASAP проблему пофиксили костылём — отключили загрузку слоя при запуске. Он не запоминался, и позже пользователь включал его каждый раз самостоятельно. Со временем от костылей отказались и довели фикс до ума.

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

Ручная проверка

Первые проверки тестовых сборок были ручные. Реально ручные: тестировщик с секундомером в руках запускал приложение пять раз с временным интервалом в 10 минут. Затем снимал «показания» и высчитывал среднее арифметическое. То же самое делали и для предыдущего релиза, чтобы сравнить скорость запуска.

Естественно, это так себе решение — занимает много времени и крайне ненадёжное. 

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

Кроме этого, изменили алгоритм подсчёта:

  1. Отказались от выжидания по 10 минут между запусками приложения, так как девайс за это время может начать «отдыхать» и запустит сберегайзеры.

  2. Количество запусков увеличили до 10 (данных от пяти запусков было мало для анализа).

  3. Стали считать медиану, так как среднее арифметическое плохо работает с аномальными выбросами — подозрительно быстрым или подозрительно медленным запуском приложения.

Такие результаты замеров на тестовых девайсах плюс‑минус совпадали с информацией о скорости запуска от пользователей. Но со временем этот процесс перестал работать.

Критически долгий старт

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

Полностью переработали главный экран

Теперь он выглядит вот так.

Переделали выдачу

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

Изменили дизайн карточек компаний и зданий

А в апреле 2022 года мы зарелизили «гибрид» — возможность работать с приложением без скачивания города.

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

Автотесты

Мы сформулировали несколько гипотез, из‑за чего могут быть такие «тормоза». Но прежде чем проверять их, решили сформулировать: как достоверно понять, что мы действительно улучшили производительность?

Можно установить сборку с оптимизацией на устройство или эмулятор и запустить некоторое количество раз. Параллельно искать в логах инфу о скорости запуска и её изменениях. В целом неплохой вариант, но есть большой минус — это трудоёмко. Процесс может занимать от 15 до 30 минут, в зависимости от QA / устройства / способа забора метрик.

Кроме этого:

  1. Слишком маленький объём получаемых данных. Теоретически можно и тысячу раз запустить приложение руками, а потом смотреть на показатели скорости запуска и анализировать их. Но на это уйдёт колоссальный объём времени.

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

  3. Троттлинг. Запуск приложения — ресурсозатратная операция для устройства. Девайс совершает кучу тяжёлой работы, увеличивается потребление энергии, из-за чего возможен перегрев. Чтобы хоть как‑то это контролировать, умный телефон начнёт искусственно занижать свою работоспособность.

Мы решили: честнее всего будет сделать так, чтобы этот тестовый стенд (бенчмарк) мог выполняться на реальных устройствах. А ещё лучше — на тех устройствах, которые находятся в топе у наших пользователей.

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

Как мы поступили:

  1. Написали автотест, который запускает приложение N раз с таймаутом в M секунд. Таймаут помогает «не загонять» устройство, при этом не даст ему сильно «уснуть».

  2. Для каждого устройства подобрали оптимальный таймаут, основываясь на относительном стандартном отклонении. Грубо говоря, это процент того, насколько сильно результаты отличаются друг от друга внутри выборки. Чем процент ниже, тем меньше отличий внутри выборки. Например, при таймауте в 10 секунд мы видим, что CV = 40%. Следовательно, можно предположить, что устройству тяжело выполнять работу по запуску каждые 10 секунд, поэтому таймаут увеличиваем, а после — снова смотрим, как изменилось CV. Такую калибровку прошёл каждый девайс, на котором мы будем запускать этот автотест.

PROFIT! В итоге у нас получился автотест, который может запускать параллельно две разные версии приложения. Таким образом боремся с тем, что устройство «живёт своей жизнью». А мы можем сравнивать изменения в скорости запуска, например, между прошлой и текущей версией приложения.

После реализации бенчмарка мы смогли посмотреть, насколько проделанные оптимизации помогают.

Расставляем трейсы

Первым делом исследовали, как анализировать текущие проблемы с перформансом.

Для этого мы начали активно профилировать приложение и расставлять трейсы. Наше приложение включает себя кусочки «разного вкуса»: Qt, QML, C++, Java. Где‑то трейсы уже были расставлены, но их оказалось недостаточно. Где‑то добавили ещё и следили, что происходит в этих кусочках в базовых сценариях: запуск приложения, поиск, открытие карточки. Результаты смотрели в Perfetto.

Выявили узкие места:

  1. Не все потоки загружены, некоторые иногда простаивали без дела. 
    Как решили бороться: уплотнить работу в потоках, чтобы не было простоя.

  2. Множество вещей создавались на старте приложения, хотя для старта были не нужны. Например, элементы измерения расстояния по двум одновременным длинным тапам.
    Как решили бороться: …отложили их создание.

  1. Выявили элементы, которые не сразу нужны пользователю при открытии карточки или поиске. Например, экшн‑бар, в котором отображается маршрут, «Позвонить», «Избранное», «Показать вход», шаринг, такси и рекламный CTA.
    Как решили бороться: аналогично — на миллисекунды отодвинули момент создания этих элементов. Это позволило быстро отобразить шапку, тело и, в конечном итоге, сам экшн‑бар.

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

Проблема накопленных данных

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

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

Заподозрили, что проблема — не в функциональности приложения, а в его данных, которые накопились на устройстве. Сняли логи и просто переустановили бетку — вуаля, приложение запускается так же, как и основная версия!

Сомнений не осталось — проблема точно в данных.

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

Отказавшись всего от одного метода Directories::logDiskUsage(options);, которым собирали статистику о файлах приложения и считали их размер, мы сильно прибавили в скорости. Всего одна строчка кода — и сотня тысяч довольных пользователей.

Ускоряем открытие карточки и выдачи

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

В карточке оптимизировали открытие двумя простыми способами: 

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

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

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

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

Добавили автотесты

Чтобы контролировать регрессию, добавили автотесты.

1. Бенчмарк скорости открытия карточек с рекламой и местечек.Он делает N открытий карточек подряд: запуск приложения → открыть карточку → снять метрики → снова открыть карточку → …

Кроме этого, он делает N запусков приложения с последующим открытием карточки: запуск приложения → открыть карточку → снять метрики → закрыть приложение → открыть приложение → … 

Это делаем из-за того, что первое открытие карточки после старта приложения — тяжёлая операция.

2. Бенчмарк скорости отрисовки элементов поисковой выдачи.Также измеряем открытия карточек с рекламой. Этот бенчмарк, как и предыдущий, умеет делать N запусков приложений с последующим поиском метрик. А ещё умеет внутри одной сессии делать N замеров.

Итог

Лучше слов всё скажут скринкасты, которые мы записали на трёх устройствах разных классов: Samsung Galaxy A51, Google Pixel (первый) и Redmi Note 8 Pro.

На каждом мы запустили три разных версии 2ГИС — 6.12 (назовём её медленной), 6.15 (это самая свежая, на ней ситуация уже сильно лучше) и ещё одной, которая сейчас в разработке (условно назвали её 6.15+). В каждой версии мы выполнили простое действие — открыли карточку компании.

Сейчас на бою версия 6.20, а мы продолжаем оптимизировать. 

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


  1. NeoCode
    16.05.2023 05:13

    Скажите, а для Android 5 у вас что нибудь рабочее осталось (с показом движущегося общественного транспорта)? Я пользуюсь какой-то бетой, но она постоянно падает.


    1. Skyseek3r Автор
      16.05.2023 05:13

      2ГИС для Android 5 можно найти вот здесь, в разделе "Остальные версии можно найти в архиве"


  1. csharpreader
    16.05.2023 05:13

    del


    1. Aquahawk
      16.05.2023 05:13
      +1

      увеличить скорость

      Увеличить скорость, это сократить время


  1. Squoworode
    16.05.2023 05:13
    +1

    Например, элементы измерения расстояния по двум одновременным длинным тапам.

    Ого, чего, оказывается, там есть... А можно ли было раньше как-то узнать о таких элементах?


    1. Skyseek3r Автор
      16.05.2023 05:13

      О новых важных изменениях коротко рассказываем в сторис. Подробнее — в Доставили.
      Но вы правы, есть небольшие приятные фичи, которым уже много лет и о них знают не все. Однажды мы доделаем все роадмапы и придумаем, как их подсвечивать.


      1. ProMix
        16.05.2023 05:13
        +1

        А можно сначала подсветить, а потом роадмапы закрывать? А то толку с фич, если их не используют

        И да, сейчас какой-нибудь список таких мелочей есть чтобы посмотреть?


      1. Squoworode
        16.05.2023 05:13
        +1

        Так ведь никто не читает теги сторис. Может, лучше вынести все эти скрытые фичи на кнопки или в меню?


  1. Didimus
    16.05.2023 05:13
    +1

    Тем временем, Яндекс навигатор позволяет себе неприлично долгое время запуска. Особенно долго стартует без интернета (в лифте, например). Ну так реклама сама себя не скачает же


    1. RKrop
      16.05.2023 05:13

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


      1. Didimus
        16.05.2023 05:13

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


    1. Rusrst
      16.05.2023 05:13

      Не для них гугл макробенчмарки и профилировщики выпускал же.


  1. quaer
    16.05.2023 05:13
    +1

    Как решили бороться: …отложили их создание.

    Как решили бороться: аналогично — на миллисекунды отодвинули момент создания этих элементов

    Можно немного подробностей, как это в коде реализовано? Как координируется при этом обращение к таким объектам?

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

    Не очень понятно.

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

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

    Как вы повлияли на очередь отображения?


  1. aMster1
    16.05.2023 05:13
    +2

    А зачем вы отключили возможность выбирать пользователю когда обновлять карты?

    Раньше можно было нажать на кнопочку и обновить одну (нужную) карту, а теперь приложение само скачивает карты, когда хочет. Да, как бы есть возможность отключить обновление (правда не указано чего конкретно, карт или приложения?) через сотовые сети, но сценарий раздачи интернета в международном роуминге через 1 устройство - достаточно часто встречается в путешествиях...


  1. Akr0n
    16.05.2023 05:13
    +2

    Перестал обновлять 2Gis после выхода 6-й версии, пользуюсь 5-й. Количество добаляемого трэша и всяческих "помощников" от зеленого банка превысило все пределы терпимости.


    1. dom1n1k
      16.05.2023 05:13

      А я вот сдуру обновил и теперь жалею.


      1. Akr0n
        16.05.2023 05:13

        Мне тоже пришлось удалять и ставить старый пакет из сторонних источников.