设为首页 加入收藏

TOP

5.8 编译器对循环结构的优化(1)
2013-10-07 14:29:51 来源: 作者: 【 】 浏览:61
Tags:5.8 编译器 循环 结构 优化

5.8 编译器对循环结构的优化(1)

5.7节介绍了3种循环结构,在执行效率上,do循环结构是最高的。由于do循环在结构上非常精简,利用了程序执行时由低地址到高地址的特点,只使用一个条件跳转指令就完成了循环,因此已经无需在结构上进行优化处理。

由于循环结构中也有分支功能,所以4.4.2节介绍的分支优化同样适用于循环结构。分支优化会使用目标分支缓冲器,预读指令。由于do循环是先执行后比较,因此执行代码都在比较之前,如下所示。

  1. int i = 0;  
  2. 00401248   mov         dword ptr [ebp-4],0  
  3. do  
  4. {  
  5.  i++;  
  6. 0040124F   mov         eax,dword ptr [ebp-4]  
  7. 00401252   add         eax,1  
  8. 00401255   mov         dword ptr [ebp-4],eax  
  9. printf("%d", i);  
  10.     ; printf讲解略  
  11. } while(i < 1000);  
  12. ; 此处的汇编代码在退出循环时才预测失败  
  13. 00401269   cmp      dword ptr [ebp-4],3E8h  
  14. 00401270   jl          main+1Fh (0040124f) 

do循环结构中只使用了一次跳转就完成了循环功能,大大提升了程序的执行效率。因此,在三种循环结构中,它的执行效率最高。

while循环结构的效率要比do循环结构低。while循环结构先比较再循环,因此无法利用程序执行顺序来完成循环。同时,while循环结构使用了2个跳转指令,在程序流程上就弱于do循环结构。为了提升while循环结构的效率,可以将其转成效率较高的do循环结构。在不能直接转换成do循环结构的情况下,可以使用if单分支结构,将do循环结构嵌套在if语句块内,由if语句判定是否能执行循环体。因此,所有的while循环都可以转换成do循环结构,如图5-13所示。

 
图5-13 while循环结构的优化图

图5-13为代码清单5-23使用O2选项后编译的Release版结构流程图,该图截取自IDA。图5-13划分了程序的流程,箭头方向显示,反汇编代码中有一个单分支结构与循环结构。首先由条件跳转指令jl比较参数,小于等于0则跳转。可见这是一个if语句。

如果jl跳转失败,则顺序向下执行,进入标号loc_40100C处。这是一个循环语句块。此语句块内使用条件跳转指令jle,当ecx小于等于edx时,跳转到地址标号loc_40100C处。edx中保存参数数据,ecx每次加1,使eax每次对ecx累加。先执行,后判断,有了这个特性便可将图5-13所对应的代码还原成由单分支结构中嵌套do循环结构的高级代码。转换成对应的C++(www.cppentry.com)代码如下:

  1. int LoopWhile(int nCount){  
  2.     int nSum = 0;  
  3.     int nIndex = 0;  
  4.     if(nCount >= 0){  
  5.       do{  
  6.       nSum += nIndex;  
  7.         nIndex++;  
  8.       }while(nIndex <= nCount)  
  9.     }  
  10.     return nSum;  

经过转换后,代码的功能没有任何改变,只是在结构上有了调整,变成了单分支结构加do循环结构。

以上讨论了while循环结构的优化,可以将其转换为do循环结构来提升效率。

从结构特征上可知,for循环是执行速度最慢的,它需要三个跳转指令才能够完成循环,因此也需要对其进行优化。for循环可以这么转换吗?从循环结构上看,其结构特征和while循环结构类似。由于赋初值部分不属于循环体,可以忽略。只要将比较部分放到循环体内,即是一个while循环结构。既然可以转换while循环结构,那么自然可以转换为do循环结构进行优化以提升效率。

有了for循环结构的优化方案,那么在对其优化过程中,VC++(www.cppentry.com) 6.0能否按照此方案进行优化呢?将代码清单5-24使用O2选项进行重新编译,优化后的for循环反汇编代码如代码清单5-25所示。

代码清单5-25 for循环结构—Release版

  1. .text:00401000 sub_401000      proc near  
  2. ; 函数参数标号定义arg_0  
  3. .text:00401000 arg_0           = dword ptr  4  
  4. ; 使用edx保存参数arg_0  
  5. .text:00401000                 mov     edx, [esp+arg_0]  
  6. ; 清空eax,ecx  
  7. .text:00401004                 xor     eax, eax  
  8. .text:00401006                 xor     ecx, ecx  
  9. .text:00401008                 test    edx, edx  
  10. ; 检查edx,小于0则跳转到标号short locret_401013处,该函数结束  
  11. .text:0040100A                 jl      short locret_401013  
  12. ; 说明此处标号在地址sub_401000+0x11处被调用  
  13. .text:0040100C loc_40100C:  
  14. ; 执行eax加等于ecx操作  
  15. .text:0040100C                 add     eax, ecx  
  16. ; 执行ecx自加1操作  
  17. .text:0040100E                 inc     ecx  
  18. .text:0040100F                 cmp     ecx, edx  
  19. ; 比较ecx与edx,小于等于则跳转到标号short loc_40100C处,这是一个向上跳  
  20. .text:00401011                 jle     short loc_40100C  
  21. ; 函数返回地址标号locret_401013处,被地址标号sub_401000+0x0A处调用  
  22. .text:00401013 locret_401013:  
  23. .text:00401013                 retn 

】【打印繁体】【投稿】【收藏】 【推荐】【举报】【评论】 【关闭】 【返回顶部
分享到: 
上一篇5.8 编译器对循环结构的优化(2) 下一篇5.7 do/while/for的比较(3)

评论

帐  号: 密码: (新用户注册)
验 证 码:
表  情:
内  容: