ThinLTO:可扩展及增量的LTO

原文地址:http://blog.llvm.org/2016/06/thinlto-scalable-and-incremental-lto.html

ThinLTO首先是在2015年EuroLLVM上提出的(LTOlink time optimization),由clangLLVM里的一个原型实现给出结果。自此,几个RFC审查了这个设计,它已经在LLVM里实现(对goldlibLTO),调整还在进行中(译者注:这篇文章发表在20166月)。对若干benmark已经显示出非常好的结果,编译时间接近非LTO编译。

本文涵盖了背景、设计、当前状态与有用信息。

本文作者为Teresa JohnsonMehdi AminiDavid Li

LTO背景与动机

LTO(链接时优化)是通过全程序分析以及跨模块优化,实现更好的运行时性能的方法。在编译阶段,clang发布LLVM字节码,而不是目标文件。链接器认识这些字节码文件,并在链接生成构成可执行文件的最终目标文件期间调用LLVMLLVM实现载入所有的输入字节码文件,把它们合并为一个模块。在这个整体模块上,串行执行过程间分析(interprocedural analysisIPA)以及过程间优化(interprocedural optimizationIPO)。

实践中这意味着LTO经常要求大量的内存(一次放下所有的IR)并且非常慢。而使用-g激活调试信息,IR的大小与由此而来的内存要求显著增加。即使没有调试信息,对非常大的应用程序,或者在内存受限的机器上编译时,这是不可接受的。它还使得增量编译效率降低,因为在源代码有任何改动时,从LTO步骤起的一切都必须重来。

ThinLTO的设计

ThinLTO是旨在像一个非LTO编译那样可扩展,同时保留完整LTO实现的大多数性能优化的新方法。

ThinLTO中,串行步骤非常短且块。这是因为不是载入字节码并合并为一个整体模块来执行这些分析,对串行链接步骤里的全局分析,它使用紧凑的每模块汇总,以及用于稍后跨模块导入的函数位置索引。随后在完全并行的后端里优化模块时,执行函数导入与其他IPO转换。

ThinLTO全局分析激活的关键转换是函数导入,其中仅可能被内联的函数会导入到每个模块。这使得每个ThinLTO后端里的内存开销最小,同时使最有力的跨模块优化机会最大化。因此IPO转换在每个扩展了导入函数的模块上执行。

ThinLTO过程分为3个阶段:

  1. 编译:像完整LTO模式那样生成IR,不过以模块汇总扩展
  2. Thin链接:Thin链接器插件层来合并汇总信息以及执行全局分析
  3. ThinLTO后端:使用基于汇总信息的导入与优化的并行后端

默认的,支持ThinLTO的链接器(参见下面)被设置为在线程里启动ThinLTO后端。因此第二与第三阶段的区别对用户是透明的。

这个过程的关键促成者是第1阶段发布的汇总信息。这些汇总信息使用字节码格式发布,但设计为可以单独载入,无需调用一个LLVMContext或其他高代价构造。每个在模块汇总中,每个全局变量与函数都有一个项。一个项包含抽象了它所描述符号的元数据。例如,一个函数使用其链接类型、包含的指令数以及可选的概要信息(optional profiling informationPGO)来抽象。另外,记录了对另外全局实体的每个引用(获取的地址、直接调用)。在Thin链接阶段,这个信息激活了完整引用图的构建,后续的快速分析使用这个全局汇总信息。

当前状态

当前在在gold plugin以及从Xcode 8开始的ld64中支持ThinLTO。另外,当前也正在lld链接器里添加支持。Clang 3.9发布将使用-flto=thin命令行选项访问ThinLTO

虽然调整仍然在进行,ThinLTO已经与LTO工作得一样好,在许多情形里有可比较的性能提升。在少数情形里ThinLTO甚至超出了完全LTO,很有可能是因为ThinLTO更高的可扩展性使得使用更激进的后端优化流水线成为可能(类似于非LTO编译)。

下面的结果收集自82.6GHz Intel Xeon E5-2689上对C/C++ SPEC cpu2006基准测试程序。每个基准测试集都独立运行三次,结果显式三次运行的平均值。

至关重要的是,由于ThinLTO可扩展的设计,这个性能是在编译时间保持在非LTO编译范围里实现的。下面的编译时间是在一台运行Linux、使用gold链接器的202.8GHz Intel Xeon CPU E5-2680 v2上收集的。这些结果是从一个干净的构建目录从头到尾构建clangninja clang),因此它包括所有的编译步骤,以及中间二进制文件的链接,比如llvm-tblgenclang-tblgen

发布编译显示了ThinLTO的编译时间如何接近一个非LTO编译。添加-gline-tables-only增加了非常小的开销,ThinLTO再一次类似于常规的非LTO编译。不过使用完整的调试信息,因为导入期间额外的开销,ThinLTO仍然比非LTO编译要慢一些。进行中的对调试元数据表示与处理的改进预期会持续减少这个开销。在所有情形里,完全的LTO实际上显著慢得多。

在内存使用方面,改进是显著的。在过去的两年里,FullLTO大大改善,如下图所示,不过我们的测量显示ThinLTO仍然保持了大的优势。

使用信息

要使用ThinLTO,对编译及链接只是添加-flto=thin。即

    % clang -flto=thin -O2 file1.c file2.c -c

    % clang -flto=thin -O2 file1.o file2.o -o a.out

如前所示,缺省的链接器将并行启动ThinLTO后端,对最终的原生链接,将得到的原生目标文件传递回给链接器。同样,使用模式与非LTO相同。类似于普通的LTO,对Linux这要求使用配置了启用插件的gold链接器或者从Xcode 8开始的ld64

分布式编译的支持

为了利用分布式编译系统,并行的ThinLTO后端每个都可以作为独立进程启动。为了支持这,gold plugin提供了一个thinlto_index_only选项,使得链接器在创建了复合索引及执行全局分析后退出。

另外,在这个模式里:

  • 不是使用一个整体式的复合索引,每个后端有一个独立的索引文件,它包含用于记录导入的复合索引的必要部分,以及基于该后端决定要施行的优化的其他全局汇总信息。
  • 可选地发布一个列出从中导入每个模块的字节码文件的纯文本,以辅助分布式编译文件分阶段进行(thinlto-emit-imports-files插件选项)

通过在字节码上调用clang,并通过选项提供索引,可以启动后端。最后,链接得到的原生目标文件生成最终的二进制文件。例如:

    % clang -flto=thin -O2 file1.c file2.c -c
    % clang -flto=thin -O2 file1.o file2.o -Wl,-plugin-opt,-thinlto-index-only
    % clang -O2 -o file1.native.o -x ir file1.o -c -fthinlto-index=./file1.o.thinlto.bc
    % clang -O2 -o file2.native.o -x ir file2.o -c -fthinlto-index=./file2.o.thinlto.bc
    % clang file1.native.o file2.native.o -o a.out

增量的ThinLTO支持

使用完全LTO,仅初始编译步骤可以增量执行。如果任何输入发生变化,高代价的IPA/IPO步骤必须重做。

使用ThinLTO,如果任何输入发生变化,串行Thin Link步骤必须重做,不过如之前提到的,这是小且快的,且不涉及载入任何模块。任何特定的ThinLTO后端必须重做,当且仅当:

  1. 对应(主)模块的字节码发生变化
  2. 模块的导入、导出列表发生变化
  3. 任何模块的导入代码发生变化
  4. 影响主模块或其导入对象的任何全局分析结果发生变化

对单个机器编译,这时线程由链接器启动,在应用基于全局汇总信息优化后,比如导入,使用上述信息的哈希值作为键缓存模块,可以实现增量编译。这个缓存已经在libLTOThinLTO处理中支持,为d64使用。要启用它,需要向链接步骤传递一个额外的标记:-Wl,-cache_path_lto, /path/to/cache

对分布式编译,上述2-4项信息都被序列化到单独的索引文件。因此编译系统可以将输入的字节码文件的内容(主模块的字节码与其导入的)连同复合索引,与先前编译生成的那些比较,确定是否需要重做特定的ThinLTO后端。为了使这个过程更高效,在编译阶段发布时,计算字节码文件内容的哈希值,结果保存在字节码文件本身,因此在Thin Link阶段,无需读取IR就能查询缓存。

下图展示了在三个不同情形下,clang完整的编译时间:

  1. 干净编译后完全链接
  2. 开发者修改了Dense Map::grow()的实现。这是项目里广泛使用的头文件,它导致大量文件被重新编译。
  3. 开发者修改了InstCombineCalls.cpp里的visitCallInst()的实现。这是一个实现文件,增量编译应该是快速的。

 

这些结果展示了完全LTO对增量编译是如何的不友好,而ThinLTO提供了一个非常接近非LTO编译的增量链接时间。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值