我们强烈建议使用 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 |
表示一个或多个字符 H 、S 和 P ,以指示此方法在启动类型方面是否必须标记为 Hot 、Startup 或 Post 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 镜像的模拟器。您可以通过执行以下操作直接收集规则。
- 在测试设备上安装应用程序的发布版本。应用程序构建类型必须是 R8 优化的且不可调试的,才能获得准确的配置文件。
- 确保配置文件尚未编译。
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
- 运行应用程序并手动浏览您要为其收集配置文件的关键用户旅程。
- 提示 ART 转储配置文件。如果您的 APK 依赖于 Jetpack Profile Installer 库,请使用它转储配置文件。
如果您不使用 Profile Installer,请使用以下命令在模拟器上手动转储配置文件。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
- 等待至少五秒钟以让配置文件生成完成。
- 将生成的二进制配置文件转换为文本。
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
- 使用
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 提供的命令有 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
第一个参数表示 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.prof
和 baseline.profm
输出文件复制到 APK 中的 assets
或 dexopt
文件夹中。
混淆映射
仅当 HRF 使用源符号时,才需要提供混淆映射。如果 HRF 是从已混淆的发布版本生成的,并且不需要映射,则可以忽略该选项并将输出复制到 assets
或 dexopt
文件夹中。
基线配置文件的传统安装
基线配置文件通常以两种方式之一交付到设备。
使用 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 安装到设备上后安装打包到 assets
或 dexopt
中的配置文件。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>
大多数包含基线配置文件的 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
禁用严格模式。但是,在大多数情况下,您应该保持启用严格模式。
混淆映射 是可选的;提供时,它有助于将混淆的符号重新映射到其人类可读版本,以方便使用。
为您推荐
- 注意:当 JavaScript 关闭时,将显示链接文本。
- SQLite 性能最佳实践
- 基线配置文件 {:#baseline-profiles}
- 卡住的部分唤醒锁