我正在查看一些代码进行审核,并且遇到了繁忙的等待:
int loop = us*32;
int x;
for(x = 0;x<loop;x++)
{
/*do nothing*/
}
我似乎记得读过这些空循环可以被优化掉。这是在这里发生的事情还是可以的?
答案 0 :(得分:15)
答案是肯定的,编译器可以优化循环。
使用volatile
限定符来避免优化:
int loop = us * 32;
volatile int x;
for (x = 0; x < loop; x++)
{
/*do nothing*/
}
如果您是在嵌入式世界中进行编程,请阅读编译器的文档,因为它们通常提供等待参数中传递的特定循环或微秒的延迟函数。
例如,avr-gcc
在util/delay.h
中具有以下功能:
void _delay_us(double __us);
答案 1 :(得分:12)
你受编译器的支配。事实上,如果它很聪明,它会发现它是一个noop。顺便说一句,Neil Butterworth有一个nice post,他也谈到了这个问题。
答案 2 :(得分:5)
这是非常不便携的东西。
在某些编译器中,其中一个可能有效(但是你必须检查启用完全优化,空指令可能会被丢弃):
for (i = 0; i < spinCount; )
++i; // yes, HERE
或:
for (i = 0; i < spinCount; ++i)
((void)0);
如果您足够幸运,那么您的编译器可能会提供一个宏或内部函数,它将编译为nop
汇编指令,类似于MSVC中的__noop
。
作为最后一个资源,你可以简单地添加一个汇编指令(它依赖于编译器,可能是__asm或类似的东西)来执行......没有,像这样:
for (i = 0; i < spinCount; ++i)
__asm nop
或(检查编译器文档):
for (i = 0; i < spinCount; ++i)
asm("nop");
修改强>
如果你没有noop
指令并且你不能添加汇编代码(对不起,你正在使用什么样的编译器?)你可以依赖于带有副作用的指令获胜的假设不被优化(或者,由@ouah发布,访问声明为volatile
的变量。)
答案 3 :(得分:3)
语言标准中没有任何内容可以禁止它,因此编译器可以做到这一点。
让我们反编译GCC 4.8以查看其功能
输入代码:
int main() {
int i;
for(i = 0; i < 16; i++)
;
}
编译和反编译:
gcc -c -g -std=c99 -O0 a.c
objudmp -S a.o
输出:
a.o: file format elf64-x86-64
Disassembly of section .text:
0000000000000000 <main>:
int main() {
0: 55 push %rbp
1: 48 89 e5 mov %rsp,%rbp
int i;
for(i = 0; i < 16; i++)
4: c7 45 fc 00 00 00 00 movl $0x0,-0x4(%rbp)
b: eb 04 jmp 11 <main+0x11>
d: 83 45 fc 01 addl $0x1,-0x4(%rbp)
11: 83 7d fc 0f cmpl $0xf,-0x4(%rbp)
15: 7e f6 jle d <main+0xd>
17: b8 00 00 00 00 mov $0x0,%eax
;
}
1c: 5d pop %rbp
1d: c3 retq
循环在那里:jle
跳回来。
使用-O3
:
0000000000000000 <main>:
0: 31 c0 xor %eax,%eax
2: c3 retq
只返回0.所以它完全被优化了。
可以对任何编译器进行相同的分析。
另见
答案 4 :(得分:2)
有些编译器,比如gcc,会检测到它是一个空的for循环,特别是对此感到悲观,期望你把它作为一个延迟循环放在那里。您可以在http://gcc.gnu.org/onlinedocs/gcc-4.7.0/gcc/Non_002dbugs.html
了解更多相关信息请注意,这是特定于编译器的,所以不要指望所有编译器。