我正在查看一些要查看的代码,并且遇到了这样的忙碌等待:
int loop = us*32;
int x;
for(x = 0;x<loop;x++)
{
/*do nothing*/
}
我似乎记得读过这些空循环可以优化掉。这是这里会发生的事情还是可以工作?
我正在查看一些要查看的代码,并且遇到了这样的忙碌等待:
int loop = us*32;
int x;
for(x = 0;x<loop;x++)
{
/*do nothing*/
}
我似乎记得读过这些空循环可以优化掉。这是这里会发生的事情还是可以工作?
答案是肯定的,编译器可以优化出循环。
使用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);
你受编译器的摆布。事实上,如果它很聪明,它会检测到它是一个 noop。顺便说一句,Neil Butterworth 有一篇不错的帖子,他也谈到了这个主题。
这是非常不便携的东西。
在某些编译器中,其中一种可能有效(但您必须检查是否启用了完全优化,空指令可能会被丢弃):
for (i = 0; i < spinCount; )
++i; // yes, HERE
或者:
for (i = 0; i < spinCount; ++i)
((void)0);
如果你足够幸运,那么你的编译器可能会提供一个宏或一个内部函数,它们将编译为nop
汇编指令,就像__noop
在 MSVC 中一样。
作为最后一个资源,您可以简单地添加一条汇编指令(它取决于编译器,可能是 __asm 或类似的东西)来执行......什么都没有,像这样:
for (i = 0; i < spinCount; ++i)
__asm nop
或(检查您的编译器文档):
for (i = 0; i < spinCount; ++i)
asm("nop");
编辑
如果您没有noop
指令并且无法添加汇编代码(对不起,您使用的是哪种编译器?)您可以依赖这样的假设,即不会优化具有副作用的指令离开(或者,正如@ouah 所发布的,访问声明的变量volatile
)。
语言标准中没有任何内容禁止它,因此编译器可以做到这一点。
让我们反编译 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。所以它被完全优化掉了。
可以对任何编译器进行相同的分析。
也可以看看
一些编译器,比如 gcc,会检测到它是一个空的 for 循环,并为此特别悲观,期望你把它作为延迟循环放在那里。您可以在http://gcc.gnu.org/onlinedocs/gcc-4.7.0/gcc/Non_002dbugs.html阅读更多相关信息
请注意,这是特定于编译器的,因此不要指望所有编译器都使用它。