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

Мы настоятельно рекомендуем автоматизировать создание правил профиля с помощью библиотеки 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 голец Кодовая точка символа Юникода, закодированная в 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, вы не сможете использовать плагин Gradle Baseline Profile. В таких случаях вы можете вручную собрать правила базового профиля. Это намного проще, если вы используете устройство или эмулятор с API 34 и выше. Хотя это все еще возможно с более низкими уровнями API, для этого требуется root-доступ, и вам необходимо использовать эмулятор, на котором работает образ AOSP. Вы можете собирать правила напрямую, выполнив следующие действия:

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

    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
    

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

    adb shell am broadcast -a androidx.profileinstaller.action.SKIP_FILE $PACKAGE_NAME/androidx.profileinstaller.ProfileInstallReceiver
    
  3. Запустите приложение и вручную пройдитесь по важным пользовательским маршрутам, для которых вы хотите собрать профиль.
  4. Подскажите ART сбросить профили. Если ваш APK зависит от библиотеки установщика профилей Jetpack, используйте ее для дампа профилей:

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

    adb root
    adb shell killall -s SIGUSR1 $PACKAGE_NAME
    adb shell am force-stop $PACKAGE_NAME
    
  5. Подождите не менее пяти секунд, чтобы завершить создание профиля.
  6. Преобразуйте сгенерированные двоичные профили в текст:

    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
    

  7. Используйте 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 Андроид С+ 31+
v0_1_0_p Android P, Q и R 28-30
v0_0_9_omr1 Андроид О MR1 27
v0_0_5_o Андроид О 26
v0_0_1_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 тысяч приложений в 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 . Однако в большинстве случаев вам следует оставить строгий режим включенным.

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

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