适用于 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.so
pgo: { instrumentation: true, benchmarks: ["dex2oat",], profile_file: "dex2oat.profdata", }
- 将以下
pgo
属性添加到libart.so
pgo: { 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。