2011-11-15 167 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。希望这些会有所帮助。