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

我们强烈建议利用 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]

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

语法 说明
FLAGS 表示 HSP 中的一个或多个字符,用于指示相应方法在启动类型方面必须标记为 HotStartupPost Startup

带有 H 标记表示相应方法是一种“热”方法,这意味着相应方法在应用的整个生命周期内会被调用多次。

带有 S 标记表示相应方法会在启动过程中被调用。

带有 P 标记表示相应方法会在启动之后被调用。

如果某个类出现在此文件中,则表示系统会在启动过程中使用该类,并且必须在堆中预先分配该类,以避免耗费资源来加载它。ART 编译器会采用各种优化策略,例如:对这些方法进行 AOT 编译,以及在生成的 AOT 文件中执行布局优化。
CLASS_DESCRIPTOR 目标方法的类的描述符。例如,androidx.compose.runtime.SlotTable 的描述符为 Landroidx/compose/runtime/SlotTable;。根据 Dalvik 可执行文件 (DEX) 格式,描述符中附加了 L。
METHOD_SIGNATURE 方法的签名,包括方法的名称、参数类型和返回值类型。例如:LayoutNode 上的

// LayoutNode.kt

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

具有签名 isPlaced()Z

这些格式可以包含通配符,以便让单个规则能够涵盖多个方法或类。如需获取在 Android Studio 中使用规则语法编写代码方面的指导帮助,请参阅 Android 基准配置文件插件。

下面显示了一个通配符规则示例:

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

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

基准配置文件规则支持以下类型。如需详细了解这些类型,请参阅 Dalvik 可执行文件 (DEX) 格式

字符 类型 说明
B byte 有符号字节
C char 采用 UTF-16 编码的 Unicode 字符码位
D double 双精度浮点值
F float 单精度浮点值
I int 整数
J long 长整数
S short 有符号短数值
V void Void
Z boolean true 或 false
L(类名称) reference 类名称的实例

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

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

手动收集基准配置文件

您无需设置 Macrobenchmark 库即可手动生成基准配置文件,并为关键用户历程创建界面自动化操作。虽然我们建议使用 Macrobenchmark,但这并不一定总是可行的。例如,如果您使用的是非 Gradle 构建系统,则无法使用基准配置文件 Gradle 插件。在这种情况下,您可以手动收集基准配置文件规则。如果您使用的是搭载 API 34 及更高版本的设备或模拟器,则更容易实现此目的。虽然较低的 API 级别仍然可以实现,但需要 root 访问权限,并且您需要使用运行 AOSP 映像的模拟器。您可以通过执行以下操作直接收集规则:

  1. 在测试设备上安装应用的发布版本。应用 build 类型必须经过 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 ProfileInstaller 库,该库会在 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. 等待至少 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 工具构建基准配置文件的紧凑二进制版本。

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 的发布 build 的路径,以及用于在使用 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 是从已经过混淆处理的发布 build 生成且不需要映射,则可以忽略此选项并将输出复制到 assetsdexopt 文件夹。

以传统方式安装基准配置文件

按照历来的做法,基准配置文件会通过两种方式提供给设备。

结合使用 install-multiple 与 DexMetadata

在搭载 API 28 及更高版本的设备上,Play 客户端为要安装的 APK 版本下载 APK 和 DexMetadata (DM) 载荷。DM 包含向设备上的软件包管理器传递的配置文件信息。

该 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 中都没有 ProfileInstaller(Play 中约有 45 万个应用,其中 77,000 个应用有 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 停用严格模式。不过,在大多数情况下,您应该启用严格模式。

混淆映射是可选的;它有助于将经过混淆处理的符号重新映射到人类可读的版本,以便于使用。