昨天在一个论坛里看到一个帖子,是关于无限循环的选择问题,之前也看到过很多次说空for比while(1)效率高的论述,只是之前一直没有功夫去考证。
话不多说,直入正题。
/*
*\brief 示例一:空for
*/
int main()
{
for(;;)
{}
return 0;
}
/*
*\brief 示例二:while(1)
*/
int main()
{
while(1)
{}
return 0;
}
借用曾经看到的某论坛大师逢帖必发的一句话“不要迷信语言,要迷信编译器”,撇去我对此人的偏见,对于这句话我还是比较认同的,毕竟自己写的代码最终是由你用的编译器来编译并连接成的,语言只是你和它打交道的中介,至于最终怎么解释执行,完全是它有最终解释权。
有点偏题,直接上编译器生成的汇编码(注,此处编译器及版本为:g++ (GCC) 4.4.6 20120305)
400554: 55 push %rbp
400555: 48 89 e5 mov %rsp,%rbp
400558: eb fe jmp 400558 <main+0x4>
40055a: 90 nop
40055b: 90 nop
40055c: 90 nop
40055d: 90 nop
40055e: 90 nop
40055f: 90 nop
两者生成的汇编代码一致,就不多贴一份了。
事实证明,两种无限循环写法最终运行效率一样,但是为什么这么多人说空for要比while(1)效率高呢?这其中还不乏一些很大的知名开源团队。出于科学求证的态度我又测试了老的GCC版本(g++ (GCC) 3.2.4),证明和上面大体是一样的。但是当我用VS2010和VC6.0测试时区别就出来了。
结果如下:
for(;;) 的反汇编:
int main()
{
00411350 push ebp
00411351 mov ebp,esp
00411353 sub esp,0C0h
00411359 push ebx
0041135A push esi
0041135B push edi
0041135C lea edi,[ebp-0C0h]
00411362 mov ecx,30h
00411367 mov eax,0CCCCCCCCh
0041136C rep stos dword ptr es:[edi]
for(;;)
{
}
0041136E jmp main+1Eh (41136Eh)
return 0;
00411370 xor eax,eax
}
while(1)的反汇编:
int main()
{
00411350 push ebp
00411351 mov ebp,esp
00411353 sub esp,0C0h
00411359 push ebx
0041135A push esi
0041135B push edi
0041135C lea edi,[ebp-0C0h]
00411362 mov ecx,30h
00411367 mov eax,0CCCCCCCCh
0041136C rep stos dword ptr es:[edi]
while(1)
0041136E mov eax,1
00411373 test eax,eax
00411375 je main+29h (411379h)
{
}
00411377 jmp main+1Eh (41136Eh)
return 0;
00411379 xor eax,eax
}
显而易见,VS平台下for(;;)生成的汇编指令少,并且不占用寄存器,没有多余的判断和跳转,比while (1)性能要好。
其实这种平台相关的代码之前我也在一篇博文中说过(见:
编译器背后的小故事),
C++标准仅仅是一些规则,而编译器才是最终规则的实现者,对于很多细节规则并没有限定,这也要求我们尽量明确写出与编译器实现差异无关的代码,就如char的实现是按照signed还是unsigned等等,这些标准没有具体说明,各个编译器厂商自然会有各自的实现方法,很多时候我们在一个平台习惯的东西,换到另一个平台就不一定凑效,这时候就需要我们知道编译器具体实现的过程,或者只需要看一下实现的结果。
正如那句“不存在没有bug的程序,只存在暂时未发现bug的程序”一样,程序员的世界里,“没有最好,只有更好” 也是至理名言 ---peakflys