适用于 Android 13 及更低版本的 Android 构建系统支持对具有 blueprint 构建规则的原生 Android 模块使用 Clang 的 基于配置文件的优化 (PGO)。本页介绍了 Clang PGO、如何持续生成和更新用于 PGO 的配置文件,以及如何将 PGO 与构建系统集成(附用例)。
注意:本文档介绍了 PGO 在 Android 平台中的使用。要了解如何从 Android 应用中使用 PGO,请访问此页面。
关于 Clang PGO
Clang 可以使用两种类型的配置文件执行基于配置文件的优化
- 基于 Instrumentation 的配置文件从 Instrumentation 目标程序生成。这些配置文件非常详细,并且会产生较高的运行时开销。
- 基于采样的配置文件通常通过采样硬件计数器生成。它们的运行时开销较低,并且无需任何 Instrumentation 或修改二进制文件即可收集。它们不如基于 Instrumentation 的配置文件详细。
所有配置文件都应从代表性工作负载生成,该工作负载可体现应用的典型行为。虽然 Clang 同时支持基于 AST 的 (-fprofile-instr-generate) 和基于 LLVM IR 的 (-fprofile-generate),但 Android 仅支持基于 LLVM IR 的 Instrumentation PGO。
以下标记是构建配置文件收集所必需的
-fprofile-generate用于基于 IR 的 Instrumentation。使用此选项,后端会使用加权最小生成树方法来减少 Instrumentation 点的数量,并优化其在低权重边缘的放置(链接步骤也使用此选项)。Clang 驱动程序会自动将分析运行时 (libclang_rt.profile-arch-android.a) 传递给链接器。此库包含在程序退出时将配置文件写入磁盘的例程。-gline-tables-only用于基于采样的配置文件收集,以生成最少的调试信息。
配置文件可以用于 PGO,分别针对基于 Instrumentation 的配置文件和基于采样的配置文件使用 -fprofile-use=pathname 或 -fprofile-sample-use=pathname。
注意:由于代码发生更改,如果 Clang 无法再使用配置文件数据,则会生成 -Wprofile-instr-out-of-date 警告。
使用 PGO
使用 PGO 涉及以下步骤
- 通过将
-fprofile-generate传递给编译器和链接器来构建带有 Instrumentation 的库/可执行文件。 - 通过在 Instrumentation 二进制文件上运行代表性工作负载来收集配置文件。
- 使用
llvm-profdata实用程序后处理配置文件(有关详情,请参阅处理 LLVM 配置文件)。 - 通过将
-fprofile-use=<>.profdata传递给编译器和链接器,使用配置文件来应用 PGO。
对于 Android 中的 PGO,应离线收集 profile 文件并与代码一起检入,以确保构建的可重现性。这些 profile 文件可以随着代码的演进而使用,但必须定期重新生成(或在 Clang 警告 profile 文件已过时时重新生成)。
收集 profile 文件
Clang 可以使用通过运行基准测试(使用库的 instrumentation 构建)或在运行基准测试时采样硬件计数器来收集的 profile 文件。目前,Android 不支持使用基于采样的 profile 文件收集,因此您必须使用 instrumentation 构建来收集 profile 文件。
- 确定基准测试以及该基准测试共同执行的一组库。
- 将
pgo属性添加到基准测试和库(详见下文)。 - 使用以下命令生成带有这些库的 instrumentation 副本的 Android 构建:
make ANDROID_PGO_INSTRUMENT=benchmark
benchmark 是一个占位符,用于标识构建期间 instrumentation 的库集合。实际的代表性输入(以及可能链接到正在进行基准测试的库的另一个可执行文件)并非 PGO 特有,并且超出了本文档的范围。
- 将 instrumentation 构建刷写或同步到设备上。
- 运行基准测试以收集 profile 文件。
- 使用
llvm-profdata工具(在下面讨论)对 profile 文件进行后处理,使其可以检入到源代码树中。
在构建期间使用 profile 文件
将 profile 文件检入到 Android 树中的 toolchain/pgo-profiles 中。名称应与库的 pgo 属性的 profile_file 子属性中指定的名称匹配。构建系统在构建库时会自动将 profile 文件传递给 Clang。ANDROID_PGO_DISABLE_PROFILE_USE 环境变量可以设置为 true 以暂时禁用 PGO 并衡量其性能优势。
要指定其他产品特定的 profile 文件目录,请将它们附加到 BoardConfig.mk 中的 PGO_ADDITIONAL_PROFILE_DIRECTORIES make 变量。如果指定了其他路径,则这些路径中的 profile 文件将覆盖 toolchain/pgo-profiles 中的 profile 文件。
当使用 dist 目标生成发布镜像到 make 时,构建系统会将缺少的 profile 文件名写入 $DIST_DIR/pgo_profile_file_missing.txt。您可以检查此文件以查看哪些 profile 文件被意外删除(这会静默禁用 PGO)。
在 Android.bp 文件中启用 PGO
要在 native 模块的 Android.bp 文件中启用 PGO,只需指定 pgo 属性。此属性具有以下子属性:
| 属性 | 描述 |
|---|---|
instrumentation
|
对于使用 instrumentation 的 PGO,设置为 true。默认为 false。 |
sampling
|
对于使用 sampling 的 PGO,设置为 true。默认为 false。 |
benchmarks
|
字符串列表。如果 ANDROID_PGO_INSTRUMENT 构建选项中指定了列表中的任何基准测试,则构建此模块以进行 profiling。 |
profile_file
|
要与 PGO 一起使用的 Profile 文件(相对于 toolchain/pgo-profile)。构建系统会通过将此文件添加到 $DIST_DIR/pgo_profile_file_missing.txt 来警告此文件不存在,除非 enable_profile_use 属性设置为 false 或 ANDROID_PGO_NO_PROFILE_USE 构建变量设置为 true。 |
enable_profile_use
|
如果构建期间不应使用 profile 文件,则设置为 false。可以在引导启动期间使用以启用 profile 文件收集,或暂时禁用 PGO。默认为 true。 |
cflags
|
在 instrumentation 构建期间要使用的其他标志列表。 |
带有 PGO 的模块示例
cc_library { name: "libexample", srcs: [ "src1.cpp", "src2.cpp", ], static: [ "libstatic1", "libstatic2", ], shared: [ "libshared1", ] pgo: { instrumentation: true, benchmarks: [ "benchmark1", "benchmark2", ], profile_file: "example.profdata", } }
如果基准测试 benchmark1 和 benchmark2 执行库 libstatic1、libstatic2 或 libshared1 的代表性行为,则这些库的 pgo 属性也可以包含基准测试。Android.bp 中的 defaults 模块可以包含一组库的通用 pgo 规范,以避免为多个模块重复相同的构建规则。
要为某个架构选择不同的 profile 文件或有选择地禁用 PGO,请按架构指定 profile_file、enable_profile_use 和 cflags 属性。示例(架构目标以粗体显示)
cc_library { name: "libexample", srcs: [ "src1.cpp", "src2.cpp", ], static: [ "libstatic1", "libstatic2", ], shared: [ "libshared1", ], pgo: { instrumentation: true, benchmarks: [ "benchmark1", "benchmark2", ], } target: { android_arm: { pgo: { profile_file: "example_arm.profdata", } }, android_arm64: { pgo: { profile_file: "example_arm64.profdata", } } } }
要在基于 instrumentation 的 profiling 期间解析对 profiling 运行时库的引用,请将构建标志 -fprofile-generate 传递给链接器。使用 PGO instrumentation 的静态库、所有共享库以及直接依赖于静态库的任何二进制文件也必须进行 PGO instrumentation。但是,此类共享库或可执行文件不需要使用 PGO profile 文件,并且它们的 enable_profile_use 属性可以设置为 false。在此限制之外,您可以将 PGO 应用于任何静态库、共享库或可执行文件。
处理 LLVM profile 文件
执行 instrumentation 的库或可执行文件会在 /data/local/tmp 中生成名为 default_unique_id_0.profraw 的 profile 文件(其中 unique_id 是特定于此库的数字哈希)。如果此文件已存在,则 profiling 运行时会在写入 profile 文件时将新 profile 文件与旧 profile 文件合并。请注意,应用开发者无法访问 /data/local/tmp;他们应使用 /storage/emulated/0/Android/data/packagename/files 等位置。要更改 profile 文件的位置,请在运行时设置 LLVM_PROFILE_FILE 环境变量。
然后,使用 llvm-profdata 实用程序将 .profraw 文件(以及可能合并多个 .profraw 文件)转换为 .profdata 文件
llvm-profdata merge -output=profile.profdata <.profraw and/or .profdata files>
profile.profdata 随后可以检入到源代码树中,以便在构建期间使用。
如果在基准测试期间加载了多个 instrumentation 的二进制文件/库,则每个库都会生成一个单独的 .profraw 文件,其中包含单独的唯一 ID。通常,所有这些文件都可以合并到一个 .profdata 文件中,并用于 PGO 构建。在库由另一个基准测试执行的情况下,必须使用来自这两个基准测试的 profile 文件来优化该库。在这种情况下,llvm-profdata 的 show 选项非常有用
llvm-profdata merge -output=default_unique_id.profdata default_unique_id_0.profraw llvm-profdata show -all-functions default_unique_id.profdata
要将 unique_id 映射到各个库,请在每个 unique_id 的 show 输出中搜索特定于该库的函数名称。
案例分析:ART 的 PGO
此案例分析以 ART 为例进行介绍,以便于理解;但是,它并非对为 ART profiling 的实际库集或其相互依赖关系的准确描述。
ART 中的 dex2oat 提前编译编译器依赖于 libart-compiler.so,而 libart-compiler.so 又依赖于 libart.so。ART 运行时主要在 libart.so 中实现。编译器和运行时的基准测试将有所不同
| 基准测试 | Profiled 库 |
|---|---|
dex2oat
|
dex2oat(可执行文件)、libart-compiler.so、libart.so |
art_runtime
|
libart.so
|
- 将以下
pgo属性添加到dex2oat、libart-compiler.sopgo: { instrumentation: true, benchmarks: ["dex2oat",], profile_file: "dex2oat.profdata", } - 将以下
pgo属性添加到libart.sopgo: { instrumentation: true, benchmarks: ["art_runtime", "dex2oat",], profile_file: "libart.profdata", } - 使用以下命令为
dex2oat和art_runtime基准测试创建 instrumentation 构建:make ANDROID_PGO_INSTRUMENT=dex2oat make ANDROID_PGO_INSTRUMENT=art_runtime - 运行基准测试以执行
dex2oat和art_runtime以获得:- 来自
dex2oat的三个.profraw文件(dex2oat_exe.profdata、dex2oat_libart-compiler.profdata和dexeoat_libart.profdata),使用处理 LLVM profile 文件中描述的方法进行标识。 - 一个
art_runtime_libart.profdata。
- 来自
- 使用以下命令为
dex2oat可执行文件和libart-compiler.so生成通用的 profdata 文件:llvm-profdata merge -output=dex2oat.profdata \ dex2oat_exe.profdata dex2oat_libart-compiler.profdata - 通过合并来自两个基准测试的 profile 文件来获取
libart.so的 profile 文件:llvm-profdata merge -output=libart.profdata \ dex2oat_libart.profdata art_runtime_libart.profdata来自两个 profile 文件的
libart.so的原始计数可能不同,因为基准测试在测试用例数量和运行持续时间方面有所不同。在这种情况下,您可以使用加权合并:llvm-profdata merge -output=libart.profdata \ -weighted-input=2,dex2oat_libart.profdata \ -weighted-input=1,art_runtime_libart.profdata以上命令为来自
dex2oat的 profile 文件分配了两倍的权重。实际权重应根据领域知识或实验确定。 - 将 profile 文件
dex2oat.profdata和libart.profdata检入到toolchain/pgo-profiles中,以便在构建期间使用。
或者,使用以下命令创建一个包含所有 instrumentation 库的 instrumentation 构建:
make ANDROID_PGO_INSTRUMENT=dex2oat,art_runtime
(or)
make ANDROID_PGO_INSTRUMENT=ALL第二个命令构建所有启用 PGO 的模块以进行 profiling。