Вручную создавайте и измеряйте базовые профили

Мы настоятельно рекомендуем автоматизировать генерацию правил профиля с помощью библиотеки Jetpack Macrobenchmark, чтобы сократить ручной труд и повысить общую масштабируемость. Однако вы можете вручную создавать и измерять правила профиля в вашем приложении.

Определите правила профиля вручную

Вы можете вручную определить правила профиля в приложении или модуле библиотеки, создав файл baseline-prof.txt в каталоге src/main . В этой же папке находится файл AndroidManifest.xml .

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

Синтаксис этих правил представляет собой надмножество удобного для восприятия формата профиля ART (HRF) при использовании adb shell profman --dump-classes-and-methods . Синтаксис аналогичен синтаксису дескрипторов и сигнатур , но позволяет использовать подстановочные знаки для упрощения процесса написания правил.

В следующем примере показаны несколько правил базового профиля, включенных в библиотеку Jetpack Compose:

HSPLandroidx/compose/runtime/ComposerImpl;->updateValue(Ljava/lang/Object;)V
HSPLandroidx/compose/runtime/ComposerImpl;->updatedNodeCount(I)I
HLandroidx/compose/runtime/ComposerImpl;->validateNodeExpected()V
PLandroidx/compose/runtime/CompositionImpl;->applyChanges()V
HLandroidx/compose/runtime/ComposerKt;->findLocation(Ljava/util/List;I)I
Landroidx/compose/runtime/ComposerImpl;

Вы можете попробовать изменить правила профиля в этом примере проекта Compiler Explorer . Обратите внимание, что Compiler Explorer поддерживает только понятный человеку формат профиля ART (HRF), поэтому подстановочные знаки не поддерживаются.

Синтаксис правила

Эти правила принимают одну из двух форм в зависимости от того, направлены ли они на методы или классы:

[FLAGS][CLASS_DESCRIPTOR]->[METHOD_SIGNATURE]

Правило класса использует следующий шаблон:

[CLASS_DESCRIPTOR]

Подробное описание смотрите в следующей таблице:

Синтаксис Описание
FLAGS Представляет один или несколько символов H , S и P указывающих, должен ли этот метод быть помечен как Hot , Startup или Post Startup в отношении типа запуска.

Метод с флагом H указывает, что это «горячий» метод, то есть он вызывается много раз в течение жизненного цикла приложения.

Метод с флагом S указывает, что это метод, вызываемый во время запуска.

Метод с флагом P указывает, что это метод, вызываемый после запуска.

Наличие класса в этом файле указывает на то, что он используется при запуске и должен быть предварительно выделен в куче, чтобы избежать затрат на загрузку классов. Компилятор ART использует различные стратегии оптимизации, такие как AOT-компиляция этих методов и оптимизация компоновки в сгенерированном AOT-файле.
CLASS_DESCRIPTOR Дескриптор класса целевого метода. Например, androidx.compose.runtime.SlotTable имеет дескриптор Landroidx/compose/runtime/SlotTable; Здесь добавляется префикс L в соответствии с форматом исполняемого файла Dalvik (DEX) .
METHOD_SIGNATURE Сигнатура метода, включая имя, типы параметров и типы возвращаемых значений. Например:

// LayoutNode.kt

fun isPlaced():Boolean {
// ...
}

в LayoutNode есть сигнатура isPlaced()Z .

Эти шаблоны могут содержать подстановочные знаки, чтобы одно правило охватывало несколько методов или классов. Инструкции по написанию правил с использованием синтаксиса в Android Studio см. в плагине Android Baseline Profiles .

Пример правила подстановочных знаков может выглядеть примерно так:

HSPLandroidx/compose/ui/layout/**->**(**)**

Поддерживаемые типы в правилах базового профиля

Правила базового профиля поддерживают следующие типы. Подробнее об этих типах см. в описании формата Dalvik Executable (DEX) .

Характер Тип Описание
B байт Знаковый байт
C чар Кодовая точка символа Unicode, закодированная в UTF-16
D двойной Значение с плавающей запятой двойной точности
F плавать Значение с плавающей запятой одинарной точности
I инт Целое число
J длинный Длинное целое число
S короткий Подписано коротко
V пустота Пустота
Z булев Правда или ложь
L (имя класса) ссылка Экземпляр имени класса

Кроме того, библиотеки могут определять правила, упакованные в артефакты AAR. При сборке APK с этими артефактами правила объединяются (аналогично слиянию манифестов) и компилируются в компактный двоичный профиль ART, специфичный для APK.

ART использует этот профиль, когда APK используется на устройствах для AOT-компиляции определенного подмножества приложения во время установки на Android 9 (уровень API 28) или Android 7 (уровень API 24) при использовании ProfileInstaller .

Собирайте базовые профили вручную

Вы можете вручную сгенерировать базовый профиль (Baseline Profile) без настройки библиотеки Macrobenchmark и создать автоматизацию пользовательского интерфейса для критически важных действий пользователя. Хотя мы рекомендуем использовать Macrobenchmarks, это не всегда возможно. Например, если вы используете систему сборки, отличную от Gradle, вы не сможете использовать плагин Gradle Baseline Profile. В таких случаях вы можете вручную собирать правила базового профиля. Это гораздо проще, если вы используете устройство или эмулятор с API 34 и выше. Хотя это возможно и с более низкими уровнями API, для этого требуются права root и необходимо использовать эмулятор с запущенным образом AOSP. Вы можете собирать правила напрямую, выполнив следующие действия:

  1. Установите релизную версию вашего приложения на тестовое устройство. Тип сборки приложения не должен быть оптимизирован для R8 и не должен допускать отладки для получения профиля, который может использоваться системой сборки.
  2. Отключите установку профиля и закройте приложение.

    Если ваш APK-файл зависит от библиотеки Jetpack Profile Installer , эта библиотека загружает профиль при первом запуске APK-файла. Это может помешать процессу генерации профиля, поэтому отключите её с помощью следующей команды:

    adb shell am broadcast -a androidx.profileinstaller.action.SKIP_FILE WRITE_SKIP_FILE $PACKAGE_NAME/androidx.profileinstaller.ProfileInstallReceiver
  3. Сбросьте компиляцию приложения и очистите все профили.

    API 34 и выше

    adb shell cmd package compile -f -m verify $PACKAGE_NAME
    adb shell pm art clear-app-profiles $PACKAGE_NAME

    API 33 и ниже

    adb root
    adb shell cmd package compile --reset $PACKAGE_NAME

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

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

  6. Выполните сохранение и дождитесь его завершения. Если ваш APK-файл зависит от библиотеки Jetpack Profile Installer, используйте её для дампа профилей:

    adb shell am broadcast -a androidx.profileinstaller.action.SAVE_PROFILE $PACKAGE_NAME/androidx.profileinstaller.ProfileInstallReceiver
    sleep 1 # wait 1 second
    adb shell am force-stop $PACKAGE_NAME
    Если вы не используете Profile Installer, вручную загрузите профили на эмулятор с помощью следующей команды:

    adb root
    adb shell killall -s SIGUSR1 $PACKAGE_NAME
    sleep 1 # wait 1 second
    adb shell am force-stop $PACKAGE_NAME

  7. Преобразуйте сгенерированные двоичные профили в текст:

    API 34 и выше

    adb shell pm dump-profiles --dump-classes-and-methods $PACKAGE_NAME

    API 33 и ниже

    Определите, был ли создан ссылочный или текущий профиль. Ссылочный профиль находится в следующем месте:

    /data/misc/profiles/ref/$$PACKAGE_NAME/primary.prof

    Текущий профиль находится в следующем месте:

    /data/misc/profiles/cur/0/$PACKAGE_NAME/primary.prof

    Определите местоположение APK:

    adb root
    adb shell pm path $PACKAGE_NAME

    Выполним преобразование:

    adb root
    adb shell profman --dump-classes-and-methods --profile-file=$PROFILE_PATH --apk=$APK_PATH > /data/misc/profman/$PACKAGE_NAME-primary.prof.txt

  8. Используйте adb для извлечения сброшенного профиля с устройства:

    adb pull /data/misc/profman/$PACKAGE_NAME-primary.prof.txt PATH_TO_APP_MODULE/src/main/

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

Измерение улучшений приложения вручную

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

PACKAGE_NAME=com.example.app
# Force Stop App
adb shell am force-stop $PACKAGE_NAME
# Reset compiled state
adb shell cmd package compile --reset $PACKAGE_NAME
# Measure App startup
# This corresponds to `Time to initial display` metric.
adb shell am start-activity -W -n $PACKAGE_NAME/.ExampleActivity \
 | grep "TotalTime"

Затем загрузите базовый профиль.

# Unzip the Release APK first.
unzip release.apk
# Create a ZIP archive.
# The name should match the name of the APK.
# Copy `baseline.prof{m}` and rename it `primary.prof{m}`.
cp assets/dexopt/baseline.prof primary.prof
cp assets/dexopt/baseline.profm primary.profm
# Create an archive.
zip -r release.dm primary.prof primary.profm
# Confirm that release.dm only contains the two profile files:
unzip -l release.dm
# Archive:  release.dm
#   Length      Date    Time    Name
# ---------  ---------- -----   ----
#      3885  1980-12-31 17:01   primary.prof
#      1024  1980-12-31 17:01   primary.profm
# ---------                     -------
#                               2 files
# Install APK + Profile together.
adb install-multiple release.apk release.dm

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

# Check dexopt state.
adb shell dumpsys package dexopt | grep -A 1 $PACKAGE_NAME

Вывод должен содержать информацию о том, что пакет скомпилирован:

[com.example.app]
  path: /data/app/~~YvNxUxuP2e5xA6EGtM5i9A==/com.example.app-zQ0tkJN8tDrEZXTlrDUSBg==/base.apk
  arm64: [status=speed-profile] [reason=install-dm]

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

# Force stop app
adb shell am force-stop $PACKAGE_NAME
# Measure app startup
adb shell am start-activity -W -n $PACKAGE_NAME/.ExampleActivity \
 | grep "TotalTime"

Базовые профили и профген

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

Profgen-cli помогает в компиляции профилей, интроспекции и транспиляции профилей ART, поэтому их можно устанавливать на устройствах под управлением Android независимо от целевой версии SDK.

Profgen-cli — это CLI-интерфейс, который компилирует HRF базового профиля в скомпилированный формат. CLI также входит в репозиторий cmdline-tools в составе Android SDK.

Эти функции доступны в studio-main :

 ../cmdline-tools/latest/bin
apkanalyzer
avdmanager
lint
profgen
retrace
screenshot2
sdkmanager

Создавайте компактные бинарные профили с помощью Profgen-cli

В Profgen-cli доступны команды bin , validate и dumpProfile . Чтобы просмотреть доступные команды, используйте profgen --help :

  profgen --help
Usage: profgen options_list
Subcommands:
    bin - Generate Binary Profile
    validate - Validate Profile
    dumpProfile - Dump a binary profile to a HRF

Options:
    --help, -h -> Usage info

Используйте команду bin для создания компактного двоичного профиля. Ниже приведён пример вызова:

profgen bin ./baseline-prof.txt \
  --apk ./release.apk \
  --map ./obfuscation-map.txt \
  --profile-format v0_1_0_p \
  --output ./baseline.prof \

Чтобы увидеть доступные параметры, используйте profgen bin options_list :

Usage: profgen bin options_list
Arguments:
    profile -> File path to Human Readable profile { String }
Options:
    --apk, -a -> File path to apk (always required) { String }
    --output, -o -> File path to generated binary profile (always required)
    --map, -m -> File path to name obfuscation map { String }
    --output-meta, -om -> File path to generated metadata output { String }
    --profile-format, -pf [V0_1_0_P] -> The ART profile format version
      { Value should be one of [
         v0_1_5_s, v0_1_0_p, v0_0_9_omr1, v0_0_5_o, v0_0_1_n
        ]
      }
    --help, -h -> Usage info

Первый аргумент представляет собой путь к HRF-файлу baseline-prof.txt .

Profgen-cli также необходим путь к релизной сборке APK и карта обфускации , которая используется для обфускации APK при использовании R8 или Proguard. Таким образом, profgen сможет преобразовать исходные символы в HRF в соответствующие им обфусцированные имена при сборке скомпилированного профиля.

Поскольку форматы профилей ART несовместимы ни вперед, ни назад, предоставьте формат профиля, чтобы profgen упаковывал метаданные профиля ( profm ), которые можно было бы использовать для перекодирования одного формата профиля ART в другой при необходимости.

Форматы профилей и версии платформы

При выборе формата профиля доступны следующие опции:

Формат профиля Версия платформы уровень API
v0_1_5_s Android S+ 31+
v0_1_0_p Android P, Q и R 28-30
v0_0_9_omr1 Android O MR1 27
v0_0_5_o Android O 26
v0_0_1_n Android N 24-25

Скопируйте выходные файлы baseline.prof и baseline.profm в папку assets или dexopt в APK.

Карты запутывания

Карту обфускации нужно предоставлять только в том случае, если HRF использует исходные символы. Если HRF сгенерирован из релизной сборки, которая уже обфусцирована, и сопоставление не требуется, вы можете проигнорировать этот параметр и скопировать выходные данные в папку assets или dexopt .

Традиционная установка базовых профилей

Базовые профили традиционно доставляются на устройство одним из двух способов.

Используйте install-multiple с DexMetadata

На устройствах с API 28 и более поздними версиями клиент Play загружает APK и полезную нагрузку DexMetadata (DM) для устанавливаемой версии APK. DM содержит информацию о профиле, которая передаётся в диспетчер пакетов на устройстве.

APK и DM устанавливаются в рамках одного сеанса установки с использованием чего-то вроде:

adb install-multiple base.apk base.dm

Установщик профилей Jetpack

На устройствах с API уровня 29 и выше библиотека Jetpack ProfileInstaller предоставляет альтернативный механизм установки профиля, упакованного в assets или dexopt после установки APK на устройство. ProfileInstaller вызывается ProfileInstallReceiver или непосредственно приложением.

Библиотека ProfileInstaller перекодирует профиль на основе версии SDK целевого устройства и копирует профиль в каталог cur на устройстве (специфичный для пакета промежуточный каталог для профилей ART на устройстве).

Когда устройство находится в режиме ожидания, профиль подхватывается процессом bg-dexopt на устройстве.

Загрузка базового профиля

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

Трансляция с androidx.profileinstaller

На устройствах с API 24 и более поздними версиями вы можете передать команду на установку профиля:

# Broadcast the install profile command - moves binary profile from assets
#     to a location where ART uses it for the next compile.
#     When successful, the following command prints "1":
adb shell am broadcast \
    -a androidx.profileinstaller.action.INSTALL_PROFILE \
    <pkg>/androidx.profileinstaller.ProfileInstallReceiver

# Kill the process
am force-stop <pkg>

# Compile the package based on profile
adb shell cmd package compile -f -m speed-profile <pkg>

ProfileInstaller отсутствует в большинстве APK-файлов с базовыми профилями (а это примерно 77 тысяч из 450 тысяч приложений в Google Play), хотя он присутствует практически в каждом APK-файле, использующем Compose. Это связано с тем, что библиотеки могут предоставлять профили без объявления зависимости от ProfileInstaller. Добавление зависимости в каждую библиотеку с профилем применяется, начиная с Jetpack.

Используйте install-multiple с profgen или DexMetaData

На устройствах с API 28 и более поздними версиями можно загрузить базовый профиль без необходимости наличия библиотеки ProfileInstaller в приложении.

Для этого используйте Profgen-cli:

profgen extractProfile \
        --apk app-release.apk \
        --output-dex-metadata app-release.dm \
        --profile-format V0_1_5_S # Select based on device and the preceding table.

# Install APK and the profile together
adb install-multiple appname-release.apk appname-release.dm

Для поддержки разделения APK выполните описанные выше шаги по извлечению профиля один раз для каждого APK. Во время установки передайте каждый APK и связанный с ним .dm -файл, убедившись, что имена APK и .dm совпадают:

adb install-multiple appname-base.apk appname-base.dm \
appname-split1.apk appname-split1.dm

Проверка

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

Сбросить содержимое двоичного профиля

Для анализа содержимого компактной двоичной версии базового профиля используйте опцию Profgen-cli dumpProfile :

Usage: profgen dumpProfile options_list
Options:
    --profile, -p -> File path to the binary profile (always required)
    --apk, -a -> File path to apk (always required) { String }
    --map, -m -> File path to name obfuscation map { String }
    --strict, -s [true] -> Strict mode
    --output, -o -> File path for the HRF (always required) { String }
    --help, -h -> Usage info

dumpProfile нуждается в APK, поскольку компактное двоичное представление хранит только смещения DEX и, следовательно, они нужны ему для восстановления имен классов и методов.

Строгий режим включен по умолчанию и выполняет проверку совместимости профиля с DEX-файлами в APK. При отладке профилей, созданных другим инструментом, могут возникнуть ошибки совместимости, из-за которых будет невозможно выполнить дамп для анализа. В таких случаях строгий режим можно отключить с помощью --strict false . Однако в большинстве случаев строгий режим следует оставлять включенным.

Карта обфускации необязательна; если она указана, она помогает преобразовать запутанные символы в их удобочитаемые версии для удобства использования.

{% дословно %} {% endverbatim %} {% дословно %} {% endverbatim %}