编译优化之 - 过程间优化(IPA/IPO)入门

  过程间分析(inter-procedural analysis)是一个多步骤的过程,是LTO分析过程中的重要部分,也是一个跨模块的分析过程。跨模块的优化功能实现最早在1987年(Link time optimization - MIPS),后来相继出现了过程间分析和转换,动态链接程序的优化(IPA + LTO)。
  过程间分析包含local分析和global分析。局部分析会为每一个过程和调用点生成Local Summary;全局分析时会根据具体要解决的数据流问题在Local Summary中去查找。但是使用IPA 分析无可避免的问题就是running out of memory!

IPA in GCC

GCC在2006年实现单文件的LTO。GCC根据LTO/WHOPR处理模型,把IPA过程划分为small IPA passes、regular IPA passes、late IPA passes。 small IPA passes派生于simple_ipa_opt_pass,它一次执行所有操作,并且仅定义执行阶段,在此阶段,它访问并修改功能主体。 regular IPA passes派生于ipa_opt_pass_d,它可能在LGEN,WPA或LTRANS阶段的任何阶段都实现了summary hooks。 late IPA passes是执行于regular IPA passes之后的简单passes,在WHOPR模式下只能看到编译单元的一部分。

  GCC中的IPA包含的操作有: increase alignment、devirtualization、constant propagation、inline、pure/const analysis等。可使用-fdump-ipa-all选项打印出ipa阶段的信息,如下:

gcc -flto -O3 ..... -fdump-ipa-all t.c //可使用任意优化选项,此处使用flto和O3

phase
由上可看出IPA阶段的处理操作,你可以结合gdb看到每一个处理过程的转换信息。


IPO in ICC

  ICC中的IPO(Interprocedural Optimization)包含众多的优化,例如:Array dimension padding、Alias analysis、Constant propagation、Dead call deletion、Dead function elimination、Inlining、Structure splitting and field reordering、Whole program analysis等重要优化过程。优化选项配置为-ipo。

IPO支持单文件编译和多文件编译两种编译模型,单文件编译使用[Q] ip编译选项,并为每个要编译的源文件生成一个真实的目标文件。在单文件编译期间,编译器对调用当前源文件中定义的过程执行内联函数扩展。编译器在O2默认优化级别上执行一些单文件过程间优化。另外,编译器可能会为O1优化级别执行一些内联。多文件编译使用[Q] ipo选项,并生成一个或多个模拟目标文件,而不是普通目标文件。此外,编译器还会从构成程序的各个源文件中收集信息。使用此信息,编译器可以跨不同源文件中的函数和过程执行优化。


1. Inlining操作

  inline即函数内联,是把被调函数里面的代码直接放到调用区域的操作。程序的函数代码段被放在代码区,局部变量与函数参数被放在栈区,而函数的调用过程也是在栈区进行的,所以inline能减少函数调用开销(inline为优化创造了新的机会,因为inline使它们对上下文敏感。通过上下文敏感,优化器可能消除不同调用站点的歧义。)。

  inline操作除了用户程序中使用关键字inline指定的代码会被inline之外,还有一些优化选项也会导致inline,比如-flto、-ipo选项。(虽然 IPA中的inline是假inline)其中early inline发生在单个文件中,比如函数调用等,是比较初级的inline操作,而IPA inline则发生在链接时,通常包含inline分析和转换等多个步骤,属于链接时优化。gcc中由-flto选项开启,icc中由-ipo选项开启。
示例如下:

(inline) int add(int a, int b)
{
        return a+b;
}
int main(){
        printf("hello world");
        add(11,11);
        return 0;
}

使用inline和不使用inline的汇编码如下:
inline noline

inline优点:

  inline操作可减少函数调用的入栈出栈开销,并且使得某些优化能够进行;使用flto的ipa inline配合一些全局优化能对程序性能有较大提升;inline函数与其它函数一样,可以使用所在类的保护成员及私有成员。

inline缺点:

  inline也可能导致register分配不好,stack size过大而带来性能的下降;如果函数体内有较大的热循环,则开销大可能会变得更大;是否进行inline由编译器决定;函数代码过多,函数复制会消耗较大内存;一些编译器不支持递归函数的inline。

  更多关于inline的深入分析在知乎大佬的文章:被知乎大佬嘲讽后的一个月,我重新研究了一下内联函数 中查看。

2. Constant propagation操作

  常量传播也称为常量折叠,是把那些每次运行时总得到相同常量的表达式替换为该常量。它的传播具有单调性和不可分配性,示例如下:

int test(int x, int y)
{
    if (x > y)
        return x + y;
    return x * y;
}
int main()
{
   test(x, 5) + test(x, 2);
   return 0;
}
// after optimized 
int test(int x, int y)
{
  return x + y;
}
int main()
{
   test(x, 5) + test(x, 2);
   return 0;
}
3. Global variables

  对于全局变量的优化,删除从未读取的全局变量,或者把函数中使用的全局变量更改为该函数中的局部变量。编译器也会跟踪哪些函数会修改变量以便提高变量的使用率,示例如下:

public static int a = 11;
void fun(){ ... }
int f()
{
  a += 11;
  fun();
  a += 11;
}
// after optimized 
int f()
{
  fun();
  a += 22;
}

GCC中对Global variables的优化已默认开启,有-fipa-reference选项控制。


References:
  • 3
    点赞
  • 16
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值