gcc是否会自动执行;展开;如果语句?
假设我有一个如下所示的循环:gcc是否会自动执行;展开;如果语句?,c,optimization,gcc,unroll,C,Optimization,Gcc,Unroll,假设我有一个如下所示的循环: for(int i = 0; i < 10000; i++) { /* Do something computationally expensive */ if (i < 200 && !(i%20)) { /* Do something else */ } } for(int i=0;i
for(int i = 0; i < 10000; i++) {
/* Do something computationally expensive */
if (i < 200 && !(i%20)) {
/* Do something else */
}
}
for(int i=0;i<10000;i++){
/*做一些计算上昂贵的事情*/
如果(i<200&!(i%20)){
/*做点别的*/
}
}
其中一些琐碎的任务被一个只运行了几次的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 */
}
for(int i=0;i<200;i++){
/*做一些计算上昂贵的事情*/
如果(!(i%20)){
/*做点别的*/
}
}
对于(int i=200;i<10000;i++){
/*做一些计算上昂贵的事情*/
}
gcc(使用适当的标志,如-O3)会自动将一个循环分解为两个,还是只展开以减少迭代次数?为什么不直接分解程序并亲自查看?但我们走了。这是测试程序:
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;
}
intmain(){
整数和=0;
int i;
对于(i=0;i<10000;i++){
如果(i<200&!(i%20)){
总和+=0xC0DE;
}
总和+=0xCAFE;
}
printf(“%d\n”,总和);
返回0;
}
这是使用gcc 4.3.3和-o3编译的反汇编代码中有趣的部分:
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 <__printf_chk@plt>
0x08048404:xor ebx,ebx
0x08048406:推送ecx
0x08048407:xor ecx,ecx
0x08048409:子esp,0xc
0x0804840c:lea esi[esi+eiz*1+0x0]
0x08048410:cmp ecx,0xc7
0x08048416:jg 0x8048436
0x08048418:mov eax,ecx
0x0804841a:imul esi
0x0804841c:mov eax,ecx
0x0804841e:sar eax,0x1f
0x08048421:sar edx,0x3
0x08048424:子edx,eax
0x08048426:lea edx,[edx+edx*4]
0x08048429:shl edx,0x2
0x0804842c:cmp ecx,edx
0x0804842e:jne 0x8048436
0x08048430:添加ebx,0xc0de
0x08048436:添加ecx,0x1
0x08048439:添加ebx,0xcafe
0x0804843f:cmp ecx,0x2710
0x08048445:jne 0x8048410
0x08048447:mov DWORD PTR[esp+0x8],ebx
0x080484B:mov DWORD PTR[esp+0x4],0x8048530
0x08048453:mov DWORD PTR[esp],0x1
0x0804845a:调用0x8048308
正如我们所看到的,对于这个特殊的例子,不,它不是。我们只有一个从main+32开始到main+85结束的循环。如果在读取汇编代码ecx=i时遇到问题;ebx=总和
但是,您的里程数可能会有所不同-谁知道在这种特殊情况下使用了什么样的启发式算法,因此您必须编译心中的代码,并查看更长/更复杂的计算对优化器的影响
尽管在任何现代CPU上,分支预测器在这样简单的代码上都能做得很好,所以在这两种情况下都不会有太多性能损失。如果您的计算密集型代码需要数十亿个周期,那么一些预测失误会造成什么性能损失?我当然记得GCC邮件列表中对此的讨论-至少有一些支持。通过检查汇编程序输出或
-da
,可能最容易验证它在您的情况下是否真的会这样做;它可能只展开了0或最大循环的情况,我不记得了。然而,在您的情况下(对于200-10000,它将是:test iYou可能会通过在每个迭代器上检查i的lsb来优化(和混淆)代码,因为奇数不会被20整除。如果iYou总是听到“循环中的If语句很慢”你从哪里听说的?只是好奇而已。啊,非常感谢!我从来不知道如何去分析反汇编代码。