2011-02-16 31 views
8

說我有一個循環,如下所示:gcc會自動「展開」if語句嗎?

for(int i = 0; i < 10000; i++) { 
    /* Do something computationally expensive */ 
    if (i < 200 && !(i%20)) { 
     /* Do something else */ 
    } 
} 

其中一些瑣碎的任務卡住,只有運行的時間屈指可數if語句後面。 我一直聽說「循環中的if語句很慢!」所以,在(輕微),提高性能的希望,我除了拆分環插入:

for(int i = 0; i < 200; i++) { 
    /* Do something computationally expensive */ 
    if (!(i%20)) { 
     /* Do something else */ 
    } 
} 

for(int i = 200; i < 10000; i++) { 
    /* Do something computationally expensive */ 
} 

會的gcc(使用合適的標誌,如-O3)自動斷開一個循環分爲二,或僅做它展開以減少迭代次數?

+1

你爲什麼不以它爲基準? – fancyPants 2011-02-16 14:55:47

+1

我當然記得從GCC郵件列表中討論這個問題 - 至少有一些支持。在你的情況下是否真的這樣做可能最容易通過檢查彙編輸出或`-da`來驗證;它可能只是展開了0例或最大循環的情況下,我不記得了。然而在你的情況下(對於200-10000,它將是:測試我<200,++ i,測試我<10000)我懷疑它會產生任何顯着的性能差異 - 按每個循環一個或兩個循環的順序將會被計算代價昂貴的代碼所淹沒。 – Rup 2011-02-16 14:58:51

回答

11

爲什麼不拆解程序並親自查看?但是,我們走了。這是testprogram:

int main() { 
    int sum = 0; 
    int i; 
    for(i = 0; i < 10000; i++) { 
     if (i < 200 && !(i%20)) { 
      sum += 0xC0DE; 
     } 
     sum += 0xCAFE; 
    } 
    printf("%d\n", sum); 
    return 0; 
} 

,這是用gcc 4.3.3和-03編譯的反彙編代碼的有趣的部分:

0x08048404 <main+20>: xor ebx,ebx 
0x08048406 <main+22>: push ecx 
0x08048407 <main+23>: xor ecx,ecx 
0x08048409 <main+25>: sub esp,0xc 
0x0804840c <main+28>: lea esi,[esi+eiz*1+0x0] 
0x08048410 <main+32>: cmp ecx,0xc7 
0x08048416 <main+38>: jg  0x8048436 <main+70> 
0x08048418 <main+40>: mov eax,ecx 
0x0804841a <main+42>: imul esi 
0x0804841c <main+44>: mov eax,ecx 
0x0804841e <main+46>: sar eax,0x1f 
0x08048421 <main+49>: sar edx,0x3 
0x08048424 <main+52>: sub edx,eax 
0x08048426 <main+54>: lea edx,[edx+edx*4] 
0x08048429 <main+57>: shl edx,0x2 
0x0804842c <main+60>: cmp ecx,edx 
0x0804842e <main+62>: jne 0x8048436 <main+70> 
0x08048430 <main+64>: add ebx,0xc0de 
0x08048436 <main+70>: add ecx,0x1 
0x08048439 <main+73>: add ebx,0xcafe 
0x0804843f <main+79>: cmp ecx,0x2710 
0x08048445 <main+85>: jne 0x8048410 <main+32> 
0x08048447 <main+87>: mov DWORD PTR [esp+0x8],ebx 
0x0804844b <main+91>: mov DWORD PTR [esp+0x4],0x8048530 
0x08048453 <main+99>: mov DWORD PTR [esp],0x1 
0x0804845a <main+106>: call 0x8048308 <[email protected]> 

所以我們看到,對於這個特殊的例子,沒有它不是。我們只有一個循環從主+32開始,到主+85結束。如果您在閱讀彙編代碼ecx = i時遇到問題, ebx =總和。

但是你的里程可能會有所不同 - 誰知道這種特殊情況下使用的啓發式算法,因此你必須編譯你想到的代碼,看看更長/更復雜的計算會影響優化器。

儘管在任何現代CPU上,分支預測器在這樣簡單的代碼上都會做的很好,所以在任何情況下都不會看到太多的性能損失。如果您的計算密集型代碼需要數十億個週期,那麼可能會出現少量預測失誤的性能損失是多少?