2011-11-15 24 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()コールを置き換えるものではありませんことを実現するのに十分なスマートだろうか?または、iの比較ごとにfoo.length()が呼び出されますか?

答えて

6

確かに知る唯一の方法は、アセンブリを試してみることです。

length()への呼び出しがインライン化されている場合、Loop Invariant Code Motionはループ内のlength()の内部を持ち上げ、1つの変数に置き換えます。

第2の考えとしては、これは疑問でもあります。文字列のサイズは、おそらくstringクラスの単純なフィールドで、スタック上にあります。したがって、単にlength()への呼び出しをインライン化すると、単純な変数アクセスへの呼び出しを減らす効果があります。

EDIT:この後者の場合 、それも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には対応するスイッチがあります。アセンブリを見るために、私は現時点でmacを使用しているので、結果オブジェクトファイルをotoolでヒットします。他のプラットフォームにも同様のツールがあります。

$ 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 

それはミスティカルと同様です。それは単なる可変的なアクセスです。

+0

+1実際に試してみてください! – Mysticial

0

正直なところ、gccがこのコードスニペットを最適化する方法を正確にはわかりません。しかし、ループの外側で冗長コードを移動することは、「部分的冗長性除去」と呼ばれます。 foo.length()をループ外に移動すること(これはループ不変式コードモーションと呼ばれます)は、部分冗長性除去の1つの形式です。ドラゴンブックのセクション9.5(この章も読んでいます)を見てください。データフロー解析を使って、このような問題を解決する方法を詳しく説明しています。スタンフォード大学のスライドはhttp://suif.stanford.edu/~courses/cs243/lectures/l5.pdfです。これらが役立つことを願っています。

関連する問題