编译器优化的能力和局限性

记得3年前涛哥来面试的时候,就被提问到关于O2,O0编译的问题,我当时听起来就是天书。gcc给用户提供了一些使用优化的控制,制定优化级别,-O1是使用一组基本的优化,-O2或者-O3会使用更全面的优化,这样做可以提高性能,但是也会使得标准的调试工具更难对程序进行调试。平时一般程序运行都是-O2,debug的时候Makefile里修改为-O0然后进行

 

下面部分摘自《深入理解计算机系统》

编译器必须很小心地对程序只是用安全的优化,也就是说对于程序可能遇到的所有可能的情况,在C语言标准提供的保证之下,优化后得到的程序和未优化的版本有一样的行为。限制编译器只进行安全的优化,消除了一些造成不希望的运行时行为的可能原因,但是这也意味着程序猿必须花费更大的力气写出程序使编译器能够将之转换成有效机器代码。为了理解决定一种程序转换是否安全的难度,来看看下面这两个过程:(ps:公司的面试题也有这道^_^)

1 void twiddle1(int *xp,int *yp)
2 {
3     *xp += *yp;
4     *xp += *yp;
5 }
6
7 void twiddle2(int *xp,int *yp)
8 {
9     *xp += 2* *yp;
10 }

这两个过程似乎有相同的行为,都是将存储在由指针yp指示的位置处的值两次加到指针xp指示的位置处的值。另外,函数twiddle2效率更高一些,值要求3次存储器引用(读*xp,读*yp,写*xp),而twiddle1需要6次(2次读*xp,2次读*yp,2次写*xp)。隐刺,如果需要编译器编译过程twiddle1,我们会认为基于twiddle2执行的计算能产生更有效的代码

 

但是,考虑xp等于yp的情况,此时,函数twiddle1会执行

*xp += *xp;

*xp += *xp;

结果是xp的值会变成4倍

 

函数twiddle2会执行

*xp += 2* *xp;

结果是xp的值变成3倍

 

编译器不知道twiddle1会如何被调用,隐刺必须假设参数xp和yp可能会相等,因此它不能产生twiddle2风格的代码作为twiddle1的优化版本

这种两个指针可能指向同一个存储器位置的情况称为memory aliasing。只在执行安全的优化中,编译器必须假设不同的指针可能会指向存储器中同一个位置。

如果编译器不能确定两个指针是否指向同一个位置,就必须假设什么情况都有可能,就限制了可能的优化策略。

发表回复