2011-11-15 261 views
3

我對編譯器知之甚少,但知道它們很複雜且足夠聰明,可以優化代碼。說我的代碼是這樣的:GNU編譯器優化

string foo = "bar"; 
for(int i = 0; i < foo.length(); i++){ 
    //some code that does not modify the length of foo 
} 

請問GNU編譯器足夠聰明,意識到的foo長度不超過這個循環的過程中改變和更換foo.length()呼叫使用適當的價值?或者foo.length()每被比較?

回答

6

要知道肯定的唯一方法就是試試看看裝配。

我的猜測是,如果內部調用length(),則Loop Invariant Code Motion會將length()的內部從循環中提取出來,並將其替換爲單個變量。

作爲第二個想法,這甚至可能是沒有意義的。字符串的大小可能只是string類中的一個簡單字段 - 它在堆棧中。因此,只需內嵌對length()的調用就可以減少對簡單變量訪問的調用。

編輯: 在該後一種情況下,它甚至不重要的foo長度是否被循環內修改。獲取字符串的長度已經只是一個變量訪問。

2

編譯器必須保證程序的行爲好像每個回合調用length()。如果它能證明沒有副作用並且結果確實是恆定的,它只能將呼叫從循環中提出。

實際案例中發生的情況需要逐案分析。如果你很好奇,那就看看這個裝配。

執行提升的典型方法是把做手工:

for (unsigned int i = 0, end = s.length(); i != end; ++i) 

也許你還想考慮現代for (char & c : s)作爲替代。

+1

當然,手動操作時,你必須確保循環的內部不會改變's'的長度。 –

+0

@GregHewgill:好吧,限制較少我會說你有責任確保循環體代碼是正確的。無論這可能意味着什麼。通常它會涉及確保解引用和訪問是正確的。 –

7

由於兩個Mysticial和Kerrek理所當然地認爲在生成的彙編偷看,這裏有一個例子:

#include <string> 
using namespace std; 

int does_clang_love_me(string foo) { 
    int j = 0; 
    for (int i = 0; i < foo.length(); i++) { 
     j++; 
    } 
    return j; 
} 

我保存在TEST.CPP上面的代碼和編譯它像這樣:

$ clang++ -o test.o -Os -c test.cpp 

-Os開關告訴clang嘗試優化最小的代碼大小。 GCC有一個你可以使用的相應開關。要查看程序集,我使用otool打開了生成的目標文件,因爲此刻我正在使用一個mac。其他平臺也有類似的工具。

$ otool -tv test.o 

test.o: 
(__TEXT,__text) section 
__Z16does_clang_love_meSs: 
0000000000000000 pushq %rbp 
0000000000000001 movq %rsp,%rbp 
0000000000000004 movq (%rdi),%rax 
0000000000000007 movq 0xe8(%rax),%rcx 
000000000000000b xorl %eax,%eax 
000000000000000d testq %rcx,%rcx 
0000000000000010 je 0x0000001e 
0000000000000012 cmpq $0x01,%rcx 
0000000000000016 movl $0x00000001,%eax 
000000000000001b cmoval %ecx,%eax 
000000000000001e popq %rbp 
000000000000001f ret 

這就像Mysticial說的;它只是一個可變訪問。

+0

+1實際嘗試! – Mysticial

0

老實說,我不知道gcc會如何優化這段代碼片段。但是,在循環外部移動冗餘碼稱爲「部分冗餘消除」。將循環外部的foo.length()稱爲循環不變代碼運動,是部分冗餘消除的一種形式。請看龍書第9.5節(我也在閱讀本章),它詳細闡述瞭如何使用數據流分析來解決這些類型的問題。這是斯坦福大學的幻燈片:http://suif.stanford.edu/~courses/cs243/lectures/l5.pdf。希望這些會有所幫助。