手动创建和测量基准配置文件

我们强烈建议您使用 Jetpack Macrobenchmark 库自动生成配置文件规则,以减少手动工作并提高整体可伸缩性。但是,您也可以在应用中手动创建和测量配置文件规则。

手动定义配置文件规则

您可以通过在 src/main 目录中创建一个名为 baseline-prof.txt 的文件,在应用或库模块中手动定义配置文件规则。此目录与包含 AndroidManifest.xml 文件的文件夹相同。

该文件每行指定一条规则。每条规则都表示一个模式,用于匹配需要优化的应用或库中的方法或类。

当使用 adb shell profman --dump-classes-and-methods 时,这些规则的语法是人类可读的 ART 配置文件格式 (HRF) 的超集。该语法类似于描述符和签名的语法,但允许使用通配符来简化规则编写过程。

以下示例显示了 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]

有关详细说明,请参阅下表

语法 说明
标志 表示一个或多个字符 HSP,以指明此方法是否必须在启动类型方面被标记为 Hot(热点)、Startup(启动)或 Post Startup(启动后)。

带有 H 标志的方法表示它是“热点”方法,这意味着它在应用的生命周期内被多次调用。

带有 S 标志的方法表示它是在启动期间调用的方法。

带有 P 标志的方法表示它是在启动后调用的方法。

此文件中存在的类表示它在启动期间使用,并且必须在堆中预先分配以避免类加载的开销。ART 编译器采用各种优化策略,例如对这些方法进行 AOT 编译,并在生成的 AOT 文件中执行布局优化。
类描述符 目标方法类的描述符。例如,androidx.compose.runtime.SlotTable 的描述符为 Landroidx/compose/runtime/SlotTable;。根据 Dalvik Executable (DEX) 格式,此处预置了 L。
方法签名 方法的签名,包括方法的名称、参数类型和返回类型。例如

// LayoutNode.kt

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

LayoutNode 上具有签名 isPlaced()Z

这些模式可以使用通配符,以便一条规则可以包含多个方法或类。有关在 Android Studio 中使用规则语法编写时的指导帮助,请参阅 Android Baseline Profiles 插件。

通配符规则的示例如下所示

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

基准配置文件规则中支持的类型

基准配置文件规则支持以下类型。有关这些类型的详细信息,请参阅 Dalvik Executable (DEX) 格式

字符 类型 说明
B 字节 有符号字节
C 字符 以 UTF-16 编码的 Unicode 字符码点
D 双精度浮点数 双精度浮点值
F 浮点数 单精度浮点值
I 整型 整数
J 长整型 长整数
S 短整型 有符号短整型
V 空值
Z 布尔型 真或假
L(类名) 引用 类名的实例

此外,库可以定义打包在 AAR 工件中的规则。当您构建 APK 以包含这些工件时,规则会合并在一起(类似于清单合并的方式),并编译成特定于 APK 的紧凑型二进制 ART 配置文件。

当 APK 在设备上使用时,ART 利用此配置文件在 Android 9(API 级别 28)或使用 ProfileInstaller 时在 Android 7(API 级别 24)上安装时 AOT 编译应用的特定子集。

手动收集基准配置文件

您可以手动生成基准配置文件,而无需设置 Macrobenchmark 库并创建关键用户旅程的 UI 自动化。虽然我们建议使用 Macrobenchmark,但并非总是可行。例如,如果您使用非 Gradle 构建系统,则无法使用基准配置文件 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 工具在构建 基准配置文件 的紧凑型二进制版本时所做的工作。

Profgen-cli 有助于配置文件编译、内省和 ART 配置文件转译,因此无论目标 SDK 版本如何,它们都可以安装在 Android 设备上。

Profgen-cli 是一个 CLI,它将基准配置文件的 HRF 编译为其编译格式。该 CLI 还作为 Android SDK 的一部分随 cmdline-tools 仓库一起提供。

这些功能在 studio-main 分支中可用

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

使用 Profgen-cli 构建紧凑型二进制配置文件

Profgen-cli 可用的命令有 binvalidatedumpProfile。要查看可用命令,请使用 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

第一个参数表示 baseline-prof.txt HRF 的路径。

Profgen-cli 还需要 APK 发布版本的路径以及在使用 R8 或 Proguard 时用于混淆 APK 的混淆映射。这样,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.profbaseline.profm 输出文件复制到 APK 中的 assetsdexopt 文件夹中。

混淆映射

仅当 HRF 使用源符号时才需要提供混淆映射。如果 HRF 是从已经混淆的发布版本生成的,并且不需要映射,则可以忽略该选项并将输出复制到 assetsdexopt 文件夹中。

基准配置文件的传统安装

基准配置文件传统上通过两种方式之一交付到设备。

install-multiple 与 DexMetadata 结合使用

在运行 API 28 及更高版本的设备上,Play 客户端会下载正在安装的 APK 版本的 APK 和 DexMetadata (DM) 有效负载。DM 包含传递给设备上 Package Manager 的配置文件信息。

APK 和 DM 作为单个安装会话的一部分进行安装,例如

adb install-multiple base.apk base.dm

Jetpack ProfileInstaller

在运行 API 级别 29 及更高版本的设备上,Jetpack ProfileInstaller 库提供了一种替代机制,可在 APK 安装到设备后将打包到 assetsdexopt 中的配置文件进行安装ProfileInstallerProfileInstallReceiver 或应用直接调用。

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>

大多数包含基准配置文件的 APK(Play 商店中 45 万个应用中约有 7.7 万个)不包含 ProfileInstaller,但几乎每个使用 Compose 的 APK 都包含它。这是因为库可以在不声明对 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 偏移量,因此它需要这些偏移量来重建类名和方法名。

严格模式默认启用,它会对配置文件与 APK 中的 DEX 文件进行兼容性检查。如果您尝试调试由其他工具生成的配置文件,可能会出现兼容性失败,从而阻止您进行转储以供调查。在这种情况下,您可以使用 --strict false 禁用严格模式。但是,在大多数情况下,您应该保持严格模式启用。

混淆映射是可选的;如果提供,它有助于将混淆的符号重新映射到其人类可读的版本,以便于使用。