如何使用Profile-Guided Optimizations构建Clang和LLVM¶
简介¶
PGO (Profile-Guided Optimization,即配置引导优化) 允许你的编译器根据代码实际运行的方式更好地优化代码。用户报告称,将此应用于Clang和LLVM可以将整体编译时间减少20%。
本指南将引导你完成如何使用PGO构建Clang的过程,尽管它也适用于其他子项目,例如LLD。
如果你想使用PGO构建其他软件,请参阅PGO的最终用户文档。
使用预配置的CMake缓存¶
请参阅 https://llvm.net.cn/docs/AdvancedBuilds.html#multi-stage-pgo
使用脚本¶
我们在 utils/collect_and_build_with_pgo.py
提供了一个脚本。此脚本已在一些Linux发行版上测试过,并且需要检出 LLVM、Clang 和 compiler-rt。 尽管名称如此,它会执行四次 Clang 的全新构建,因此可能需要一段时间才能运行完成。 有关如何运行它以及可用的不同选项的更多信息,请参阅脚本的 --help
。 如果你想针对特定的用例(例如,编译特定的大型软件)最大限度地利用 PGO,请务必阅读下面关于“benchmark”选择的部分。
请注意,此脚本仅在少数Linux发行版上进行了测试。 我们非常感谢为其他平台添加支持的补丁。 :)
此脚本还支持 --dry-run
选项,该选项使其打印重要命令而不是运行它们。
选择 ‘benchmark’¶
当收集的profile代表用户计划如何使用编译器时,PGO效果最佳。 值得注意的是,如果你要以ARM为目标,那么llc构建x86_64代码的高度准确的profile并没有太大的帮助。
默认情况下,上面的脚本执行两件事以获得可靠的覆盖率。 它
运行 Clang 和 LLVM 的所有 lit 测试,以及
使用instrumented Clang构建Clang、LLVM以及所有其他可用的LLVM子项目。
总而言之,这些应该给你带来
构建 C++ 的可靠覆盖率,
构建 C 的良好覆盖率,
运行优化的出色覆盖率,
你的主机架构的后端出色覆盖率,以及
其他架构的一些覆盖率(如果其他架构是受支持的后端)。
总之,这应该涵盖 Clang 和 LLVM 的各种用途。 如果你对编译器有非常具体的需求(例如,你的编译器旨在为四个不同的平台编译大型浏览器,或类似情况),你可能需要做其他事情。 这可以在脚本本身中配置。
使用PGO构建Clang¶
如果你不想使用脚本或 cmake 缓存,这部分简要介绍如何使用 PGO 构建 Clang/LLVM。
首先,你应该在本地检出至少 LLVM、Clang 和 compiler-rt。
接下来,从高层次来说,你将需要执行以下操作
构建标准的 Release Clang 和相关的 libclang_rt.profile 库
使用你上面构建的 Clang 构建 Clang,但要使用 instrumentation
使用 instrumented Clang 生成 profile,这包括两个步骤
在代表用户将如何使用所述工具的任务上运行 instrumented Clang/LLVM/lld/etc。
使用工具将上面生成的“raw” profile转换为单个最终的 PGO profile。
使用从你的 benchmark 收集的 profile 构建最终的 release Clang(以及你需要的任何其他二进制文件)
更详细的步骤
像往常一样配置 Clang 构建。 强烈建议你为此使用 Release 配置,因为它将用于构建另一个 Clang。 因为你需要 Clang 和支持库,所以你将需要构建
all
目标(例如,ninja all
或make -j4 all
)。像上面一样配置 Clang 构建,但添加以下 CMake 参数
-DLLVM_BUILD_INSTRUMENTED=IR
– 这使我们能够构建所有带有 instrumentation 的内容。-DLLVM_BUILD_RUNTIME=No
– 一些项目在通过 profiling 构建时会产生不良交互,并且不是必须构建的。 此标志会将其关闭。-DCMAKE_C_COMPILER=/path/to/stage1/clang
- 使用我们在步骤 1 中构建的 Clang。-DCMAKE_CXX_COMPILER=/path/to/stage1/clang++
- 与上面相同。
在此构建目录中,你只需构建
clang
目标(以及你的 benchmark 所需的任何支持工具)。
如上所述,这有两个步骤:收集 profile 数据,然后将其整理成有用的形式
使用在步骤 2 中生成的 Clang 构建你的 benchmark。 推荐的“标准”benchmark是在你的 instrumented Clang 的构建目录中运行
check-clang
和check-llvm
,并使用你的 instrumented Clang 对 Clang/LLVM 进行完整构建。 因此,创建另一个构建目录,并带有以下 CMake 参数-DCMAKE_C_COMPILER=/path/to/stage2/clang
- 使用我们在步骤 2 中构建的 Clang。-DCMAKE_CXX_COMPILER=/path/to/stage2/clang++
- 与上面相同。
如果你的用户是 debug info 的爱好者,你可能需要考虑使用
-DCMAKE_BUILD_TYPE=RelWithDebInfo
而不是-DCMAKE_BUILD_TYPE=Release
。 这将为 clang 的 debug info 部分提供更好的覆盖率,但将花费更长的时间才能完成,并将导致更大的构建目录。建议使用你的 instrumented Clang 构建
all
目标,因为更多的覆盖率通常更好。
你现在应该在
path/to/stage2/profiles/
中有一些*.profraw
文件。 你需要使用llvm-profdata
合并这些文件(即使你只有一个! profile 合并也会将 profraw 转换为实际的 profile 数据)。 这可以使用/path/to/stage1/llvm-profdata merge -output=/path/to/output/profdata.prof path/to/stage2/profiles/*.profraw
完成。
现在,构建你的最终的、PGO 优化的 Clang。 为此,你将需要将以下附加参数传递给 CMake。
-DLLVM_PROFDATA_FILE=/path/to/output/profdata.prof
- 使用上一步中的 PGO profile。-DCMAKE_C_COMPILER=/path/to/stage1/clang
- 使用我们在步骤 1 中构建的 Clang。-DCMAKE_CXX_COMPILER=/path/to/stage1/clang++
- 与上面相同。
从这里,你可以构建你需要的任何目标。
注意
你可能会在构建输出中看到关于 profile 不匹配的警告。 这些通常是无害的。 要消除它们,你可以将
-DCMAKE_C_FLAGS='-Wno-backend-plugin' -DCMAKE_CXX_FLAGS='-Wno-backend-plugin'
添加到你的 CMake 调用中。
恭喜! 你现在拥有了一个使用 profile-guided optimizations 构建的 Clang,如果你愿意,可以删除除最终构建目录之外的所有目录。
如果这对你来说效果很好,并且你计划经常这样做,则可以进行一些小的优化:LLVM 和 Clang 有一个名为 tblgen 的工具,该工具在构建过程中构建和运行。 虽然在步骤 3 中构建它以获得覆盖率可能很好,但你的其他构建都不应从构建它中受益。 你可以将 CMake 选项 -DLLVM_NATIVE_TOOL_DIR=/path/to/stage1/bin
传递给步骤 2 及以后的步骤,以避免这些无用的重建。