Gcc 優化選項 與尾遞歸優化

今天作高性能計算機系統的做業的時候,發現gcc中的優化選項有不少應用 。html

例如對於C源碼:算法

#include <stdio.h>
#include <stdlib.h>

int main()
{
    int x[101],y[101];
    int a,i;
    a = 5;
    for(i=0;i<=100;i++)
    {
        x[i] = i+1;
        y[i] = i;
    }

    for(i=100; i>=0; i--)
        y[i] += a*x[i];
    return 0;
}

一、直接用gcc main.c –S –O0進行編譯,即禁止編譯器進行優化,生成的彙編語言文件爲:編程

        

 .file  "main.c"

         .def  ___main; .scl   2;      .type         32;   .endef

         .text

.globl _main

         .def  _main;      .scl   2;      .type         32;   .endef

_main:

         pushl         %ebp

         movl %esp, %ebp

         andl $-16, %esp

         subl  $816, %esp

         call   ___main

         movl $5, 808(%esp)

         movl $0, 812(%esp)

         jmp  L2

L3:

         movl 812(%esp), %eax

         movl 812(%esp), %edx

         incl   %edx

         movl %edx, 404(%esp,%eax,4)

         movl 812(%esp), %eax

         movl 812(%esp), %edx

         movl %edx, (%esp,%eax,4)

         incl   812(%esp)

L2:

         cmpl $100, 812(%esp)

         jle     L3

         movl $100, 812(%esp)

         jmp  L4

L5:

         movl 812(%esp), %eax

         movl 812(%esp), %edx

         movl (%esp,%edx,4), %ecx

         movl 812(%esp), %edx

         movl 404(%esp,%edx,4), %edx

         imull 808(%esp), %edx

         leal   (%ecx,%edx), %edx

         movl %edx, (%esp,%eax,4)

         decl  812(%esp)

L4:

         cmpl $0, 812(%esp)

         jns    L5

         movl $0, %eax

         leave

         ret

 

二、當用gcc main.c –S –O1進行編譯的時候,嘗試優化編譯時間和可執行文件大小等,彙編程序以下:編程語言

   

 1      .file  "main.c"
 2 
 3          .def  ___main; .scl   2;      .type         32;   .endef
 4 
 5          .text
 6 
 7 .globl _main
 8 
 9          .def  _main;      .scl   2;      .type         32;   .endef
10 
11 _main:
12 
13          pushl         %ebp
14 
15          movl %esp, %ebp
16 
17          andl $-16, %esp
18 
19          call   ___main
20 
21          movl $0, %eax
22 
23          leave
24 
25          ret

 

三、當用gcc main.c –S –O2進行編譯的時候,會進行部分優化,但不進行循環展開和函數內聯等操做,相比於O1的1級優化獲得的彙編代碼,多了一條對齊指令即.p2align 2,3;彙編程序以下:函數式編程

        

 1  .file  "main.c"
 2 
 3          .def  ___main; .scl   2;      .type         32;   .endef
 4 
 5          .text
 6 
 7          .p2align 2,,3
 8 
 9 .globl _main
10 
11          .def  _main;      .scl   2;      .type         32;   .endef
12 
13 _main:
14 
15          pushl         %ebp
16 
17          movl %esp, %ebp
18 
19          andl $-16, %esp
20 
21          call   ___main
22 
23          xorl  %eax, %eax
24 
25          leave
26 
27          ret

 

 

四、用gcc main.c –S –O3進行優化時,會進行循環展開,分支預測,函數內聯等,但與O2的2級優化獲得的彙編代碼同樣,多是由於在O2和O3的Gcc都能識別尾遞歸調用並進行優化,因此在這裏使用了尾調用方式,從代碼中也能夠看到有一條遞歸調用指令call main。查資料獲得,實現尾遞歸優化的選項是-foptimize-sibling-calls,是O2新增的的功能。彙編程序以下:函數

        

 1  .file  "main.c"
 2 
 3          .def  ___main; .scl   2;      .type         32;   .endef
 4 
 5          .text
 6 
 7          .p2align 2,,3
 8 
 9 .globl _main
10 
11          .def  _main;      .scl   2;      .type         32;   .endef
12 
13 _main:
14 
15          pushl         %ebp
16 
17          movl %esp, %ebp
18 
19          andl $-16, %esp
20 
21          call   ___main
22 
23          xorl  %eax, %eax
24 
25          leave
26 
27          ret

說到尾遞歸,則要說到遞歸調用。尾遞歸的wiki解釋以下:性能

尾部遞歸是一種編程技巧。遞歸函數是指一些會在函數內調用本身的函數,若是在遞歸函數中,遞歸調用返回的結果總被直接返回,則稱爲尾部遞歸。尾部遞歸的函數有助將算法轉化成函數編程語言,並且從編譯器角度來講,亦容易優化成爲普通循環。這是由於從電腦的基本面來講,全部的循環都是利用重複移跳到代碼的開頭來實現的。若是有尾部歸遞,就只須要疊套一個堆棧,由於電腦只須要將函數的參數改變再從新調用一次。利用尾部遞歸最主要的目的是要優化,例如在Scheme語言中,明確規定必須針對尾部遞歸做優化。[1][2]可見尾部遞歸的做用,是很是依賴於具體實現的。(http://zh.wikipedia.org/wiki/%E5%B0%BE%E9%80%92%E5%BD%92)優化

這種調用老是在函數末尾執行,而且不會用到調用函數裏的任何局部變量。因此有些編譯器對此進行優化,在被調用函數執行時,直接利用調用函數的堆棧,不須要從新開闢堆棧空間,因此通常不會致使遞歸中出現的棧溢出。而通常遞歸由於調用過程當中會存儲局部變量,因此調用次數太多時就會發生溢出。可是並非全部編譯器都會對尾遞歸進行優化,通常在函數式編程語言中會優化。因此當在gcc中用了-O2或者-O3優化選項以後,就會對尾遞歸進行優化,通常不會形成溢出,而默認的進行可能會形成溢出。能夠參考這篇博文:http://www.cnblogs.com/JeffreyZhao/archive/2009/04/01/tail-recursion-explanation.html。其中說到:與普通遞歸相比,因爲尾遞歸的調用處於方法的最後,所以方法以前所積累下的各類狀態對於遞歸調用結果已經沒有任何意義,所以徹底能夠把本次方法中留在堆棧中的數據徹底清除,把空間讓給最後的遞歸調用。這樣的優化便使得遞歸不會在調用堆棧上產生堆積,意味着即時是「無限」遞歸也不會讓堆棧溢出。spa

相關文章
相關標籤/搜索