如何防止GCC优化繁忙的等待循环?

如何防止GCC优化繁忙的等待循环?,c,optimization,gcc,avr-gcc,C,Optimization,Gcc,Avr Gcc,我想为Atmel AVR微控制器编写一个C代码固件。我将使用GCC编译它。另外,我想启用编译器优化(-Os或-O2),因为我认为没有理由不启用它们,而且它们可能会比手动编写汇编更快地生成更好的汇编方法 但是我想要一小段未优化的代码。我想将函数的执行延迟一段时间,因此我想编写一个不做任何事情的循环,只是为了浪费一些时间。不需要精确,只要等一段时间 /* How to NOT optimize this, while optimizing other code? */ unsigned char i

我想为Atmel AVR微控制器编写一个C代码固件。我将使用GCC编译它。另外,我想启用编译器优化(
-Os
-O2
),因为我认为没有理由不启用它们,而且它们可能会比手动编写汇编更快地生成更好的汇编方法

但是我想要一小段未优化的代码。我想将函数的执行延迟一段时间,因此我想编写一个不做任何事情的循环,只是为了浪费一些时间。不需要精确,只要等一段时间

/* How to NOT optimize this, while optimizing other code? */
unsigned char i, j;
j = 0;
while(--j) {
    i = 0;
    while(--i);
}
由于AVR中的内存访问要慢得多,我希望
I
j
保存在CPU寄存器中


更新:我刚刚找到了。虽然大多数时候使用这些函数可能是一个更好的主意,但这个问题仍然有效且有趣


相关问题:


i
j
变量声明为
volatile
。这将阻止编译器优化包含这些变量的代码

unsigned volatile char i, j;

我不知道avr版本的编译器是否支持(链接中有趣的部分都是从gcc版本4.4开始的),但这通常是您开始的地方。

我在跟随了来自的链接后得出了这个答案,但它采用了与他/她的答案不同的方法

GCC文件中提到:

noinline
此函数属性防止将函数考虑用于内联。如果函数没有副作用,那么除了内联之外,还有一些优化会导致函数调用被优化掉,尽管函数调用是活动的。为了避免这些调用被优化掉,请放置
asm(“”)

这给了我一个有趣的想法。。。我没有在内部循环中添加
nop
指令,而是尝试在其中添加一个空的汇编代码,如下所示:

unsigned char i, j;
j = 0;
while(--j) {
    i = 0;
    while(--i)
        asm("");
}
成功了!该循环尚未优化,没有插入额外的
nop
指令

此外,如果使用
volatile
,gcc将把这些变量存储在RAM中,并添加一组
ldd
std
,将它们复制到临时寄存器中。另一方面,这种方法不使用
volatile
,也不会产生这样的开销


更新:如果您使用
-ansi
-std
编译代码,则必须将
asm
关键字替换为
\uu asm\uu
,如下所示


此外,如果您的。

将该循环放在一个单独的.c文件中,并且不优化该文件,则还可以使用
\uuasm\uuuuuuuuuu\uuuuu volatile\uuuu(“”)
。最好在汇编程序中编写该例程并从C调用它,无论哪种方式,优化器都不会介入

我有时做一些不稳定的事情,但通常创建一个简单返回的asm函数
对该函数进行调用优化器将使for/while循环紧密,但不会对其进行优化,因为它必须对伪函数进行所有调用。Denilson Sá给出的nop答案也有同样的作用,但更为严格……

使用易失性asm应该会有所帮助。 您可以在此处阅读更多信息:-

如果您在Windows上工作,您甚至可以尝试将代码放在pragmas下,如下所述:-


希望这能有所帮助。

我不知道为什么还没有提到这种方法是完全错误的,很容易被编译器升级等破坏。确定要等待的时间值并旋转轮询当前时间,直到超过所需的值,这将更有意义。在x86上,您可以使用
rdtsc
实现此目的,但更方便的方法是调用
clock_gettime
(或非POSIX操作系统的变体)获取时间。当前的x86_64 Linux甚至将避免对
clock_gettime
的系统调用,并在内部使用
rdtsc
。或者,如果您可以处理系统调用的成本,只需使用
clock\u nanosleep
开始…

您也可以使用。用寄存器声明的变量存储在CPU寄存器中

就你而言:

register unsigned char i, j;
j = 0;
while(--j) {
    i = 0;
    while(--i);
}

对我来说,在GCC4.7.0上,空asm通过-O3进行了优化(没有尝试使用-O2)。 在寄存器或volatile中使用i++会导致很大的性能损失(在我的例子中)

我所做的是链接到另一个空函数,编译器在编译“主程序”时无法看到该函数

基本上:

创建“helper.c”,并声明此函数(空函数)

然后编译
gcc helper.c-c-o helper.o
然后

并通过
gcc my_benchmark.cc helper.o
链接它

这给了我最好的结果(据我所知,没有任何开销,但无法测试,因为没有它我的程序将无法工作:)


我认为它也应该与国际刑事法院合作。如果您启用链接优化,可能不会,但在gcc中,它会这样做。

\uuu asm\uuu
语句是不够的:更好地使用数据依赖关系

像这样:

main.c

int main(void) {
    unsigned i;
    for (i = 0; i < 10; i++) {
        __asm__ volatile("" : "+g" (i) : :);

    }
}
输出:

   0x0000000000001040 <+0>:     xor    %eax,%eax
   0x0000000000001042 <+2>:     nopw   0x0(%rax,%rax,1)
   0x0000000000001048 <+8>:     add    $0x1,%eax
   0x000000000000104b <+11>:    cmp    $0x9,%eax
   0x000000000000104e <+14>:    jbe    0x1048 <main+8>
   0x0000000000001050 <+16>:    xor    %eax,%eax
   0x0000000000001052 <+18>:    retq 
它似乎完全删除了循环和输出:

   0x0000000000001040 <+0>:     xor    %eax,%eax
   0x0000000000001042 <+2>:     retq
产生:

   0x0000000000001040 <+0>:     nop
   0x0000000000001041 <+1>:     nop
   0x0000000000001042 <+2>:     nop
   0x0000000000001043 <+3>:     nop
   0x0000000000001044 <+4>:     nop
   0x0000000000001045 <+5>:     nop
   0x0000000000001046 <+6>:     nop
   0x0000000000001047 <+7>:     nop
   0x0000000000001048 <+8>:     nop
   0x0000000000001049 <+9>:     nop
   0x000000000000104a <+10>:    xor    %eax,%eax
   0x000000000000104c <+12>:    retq
可在以下位置拆卸:

Dump of assembler code for function busy_loop:
   0x0000000000001140 <+0>:     test   %edi,%edi
   0x0000000000001142 <+2>:     je     0x1157 <busy_loop+23>
   0x0000000000001144 <+4>:     xor    %eax,%eax
   0x0000000000001146 <+6>:     nopw   %cs:0x0(%rax,%rax,1)
   0x0000000000001150 <+16>:    add    $0x1,%eax
   0x0000000000001153 <+19>:    cmp    %eax,%edi
   0x0000000000001155 <+21>:    ja     0x1150 <busy_loop+16>
   0x0000000000001157 <+23>:    retq   
End of assembler dump.
Dump of assembler code for function main:
   0x0000000000001040 <+0>:     mov    $0xa,%edi
   0x0000000000001045 <+5>:     callq  0x1140 <busy_loop>
   0x000000000000104a <+10>:    xor    %eax,%eax
   0x000000000000104c <+12>:    retq   
End of assembler dump.

相关线程:


在Ubuntu19.04、GCC 8.3.0中测试。

可能有某种“睡眠”系统调用?也许您可以嵌入一些汇编逻辑?为什么不插入类似于
volatile asm(“rep;nop;”)
的东西,通过浪费CPU周期来进行繁忙暂停,而这些CPU周期什么也不做?为什么不将这段代码放在函数中,并使用“-O0”与“-O2”代码的其余部分分开编译?很明显,它们是连在一起的。@George:刚刚检查过
avr libc
没有只等待一段时间的睡眠功能。相反,它映射到CPU
sleep
指令,该指令启动一种低功耗模式(有效地停止CPU)。好主意
int main(void) {
    unsigned i;
    for (i = 0; i < 10; i++) {
        __asm__ volatile("");
    }
}
   0x0000000000001040 <+0>:     xor    %eax,%eax
   0x0000000000001042 <+2>:     retq
__asm__ volatile("nop");
   0x0000000000001040 <+0>:     nop
   0x0000000000001041 <+1>:     nop
   0x0000000000001042 <+2>:     nop
   0x0000000000001043 <+3>:     nop
   0x0000000000001044 <+4>:     nop
   0x0000000000001045 <+5>:     nop
   0x0000000000001046 <+6>:     nop
   0x0000000000001047 <+7>:     nop
   0x0000000000001048 <+8>:     nop
   0x0000000000001049 <+9>:     nop
   0x000000000000104a <+10>:    xor    %eax,%eax
   0x000000000000104c <+12>:    retq
void __attribute__ ((noinline)) busy_loop(unsigned max) {
    for (unsigned i = 0; i < max; i++) {
        __asm__ volatile("" : "+g" (i) : :);
    }
}

int main(void) {
    busy_loop(10);
}
Dump of assembler code for function busy_loop:
   0x0000000000001140 <+0>:     test   %edi,%edi
   0x0000000000001142 <+2>:     je     0x1157 <busy_loop+23>
   0x0000000000001144 <+4>:     xor    %eax,%eax
   0x0000000000001146 <+6>:     nopw   %cs:0x0(%rax,%rax,1)
   0x0000000000001150 <+16>:    add    $0x1,%eax
   0x0000000000001153 <+19>:    cmp    %eax,%edi
   0x0000000000001155 <+21>:    ja     0x1150 <busy_loop+16>
   0x0000000000001157 <+23>:    retq   
End of assembler dump.
Dump of assembler code for function main:
   0x0000000000001040 <+0>:     mov    $0xa,%edi
   0x0000000000001045 <+5>:     callq  0x1140 <busy_loop>
   0x000000000000104a <+10>:    xor    %eax,%eax
   0x000000000000104c <+12>:    retq   
End of assembler dump.
void __attribute__ ((noinline)) busy_loop(unsigned max, unsigned max2) {
    for (unsigned i = 0; i < max2; i++) {
        for (unsigned j = 0; j < max; j++) {
            __asm__ volatile ("" : "+g" (i), "+g" (j) : :);
        }
    }
}

int main(void) {
    busy_loop(10, 10);
}