编译器优化的智能程度
对于下面的一段循环:
for (x = 0; x < len; x++)
DestArray[0] = SourceArray[0] + K; (K为常量)
采用最大优化选项后,程序的运行时间与len的大小成正比线性关系。
测试平台:Vs2005 and Mingw C.
就肉眼看,我们可以看到无论len的大小是多少,实际上就是重复
DestArray[0] = SourceArray[0] + K;这一句,源数据和目的数据对象的地址都是不变的。那么可以优化为只执行一次DestArray[0] = SourceArray[0] + K;,因为执行多次和一次的结果是没有区别的。
但是,编译器仿佛没有实现以上所述的优化。是因为有什么“优化阻碍规则”呢,还是先有的编译器优化的智能程度不够呢?请教高人
[解决办法]
这里要看
DestArray和SourceArray
是如何定义的。
如果这两个是指针(从其他地方传送过来),编译器是无法判断它们之间是否有歧义的(比如是否两个指针指像同一个内存位置),
比如这个代码中,如果DestArray和SourceArray指向同一个位置,那么这个代码相当于
DestArray[0]+=K*len;
但是如果不相同而且不重叠,那么代码相当于DestArray[0]=SourceArray[0]+K.
但是如果这两个都是局部数组,那么大部分编译器将可以优化掉。
当然也不是说这样的代码编译器就无法优化掉,只是一般编译器不会针对如此特殊的代码去优化,毕竟不会有人真的去写这样的代码。
如果确认源代码中DestArray和SourceArray必然 不同,可以试着启用编译器中的InterProcedure Analaysis看看,比如Intel的编译器可以添加-ipo选项来实现。
[解决办法]
你只需把printf( "%x\n ", dst[rand()]);改成printf( "%x\n ", dst[0]);
vc8 release就彻底把程序优化掉了。
[解决办法]
> > > 另外,我想指出的一点是,如果上面的程序注释掉printf( "%x\n ", dst[rand()]);这一行,
程序总共就只运行了9个clock ticks(VS下).
这个问题很容易解释,编译器里面有一个优化叫做Dead Code Elimination,
如果没有任何对dst数组的输出,那么所有对dst数组的赋值都属于dead code,所以可以完全删除。
> > > 从loops的结果:你只需把printf( "%x\n ", dst[rand()]);改成printf( "%x\n ", dst[0]);
vc8 release就彻底把程序优化掉了。
来看,vc8使用了scalarization, 也就是当它发现所有对dst数组的访问都是某几个有限已知位置的单元时,它可以将它们(dst[0])优化成一个普通变量,在这种情况下,dst[0]变成一个普通变量了(数组被优化了),然后代码就很容易被优化了。
而在使用dst[rand()]时代码无法优化,说明vc8编译器中alias analysis是上下文无关的。
因为编译器发现循环中的dst[0]和后面的dst[rand()]有alias,从而导致整个循环无法优化(而不管dst[rand()]的访问在什么地方。
不过现在大部分编译器中alias analysis都是上下文无关的,这是因为效率的原因。
[解决办法]
不是,负责消除没用的代码的是DCE,如果printf在for之前,for里面内容必然是dead code,会被删除。
我的意思是对for循环本身代码的优化过程需要知道关于数组dst的alias信息(比如是否能够做scalarization等)。