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

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

手动定义配置文件规则

您可以通过在应用或库模块中创建一个名为 baseline-prof.txt 的文件来手动定义配置文件规则,该文件位于 src/main 目录中。这与包含 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;

您可以在此 示例编译器浏览器项目 中尝试修改配置文件规则。请注意,编译器浏览器仅支持可读 ART 配置文件格式 (HRF),因此不支持通配符。

规则语法

这些规则采用两种形式之一来定位方法或类

[FLAGS][CLASS_DESCRIPTOR]->[METHOD_SIGNATURE]

类规则使用以下模式

[CLASS_DESCRIPTOR]

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

语法 描述
标志 表示一个或多个字符 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 Baseline Profiles 插件。

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

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

Baseline Profile 规则中支持的类型

Baseline Profile 规则支持以下类型。有关这些类型的详细信息,请参阅 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 编译。

手动收集 Baseline Profiles

您可以手动生成 Baseline Profile,无需设置 Macrobenchmark 库并创建关键用户旅程的 UI 自动化。尽管我们建议使用 Macrobenchmarks,但这并不总是可能的。例如,如果您使用的是非 Gradle 构建系统,则无法使用 Baseline Profile 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 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/
    

这会提取生成的配置文件规则并将其安装到您的应用程序模块中。下次构建应用程序时,将包含 Baseline Profile。请按照 安装问题 中的步骤验证这一点。

手动衡量应用程序改进

我们强烈建议您通过基准测试来衡量应用程序的改进。但是,如果您想手动衡量改进,则可以从测量未优化的 应用程序启动 以供参考开始。

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"

接下来,侧载 Baseline Profile。

# 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"

Baseline Profiles 和 profgen

本节介绍profgen工具在构建 Baseline Profile 的紧凑二进制版本时执行的操作。

Profgen-cli 帮助编译、内省和转译 ART 配置文件,以便无论目标 SDK 版本如何,都可以将其安装在 Android 设备上。

Profgen-cli 是一个 CLI,用于将 Baseline Profile 的 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 文件夹中。

Baseline Profiles 的传统安装

Baseline Profiles 通常通过以下两种方式之一交付到设备。

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 库提供了一种替代机制来安装打包到 assetsdexopt 中的配置文件,前提是 APK 已安装在设备上。 ProfileInstallerProfileInstallReceiver 或应用程序直接调用。

ProfileInstaller 库根据目标设备 SDK 版本对配置文件进行转码,并将配置文件复制到设备上的 cur 目录(设备上 ART 配置文件的软件包特定暂存目录)。

设备空闲后,设备上的 bg-dexopt 进程会拾取配置文件。

侧载 Baseline Profile

本节介绍如何在给定 APK 的情况下安装 Baseline Profile。

使用 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>

大多数带有 Baseline Profiles 的 APK 中都没有 ProfileInstaller(大约占 Play 中 450K 个应用程序中的 77K),尽管它实际上存在于每个使用 Compose 的 APK 中。这是因为库可以在不声明对 ProfileInstaller 的依赖的情况下提供配置文件。从 Jetpack 开始,在每个包含配置文件的库中添加依赖项。

install-multiple 与 profgen 或 DexMetaData 一起使用

在运行 API 28 及更高版本的设备上,您可以侧载 Baseline Profile,而无需在应用程序中包含 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

验证

要验证配置文件是否已正确安装,您可以使用手动测量应用改进中的步骤。

转储二进制配置文件的内容

要检查 Baseline Profile 的紧凑二进制版本的內容,请使用 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 禁用严格模式。但是,在大多数情况下,您应该保持严格模式启用。

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