На конференции Google I/O 2022 показали инструмент Baseline Profiles, с помощью  которого можно ускорить запуск приложений после установки. Мы попробовали его у себя в Дринките и получили прирост до 20% при холодном запуске приложения! В этой статье расскажу, как внедрить инструмент, оценить его работу на production приложении, немного погружу в историю компиляторов в целом и рассмотрю более продвинутые сценарии для генерации Profile. Демонстрировать это я буду на нашем приложении Дринкит. Поехали! Baseline Profiles — это список классов и методов, которые компилируются заранее и устанавливаются вместе с приложением. Всё это улучшает время запуска и общую производительность приложения для пользователей. Чтобы понять, как Baseline Profiles позволяет ускорить запуск приложения, давайте сначала посмотрим, как устроена компиляция байткода в Android. Суть такова, что внутри .apk, который мы устанавливаем на устройство, лежат .dex файлы с байткодом. С готовым байткодом умеет работать виртуальная машина Андроида. До версии 5.0 Android работал на виртуальной машине Dalvik. На ней использовался JIT (Just-In-Time)-компилятор: код компилировался в рантайме, снижалось потребление оперативной памяти, при этом значительно снижалась производительность компиляции во время работы. Начиная с версии 5.0 начали использовать ART (Android Runtime) — улучшенную виртуальную среду. Вместе с ней — АОТ-компиляцию (ahead-of-time compilation), которая обеспечивает лучший показатель производительности благодаря предварительной компиляции всего кода. Из-за этого затраты на RAM достигали максимума. И при каждом обновлении системы пользователи наблюдали диалог, который сообщал об происходящей оптимизации приложений. В качестве оптимизированного подхода с Android 7.0 используется комбинация из обоих миров. Компилятор по умолчанию проводит JIT-компиляцию байткода, но если в процессе работы приложения будут обнаружены часто используемые участки кода, то они AOT-скомпилируются с помощью утилиты dex2oat, записывая результат компиляции в бинарные .oat файлы. То, что содержит в себе список классов и методов, которые следует скомпилировать в машинный код, называется Profile. Получается, что при первом запуске у нас нет скомпилированных кусков кода и JIT компилирует всё, что ему надо для работы, постепенно записывая в Profile то, что нужно для AOT-компиляции – критические, часто встречаемые фрагменты приложения. Каждый последующий запуск приложения переиспользует ранее скомпилированные куски кода и не компилирует их каждый раз заново. Тем самым каждый последующий запуск становится быстрее предыдущего. С Android 9.0 появились облачные профили, т.е. пользователи запускают приложения и по мере использования созданные локально профили загружаются в облако и становятся доступны всем, кто скачивает приложение из Google Play. И с этого момента новые  пользователи получают быстрый старт приложения при установке из стора. Но у этого есть небольшой минус: если в облаке ещё недоступны профили, то при первом запуске пользователи будут дольше находиться на экране загрузки. Исправить этот скачок в времени старта можно, если с приложением уже будут поставляться готовые профили — те самые Baseline Profiles. В этом и заключается принцип работы Baseline Profiles: мы заранее генерируем файлы, которые скажут Андроиду, что надо скомпилировать AOT — тогда первый запуск будет быстрее, примерно такой, как после 10–20 запусков. Теперь рассмотрим, как генерировать Baseline Profiles. Итак, в первую очередь нам нужно создать в проекте новый модуль benchmark (впрочем, вы можете выбрать любое имя модуля, которое захотите) с типом бенчмаркинга macrobenchmark. У нас создался модуль с шаблонным макробенчмарк-тестом, который мы пока не трогаем. Теперь создаём новый BaselineProfileGenerator и копируем все из Google codelab. Далее, если вы всё ещё читаете это на момент стабильной версии androidx.benchmark:benchmark-macro-junit4:1.1.*, то вам необходимо запустить этот тест на рутовом девайсе. Для этого подходит эмулятор без Google Services. Во время его работы нужно выполнить в терминале: Если же вы используете более новую версию бенчмаркинга, начиная с версии1.2.0-alpha06, androidx.benchmark:benchmark-macro-junit4:1.2.0-alpha* то сгенерировать Baseline Profile можно даже на реальном устройстве — при этом даже не потребуются root-права. Всё! Когда вы запустите тест, то на девайсе будет создан *.txt файл с сгенерированным скомпилированным кодом, который с помощью adb pull (подсказка есть в результате работы теста) можно поместить в проект в /app/scr/main Пришло время замерить, насколько быстрее начало запускаться приложение после старта Чтобы включить профили в приложение во время тестирования, нужно подключить к app-модулю библиотеку. В общем-то, это всё, что требуется для его установки, помимо наличия самого профиля в /app/src/main Сам тест для сравнения времени холодного старта будет выглядеть примерно вот так: Смысл этого теста в том, что замеряются метрики, переданные параметром metrics для приложения с заданным packageName. Переменным в тесте является compilationMode: в одном случае чистый запуск без baseline-профилей, а второй — с установкой профилей на старте приложения. Запускаем наш бенчмарк-тест: делаем несколько запусков, чтобы усреднить значения в зависимости от переданного значения iterations: По медианным значениям между двумя тестами можно сразу заметить, что запуск приложения с профилями достигает прироста в скорости на 20% А это всего лишь самый базовый флоу для генерации Baseline Profile. Google советует описать его подробно для критичного сценария пользователя. Но даже с такими показателями можно проверить, как профили поведут себя на устройствах реальных пользователей. Чтобы добавить Profile в своё приложение, никаких дополнительных действий делать не нужно — это происходит автоматически, когда копируете их в папку /app/src/main. Дальше сборку и отправляем в стор. Спустя время можно смотреть графики. Вспомним, как выглядит график времени запуска в зависимости от количества запусков, где не используются профили: Теперь возьмём нашу ближайшую версию до появления Baseline Profiles в продакшене. Видим схожую ситуацию: при релизе время старта было выше, чем обычно, из-за отсутствия уже скомпилированных профилей от накопительных запусков приложения. Видите, зелёный график начинается выше, чем синий. Это означает, что у первых клиентов, которые установили приложение, было вначале повышенное время запуска. Ситуация с включёнными в приложение профилями показывает абсолютно противоположный результат. Здесь зелёный график начинается ниже синего, что соответствует версии приложения, в которой мы добавили профили в APK. Время старта после обновления уменьшилось, чего мы и добивались. Можно попробовать предположить, почему зелёный график – с профилями – находится даже ниже среднего времени старта. По идее, он должен быть как синий. Мы пока точно не знаем, но есть такие версии: первые клиенты обновляются более новые и мощные устройства - у них в среднем всё быстрее; рандом, случайность.   А какие у вас версии? Напишите в комментарии! Следующим шагом в оптимизации времени запуска и прокачке профилей будет описание расширенного сценария. Здесь нам понадобилось реализовать работу с картой, диалогами разрешений, скроллом и ветвлению в сценарии. Для чего это нужно? Так как Profile содержит AOT-скомпилированные машинные команды, то пользователь во время сценария с меньшей вероятностью столкнётся с проблемами производительности, если сценарий уже будет скомпилирован заранее. Для генерации Baseline Profile мы выбрали следующий флоу: при запуске приложения пользователь видит карту и диалоги разрешений; он предоставляет разрешения, выбирает кофейню и переходит в меню; в меню немного проскроллит список и перейдёт к авторизации. Разберём по шагам, как закодить этот сценарий. Есть ситуация, когда UI-тест не может найти элемент на экране из-за находящегося поверх экрана системного диалога разрешений. Как вариант, это можно прокликивать руками, но так придётся делать на каждый запуск теста, поэтому проще это автоматизировать! Сначала хотели сделать всё красиво и без лишних кликов, но метод, автоматически предоставляющий разрешение, как @Rule совсем не работал. Возможно, мы что-то делали не так. Поэтому пришлось прокликивать каждый диалог отдельно. В месте, где потенциально может возникнуть разрешение, помещаем такой код. В прочем, несложно на всякий exception об отсутствии элемента на экране делать fallback на проверку разрешений, но для простоты было сделано так, как написано ниже Информацию об этом способе нашли в статье. Новые пользователи, у которых не выбрана кофейня на старте, при запуске попадают на экран карты. Если кофейня уже выбрана, то гость сразу попадает в меню со вкусными напитками и красивыми картинками. Как организовать это ветвление в сценарии? Ветвление в UI-тесте делается просто. Ищем элемент, который есть на одном экране и которого нет на другом, и ориентируемся на его наличие. Вот и всё! А внутри уже пишем сценарий, специфичный для экрана: выберем кофейню, нажмём на корзину, проскроллим список или перейдём на другой экран Когда мы начинаем сценарий с меню, нужно выполнить небольшой скролл вниз-вверх, а затем уже переходить в авторизацию по нажатию на кнопку. Для того чтобы сделать скролл, нужно найти список на экране, а затем вызвать для него метод, который выполнит скролл. Мы используем метод fling, потому что он довольно простой в использовании. Продолжаем модифицировать свой Baseline критического сценария, чтобы предоставить пользователю самый лучший и быстрый опыт использования приложения при первом старте! В итоге наш сценарий, имеющий в себе только startActivityAndWait(), перерастает в нечто большее и уже осмысленное по поведению пользователя: Baseline Profiles ускоряет первый запуск приложения за счёт того, что с приложением поставляется AOT-скомпилированный код, который выполняется при старте. Наш опыт показал, что использование Baseline Profile в приложении сокращает время старта до 20%, а при обновлении пользователям больше не приходится долго ждать запуска. Внедрить инструмент абсолютно несложно – минимальными усилиями вы сможете сделать ваши проекты лучше. Android Developers – Making apps blazing fast with Baseline Profiles  Документация Google про создания Baseline Profiles Пошаговый Google Codelab про создание Baseline Profiles Runtime Permissions – UI Testing В канале Dodo Mobile мы рассказываем про разработку приложений Додо Пиццы, Дринкит и Донер 42. Подписывайтесь, чтобы узнавать новости раньше всех (ну, почти).