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

我们强烈建议使用 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.kt

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

LayoutNode 上的方法 isPlaced() 的签名为 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 空值
Z boolean 真或假
L(类名) reference 类实例

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

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

手动收集基线配置文件

您可以手动生成基线配置文件,而无需设置 Macrobenchmark 库并创建关键用户旅程的 UI 自动化。虽然我们建议使用 Macrobenchmarks,但并不总是可行的。例如,如果您使用非 Gradle 构建系统,则无法使用基线配置文件 Gradle 插件。在这种情况下,您可以手动收集基线配置文件规则。如果您使用运行 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 Profile Installer 库,则该库会在您的 APK 首次启动时引导配置文件。这可能会干扰配置文件生成过程,因此请使用以下命令禁用它。

    adb shell am broadcast -a androidx.profileinstaller.action.SKIP_FILE $PACKAGE_NAME/androidx.profileinstaller.ProfileInstallReceiver
  3. 运行应用程序并手动浏览您要为其收集配置文件的关键用户旅程。
  4. 提示 ART 转储配置文件。如果您的 APK 依赖于 Jetpack Profile Installer 库,请使用它转储配置文件。

    adb shell am broadcast -a androidx.profileinstaller.action.SAVE_FILE $PACKAGE_NAME/androidx.profileinstaller.ProfileInstallReceiver
    adb shell am force-stop $PACKAGE_NAME
    如果您不使用 Profile Installer,请使用以下命令在模拟器上手动转储配置文件。

    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 工具的作用。基线配置文件

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 文件夹中。

基线配置文件的传统安装

基线配置文件通常以两种方式之一交付到设备。

使用 DexMetadata 的 install-multiple

在运行 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(Play 商店中约 450,000 个应用中的 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 禁用严格模式。但是,在大多数情况下,您应该保持启用严格模式。

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