编译优化之 - 链接时优化(LTO)入门

1. 关于 LTO 、-flto 、ThinLTO
  • LTO(Link Time Optimization)链接时优化是链接期间的程序优化,多个中间文件通过链接器合并在一起,并将它们组合为一个程序,缩减代码体积,因此链接时优化是对整个程序的分析和跨模块的优化。IPO(IPA)的说明介绍可参考:编译优化之 - 过程间优化(IPA/IPO)入门
    link time时需要为GP alias计算大小,是否超过16bit,以决定用什么东西。该计算在linker中做而不是compiler来做。
  • flto是使用lto的主要方法,是一个优化选项,禁用lto使用-fno-lto。flto主要做的操作有inline、ipa和alias分析等。
  • ThinLTO是一种可扩展和增量式的新型LTO,与LTO相比,表现甚至更好。要使用ThinLTO,只需添加-flto=thin选项即可进行编译和链接。第一阶段类似于传统LTO的步骤,在进行一些早期优化(主要是为了减小大小)之后,调用前端将每个输入源文件转换为包含IR的中间文件。只是使用ThinLTO,每个文件中都包含一个附加的摘要部分。ThinLTO overview如下:

ThinLTO overview

2. LLVM或AOCC中flto

  LLVM中lto work在IR(Intermediate representation)上,我们常用的选项-flto其实代表-flto=full,指lto将分散的目标文件的所有LLVM IR组合到一个大的LLVM模块中,然后对其进行整体分析优化并生成machinecode,该选项仅并行执行前端的语义分析,优化和machinecode生成在单线程完成。-flto=thin则是把模块分开,根据需要才从其他模块导入功能,并且除全局分析外均采用并行的方式进行优化和machinecode的生成。因此使用-flto=thin-flto=full编译链接的速度大大加快,而且对于SPEC CPU2017部分benchmark性能更好。

2.1 Linkers

  ThinLTO当前在LLVM编译器中实现,其思想是主要是指导inline优化。它支持以下三种链接器:

  1. gold linker:使用前需要先安装插件,然后就可以使用-fuse-ld=gold。安装请见(The LLVM gold plugin)。
  2. ld64:从Xcode 8开始。
  3. lld:来自于llvm项目,运行速度更快,特别是在众核处理器上。默认支持LTO,lld读取llvm IR bitcode进行编译优化并输出文件。详细请见(The LLVM Linker — lld)。LLVM中关于lld的详细说明介绍请见知乎大佬的:LLVM中的lld程序流程分析 这篇文章。

基本用法:

// 使用-flto=thin编译链接
clang -flto=thin -O2 file1.c file2.c -c
clang -flto=thin -O2 file1.o file2.o -o a.out

// 使用lld-link时,只需将-flto选项添加到编译步骤中
clang-cl -flto=thin -O2 -c file1.c file2.c
lld-link /out:a.exe file1.obj file2.obj
  • 备注:在AOCC2.0或更高版本中已不再使用gold linker plugins作为链接器,而是使用lld作为default linker。在LLVM9.0或更高版本中使用-fuse-ld=lld指定链接器。
3. GCC和ICC中flto
3.1 GCC中用法

  GCC-4.6.0中开始支持LTO框架,主要分为:Partitioned LTO和Non-Partitioned LTO
分区的LTO(Partitioned LTO)整体步骤为:

  1. LGEN:生成摘要信息;生成转换单元信息
  2. WPA(Whole Program Analysis):读取除函数体外的call graph信息;每个函数的摘要信息
  3. LTRANS:局部转换

处理顺序为:

  • gcc executes LGEN
  • Subsequent process of lto1 executes WPA
  • Subsequent independent processes of lto1 execute LTRANS

不分区的LTO(Non-Partitioned LTO)整体步骤为:

  1. LGEN:生成转换单元信息
  2. IPA(InterProcedural Analysis) :读取call graph和函数体内容;进行分析和转换

处理顺序为:

  • gcc executes LGEN
  • Subsequent process of lto1 executes IPA

以上内容摘自于:https://www.cse.iitb.ac.in/grc/slides/pldi14tut-gcc/5-gcc-pldi14-lto-mechanism.pdf,是以GCC-4.7.2版本源码进行的分析说明。

  GCC中使用-flto编译代码时,它将生成GIMPLE中间表示,并将其写到目标文件的ELF(部分的数据结构和枚举代码在 lto-streamer.h中),将目标文件链接在一起时,将从这些ELF中读取所有功能体,并将其实例化(链接期间,所有对象模块放在一起并调用lto1)。当前,大多数基于ELF的系统以及darwincygwinmingw系统都启用了LTO支持。
  从源码时使用-flto编译,所有的passesall_lto_gen_passes中管理,它包含两个IPA passes:

  • pass_ipa_lto_gimple_out:该pass执行函数体lto_output在lto-streamer-out.c文件中。它遍历调用图,对每个可达的声明,类型和函数进行编码。
  • pass_ipa_lto_finish_out:该pass执行函数体produce_asm_for_decls在lto-streamer-out.c文件中。它获取上一步中的结果并将其编码在相应的ELF文件节中。

GCC中基本用法:

gcc -c -o test1.o -O2 -flto test1.c
gcc -O2 -flto test1.o test2.o ... -o test

// LTO的另一个功能是可以对用不同语言编写的文件进行过程间优化
gcc -c -flto foo.c
g++ -c -flto bar.cc
gfortran -c -flto baz.f90
g++ -o myprog -flto -O2 foo.o bar.o baz.o -lgfortran

  GCC中如果你想知道你的代码从前端开始的编译过程中做了哪些pass,可使用fdump-tree-all-fdump-ipa-all或者-fdump-rtl-all打印出来,如下:

gcc -flto -O3 .....  -fdump-tree-all test.c    // 可使用任意选项,此处打印flto 和O3

phase
  由上图可看出,源码经过前端处理之后在中端需要经过很多处理,你可以结合gdb看到每一个处理过程的转换信息。
对于最简单的 printf(“hello world”),在GCC中没有-flto选项的情况下得到的汇编码如下:
assemble
使用-flto之后的大致输出信息如下:
flto
由此看出:LTO输出不包含目标代码,而仅包含LTO信息。
关于flto优化的具体信息可通过--verbose打印出来:
verbose info
在以上标注的6个步骤中,-flto真正开始起作用是从第4步开始,lto1位置。

3.2 ICC中用法

  ICC中提供了使用-ffat-lto-objects选项的链接时优化,以实现GCC兼容性。ICC中的链接时优化是在IPO中选取启用的。
ICC中基本用法:

ifort -c -o test1.o -O2 -ipo test1.f90
ifort -O2 -ipo test1.o test2.o ... -o test
4. 注意点
  • LTO生成的.o文件并不是真正的Object file,而是一个携带优化信息的中间文件,这些.o文件通过Linker整理分析之后合并成最终可执行文件。
  • 使用LTO会导致编译链接速度变慢,占用更大的内存空间。
  • 一些选项不支持和LTO一起使用,例如:-flive-patching等。甚至配合某些选项使用时一些benchmark编不过。

References:
  • 15
    点赞
  • 43
    收藏
    觉得还不错? 一键收藏
  • 4
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值