【问题标题】:Will an empty for loop used as a sleep be optimized away? [duplicate]用作睡眠的空 for 循环会被优化掉吗? [复制]
【发布时间】:2012-05-05 05:28:18
【问题描述】:

我正在查看一些要查看的代码,并且遇到了这样的忙碌等待:

int loop = us*32;
int x;
for(x = 0;x<loop;x++)
{
    /*do nothing*/      
}

我似乎记得读过这些空循环可以优化掉。这是这里会发生的事情还是可以这样做?

【问题讨论】:

  • us?如果这段代码有效,那一定是一个非常慢的 CPU。
  • @KarolyHorvath 这是一个嵌入式系统,可能确实有一些非常慢的处理器(按照今天的标准)。这不是我完全熟悉的东西

标签: c


【解决方案1】:

语言标准中没有禁止它,因此编译器可以做到这一点。

让我们反编译 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。所以它被完全优化掉了。

可以对任何编译器进行相同的分析。

另请参阅

【讨论】:

    【解决方案2】:

    这是非常不便携的东西。

    在某些编译器中,其中一种可能有效(但您必须检查是否启用了完全优化,空指令可能会被丢弃):

    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】:

      答案是肯定的,编译器可以优化出循环。

      使用volatile 限定符来避免优化:

      int loop = us * 32;
      volatile int x;
      for (x = 0; x < loop; x++)
      {
          /*do nothing*/      
      }
      

      如果您在嵌入式世界中进行编程,请阅读编译器的文档,因为它们通常提供延迟函数,这些函数会等待一定数量的周期或微秒传入参数。

      例如avr-gccutil/delay.h中有如下功能:

      void _delay_us(double __us);
      

      【讨论】:

      【解决方案4】:

      一些编译器,比如 gcc,会检测到它是一个空的 for 循环,并为此特别悲观,期望你把它作为一个延迟循环放在那里。你可以在http://gcc.gnu.org/onlinedocs/gcc-4.7.0/gcc/Non_002dbugs.html阅读更多相关信息

      请注意,这是特定于编译器的,所以不要指望所有编译器都使用它。

      【讨论】:

      • 确实,我在 mingw/msvc 中遇到了这个问题,我不得不调试代码,对另一个线程修改的变量进行忙碌等待,并且循环被“优化”(在 mingw仅使用 -O2 的情况)进入跳转到自身的 jmp 语句(即无限循环),调试它很有趣。 :)
      【解决方案5】:

      你任由编译器摆布。事实上,如果它很聪明,它会检测到它是一个 noop。顺便说一句,Neil Butterworth 有一个nice post,他也谈到了这个主题。

      【讨论】:

      • 感谢您的回答。我的脑海里一直在说这种事情可能是个问题
      • 并非如此,即使您不能发出内联汇编代码,编译器也不会(也不能)丢弃具有局部副作用的表达式。
      猜你喜欢
      • 2014-03-05
      • 1970-01-01
      • 1970-01-01
      • 2020-05-12
      • 1970-01-01
      • 2017-01-27
      • 2013-01-17
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多