编译优化之-链接时优化(LTO)⼊门
1. 关于 LTO 、-flto 、ThinLTO
LTO(Link Time Optimization)链接时优化是链接期间的程序优化,多个中间⽂件通过链接器合并在⼀起,并将它们组合为⼀个程序,缩减代码体积,因此链接时优化是对整个程序的分析和跨模块的优化。IPO(IPA)的说明介绍可参考:
link time时需要为GP alias计算⼤⼩,是否超过16bit,以决定⽤什么东西。该计算在linker中做⽽不是compiler来做。xcode入门
flto是使⽤lto的主要⽅法,是⼀个优化选项,禁⽤lto使⽤-fno-lto。flto主要做的操作有inline、ipa和alias分析等。
ThinLTO是⼀种可扩展和增量式的新型LTO,与LTO相⽐,表现甚⾄更好。要使⽤ThinLTO,只需添加-flto=thin选项即可进⾏编译和链接。第⼀阶段类似于传统LTO的步骤,在进⾏⼀些早期优化(主要是为了减⼩⼤⼩)之后,调⽤前端将每个输⼊源⽂件转换为包含IR 的中间⽂件。只是使⽤ThinLTO,每个⽂件中都包含⼀个附加的摘要部分。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。安装请见()。
2. ld64:从Xcode 8开始。
3. lld:来⾃于llvm项⽬,运⾏速度更快,特别是在众核处理器上。默认⽀持LTO,lld读取llvm IR bitcode进⾏编译优化并输出⽂件。详
细请见()。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 / 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
  GCC中使⽤-flto编译代码时,它将⽣成GIMPLE中间表⽰,并将其写到⽬标⽂件的ELF(部分的数据结构和枚举代码在 lto-streamer.h 中),将⽬标⽂件链接在⼀起时,将从这些ELF中读取所有功能体,并将其实例化(链接期间,所有对象模块放在⼀起并调⽤lto1)。当前,⼤多数基于ELF的系统以及darwin,cygwin和mingw系统都启⽤了LTO⽀持。
  从源码时使⽤-flto编译,所有的passes在all_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 -
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
  由上图可看出,源码经过前端处理之后在中端需要经过很多处理,你可以结合gdb看到每⼀个处理过程的转换信息。对于最简单的 printf(“hello world”),在GCC中没有-flto选项的情况下得到的汇编码如下:
使⽤-flto之后的⼤致输出信息如下:
由此看出:LTO输出不包含⽬标代码,⽽仅包含LTO信息。
关于flto优化的具体信息可通过--verbose打印出来:
在以上标注的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:

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。