编译优化之 - 过程间优化(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,比如-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也可能导致register分配不好,stack size过大而带来性能的下降。使用flto的ipa 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:

原创文章 38 获赞 13 访问量 4036

猜你喜欢

转载自blog.csdn.net/qq_36287943/article/details/103930336