如何使用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(例如,编译特定的大型软件),请阅读下面关于“基准测试”选择的章节。
请注意,此脚本仅在少数Linux发行版上进行了测试。与往常一样,非常欢迎添加其他平台支持的补丁。 :)
此脚本还支持--dry-run
选项,该选项会打印重要命令而不是运行它们。
选择“基准测试”¶
当收集的配置文件代表用户计划如何使用编译器时,PGO的效果最佳。值得注意的是,如果您的目标是ARM,则x86_64代码构建的llc的高精度配置文件并没有太大帮助。
默认情况下,上面的脚本执行两件事以获得良好的覆盖率。它
运行所有Clang和LLVM的lit测试,以及
使用带插装的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,但要进行插装
使用插装的Clang生成配置文件,这包括两个步骤
在代表用户如何使用这些工具的任务上运行插装的Clang/LLVM/lld等。
使用工具将上面生成的“原始”配置文件转换为单个最终PGO配置文件。
使用从基准测试中收集的配置文件构建最终的Release版Clang(以及您需要的任何其他二进制文件)
更详细的步骤
像往常一样配置Clang构建。强烈建议您为此使用Release配置,因为它将用于构建另一个Clang。因为您需要Clang和支持库,所以您需要构建
all
目标(例如ninja all
或make -j4 all
)。像上面一样配置Clang构建,但添加以下CMake参数
-DLLVM_BUILD_INSTRUMENTED=IR
– 这会导致我们构建所有带插装的内容。-DLLVM_BUILD_RUNTIME=No
– 一些项目在使用配置文件构建时存在不良交互,并且不需要构建。此标志将其关闭。-DCMAKE_C_COMPILER=/path/to/stage1/clang
- 使用我们在步骤1中构建的Clang。-DCMAKE_CXX_COMPILER=/path/to/stage1/clang++
- 与上面相同。
在此构建目录中,您只需要构建
clang
目标(以及基准测试所需的任何支持工具)。
如上所述,这有两个步骤:收集配置文件数据,然后将其转换为有用的形式
使用步骤2中生成的Clang构建您的基准测试。“标准”建议的基准测试是在插装的Clang的构建目录中运行
check-clang
和check-llvm
,并使用插装的Clang完整构建Clang/LLVM。因此,创建另一个构建目录,并使用以下CMake参数:-DCMAKE_C_COMPILER=/path/to/stage2/clang
- 使用我们在步骤2中构建的Clang。-DCMAKE_CXX_COMPILER=/path/to/stage2/clang++
- 与上面相同。
如果您的用户喜欢调试信息,您可能需要考虑使用
-DCMAKE_BUILD_TYPE=RelWithDebInfo
而不是-DCMAKE_BUILD_TYPE=Release
。这将提供对clang的调试信息部分的更好覆盖率,但完成时间更长,并且会产生更大的构建目录。建议使用插装的Clang构建
all
目标,因为更多的覆盖率通常更好。
您现在应该在
path/to/stage2/profiles/
中有一些*.profraw
文件。您需要使用llvm-profdata
合并这些文件(即使您只有一个!配置文件合并会将profraw转换为实际的配置文件数据)。这可以通过/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配置文件。-DCMAKE_C_COMPILER=/path/to/stage1/clang
- 使用我们在步骤1中构建的Clang。-DCMAKE_CXX_COMPILER=/path/to/stage1/clang++
- 与上面相同。
从这里,您可以构建所需的任何目标。
注意
您可能会在构建输出中看到有关配置文件不匹配的警告。这些通常是无害的。要消除它们,您可以在CMake调用中添加
-DCMAKE_C_FLAGS='-Wno-backend-plugin' -DCMAKE_CXX_FLAGS='-Wno-backend-plugin'
。
恭喜!您现在拥有了一个使用配置文件引导优化构建的Clang,如果您愿意,可以删除除最终构建目录之外的所有目录。
如果这对您来说效果很好,并且您计划经常这样做,则可以进行一项小的优化:LLVM和Clang有一个名为tblgen的工具,它在构建过程中构建和运行。虽然在步骤3中将其构建为覆盖率的一部分可能很好,但您的其他构建都不应该从中受益。您可以将CMake选项-DLLVM_NATIVE_TOOL_DIR=/path/to/stage1/bin
传递到步骤2及以后,以避免这些无用的重新构建。