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

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

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

You can manually generate a Baseline Profile without setting up the Macrobenchmark library and create UI automations of your critical user journeys. Although we recommend using Macrobenchmarks, it might not always be possible. For example, if you're using a non-Gradle build system, then you can't use the Baseline Profile Gradle plugin. In such cases, you can manually collect Baseline Profile rules. This is much easier if you use a device or emulator running API 34 and higher. Although it's still possible with lower API levels, it requires root access, and you need to use an emulator running an AOSP image. You can collect rules directly by doing the following:

  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, и, следовательно, они необходимы для восстановления имен классов и методов.

Strict mode is enabled by default, and this performs a compatibility check of the profile to the DEX files in the APK. If you are trying to debug profiles that were generated by another tool, you might get compatibility failures that prevent you from being able to dump for investigation. In such cases, you can disable strict mode with --strict false . However, in most cases you should keep strict mode enabled.

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

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