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

Мы настоятельно рекомендуем автоматизировать генерацию правил профилирования с помощью библиотеки 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 (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 .

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

Вы можете вручную сгенерировать базовый профиль без настройки библиотеки Macrobenchmark и создать автоматизацию пользовательского интерфейса для критически важных сценариев взаимодействия с пользователем. Хотя мы рекомендуем использовать Macrobenchmarks, это не всегда возможно. Например, если вы используете систему сборки, отличную от Gradle, то вы не сможете использовать плагин Baseline Profile Gradle. В таких случаях вы можете вручную собрать правила базового профиля. Это намного проще, если вы используете устройство или эмулятор с 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 — это интерфейс командной строки, который компилирует HRF базового профиля в его собственный формат. Этот интерфейс командной строки также входит в репозиторий 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 ProfileInstaller

На устройствах с 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 000 из 450 000 приложений в Play Store), хотя он присутствует практически в каждом 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

Проверка

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

Выгрузить содержимое бинарного профиля

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

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 . Однако в большинстве случаев следует оставлять строгий режим включенным.

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

{% verbatim %} {% endverbatim %} {% verbatim %} {% endverbatim %}