DTLTO(分布式 ThinLTO)¶
分布式 ThinLTO(DTLTO)¶
分布式 ThinLTO(DTLTO)允许在链接步骤中通过外部分发系统(例如 Incredibuild)来分发后端的 ThinLTO 编译任务。
DTLTO 扩展了现有的 ThinLTO 分发支持,该支持使用独立的 thin-link、后端编译和 link 步骤。有关“独立 thin-link”方法的文档请参见:
https://blog.llvm.org/2016/06/thinlto-scalable-and-incremental-lto.html
使用“独立 thin-link”方法要求构建系统能够处理由各个 summary index 文件动态指定的依赖,例如 Bazel。DTLTO 消除了这一要求,使其可用于任何支持进程内(in-process)ThinLTO 的构建流程。
下列命令展示了基本示例中“独立 thin-link”方法的步骤:
- clang -flto=thin -O2 t1.c t2.c -c
- clang -flto=thin -O2 t1.o t2.o -fuse-ld=lld -Wl,–thinlto-index-only
- clang -O2 -o t1.native.o t1.o -c -fthinlto-index=t1.o.thinlto.bc
- clang -O2 -o t2.native.o t2.o -c -fthinlto-index=t2.o.thinlto.bc
- clang t1.native.o t2.native.o -o a.out -fuse-ld=lld
使用 DTLTO,步骤 2 到 5 会作为链接步骤的一部分被内部执行。上述等价的 DTLTO 命令为:
clang -flto=thin -O2 t1.o t2.o -fuse-ld=lld -fthinlto-distributor=<distributor_process>
对于 DTLTO,LLD 为每个 ThinLTO 后端编译任务准备如下内容:
- 一个单独的索引文件和输入/输出文件列表(对应上面步骤 2)。
- 一个用于执行 ThinLTO 后端编译的 Clang 命令行。
这些信息通过 JSON 文件传递给 distributor_process,由其使用分发系统执行后端编译(对应上面步骤 3 和 4)。一旦完成,LLD 将把编译生成的本机目标文件整合回链接流程并完成链接(对应步骤 5)。
这种设计将分发系统的细节隔离出 LLVM 源代码之外。
LLVM 源树中包含了一个示例分发器(distributor),该示例在本地系统上执行所有工作。使用这个分发器运行示例的命令,例如:
clang -flto=thin -fuse-ld=lld -O2 t1.o t2.o -fthinlto-distributor=$(which python3) \
-Xthinlto-distributor=$LLVMSRC/llvm/utils/dtlto/local.py
分发器(Distributors)¶
分发器是负责以下工作的程序:
- 消费 JSON 后端编译任务描述文件。
- 把任务描述转换为分发系统所需的请求。
- 阻塞(等待)直到所有后端编译完成。
分发器在失败时必须返回非零退出码。它们可以实现为平台原生可执行程序或脚本语言(例如 Python)。
Clang 与 LLD 提供了选项来指定用于管理后端编译的分发器程序。分发器选项与后端编译选项也可以一并指定,这些选项会被透明地转发。
后端编译当前通过调用 Clang 来执行。详见:
- Clang 文档: https://clang.llvm.org/docs/ThinLTO.html
- LLD 文档: https://lld.llvm.org/DTLTO.html
当 LLD 与分发器一起被调用时,LLD 会生成一个描述后端编译任务的 JSON 文件并执行分发器,同时把该文件传递给分发器。
JSON 模式(JSON Schema)¶
下面的示例说明了 JSON 格式(描述对模块 t1.o 与 t2.o 的后端编译任务):
{
"***mon": {
"linker_output": "dtlto.elf",
"args": ["/usr/bin/clang", "-O2", "-c", "-fprofile-sample-use=my.prof"],
"inputs": ["my.prof"]
},
"jobs": [
{
"args": ["t1.o", "-fthinlto-index=t1.o.thinlto.bc", "-o", "t1.native.o", "-fproc-stat-report=t1.stats.txt"],
"inputs": ["t1.o", "t1.o.thinlto.bc"],
"outputs": ["t1.native.o", "t1.stats.txt"]
},
{
"args": ["t2.o", "-fthinlto-index=t2.o.thinlto.bc", "-o", "t2.native.o", "-fproc-stat-report=t2.stats.txt"],
"inputs": ["t2.o", "t2.o.thinlto.bc"],
"outputs": ["t2.native.o", "t2.stats.txt"]
}
]
}
jobs 数组中的每一项都表示一个后端编译任务。每个任务对象记录自己的命令行参数与输入/输出文件。共享的参数与输入定义在 ***mon 对象中一次即可。
保留条目(Reserved Entries):
-
***mon.args数组中的第一个条目指定要调用的编译器可执行文件。 - 每个任务
inputs数组的第一个条目为正在被编译的模块的 bitcode 文件。 - 每个任务
inputs数组的第二个条目为对应的单独 summary 索引文件。 - 每个任务
outputs数组的第一个条目为主输出的目标文件。
对于 outputs 数组,只有第一个条目为保留项(主输出文件);其余条目的顺序不保证。有些分发系统依赖主输出路径(例如用它作为编译任务的用户可读标签),因此将主输出放在保留位置。当前 DTLTO 实现不会产生多个输出文件,但未来若加入会产出额外输出文件的 LTO 选项,新的输出也将被包含到该数组中。
将命令行参数与输入/输出文件分别存储,允许在不修改分发器的情况下更换远程编译器,因为分发器不需要理解编译器命令行的细节。
要生成后端编译命令,会把公共参数与任务特定参数连接起来。
基于上面示例 JSON,分发器应以最大并行度执行下列后端编译命令:
/usr/bin/clang -O2 -c -fprofile-sample-use=my.prof t1.o -fthinlto-index=t1.o.thinlto.bc -o t1.native.o \
-fproc-stat-report=t1.stats.txt
/usr/bin/clang -O2 -c -fprofile-sample-use=my.prof t2.o -fthinlto-index=t2.o.thinlto.bc -o t2.native.o \
-fproc-stat-report=t2.stats.txt
待办事项(TODOs)¶
下列功能已计划加入 DTLTO,但尚未实现:
- 支持 ThinLTO 的进程内缓存(in-process cache)。
- 支持 ELF/COFF 之外的平台。
- 支持包含 bitcode 成员的归档(archives)。
- 支持更多的 LTO 配置;当前仅支持非常有限的一组 LTO 配置,例如目前尚不支持基本块段(basic block sections)。
约束(Constraints)¶
- 应使用版本匹配的 Clang 与 LLD(相互兼容的版本)。
- 使用的分发器必须支持与正在使用的 LLD 版本相对应的 JSON 模式。
原文地址:https://llvm.org/docs/DTLTO.html