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

问题描述 投票:58回答:8

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

但我想要一小段没有优化的代码。我想延迟函数的执行一段时间,因此我想写一个do-nothing循环只是为了浪费一些时间。不需要精确,只需等待一段时间。

/* How to NOT optimize this, while optimizing other code? */
unsigned char i, j;
j = 0;
while(--j) {
    i = 0;
    while(--i);
}

由于AVR中的内存访问速度要慢很多,我希望ij保存在CPU寄存器中。


更新:我刚刚从util/delay.h找到了util/delay_basic.hAVR Libc。尽管大多数情况下使用这些功能可能更好,但这个问题仍然有效且有趣。


相关问题:

c optimization gcc avr-gcc
8个回答
73
投票

我按照dmckee's answer的链接开发了这个答案,但它采用了与他/她的答案不同的方法。

来自海湾合作委员会的Function Attributes文件提到:

noinline此函数属性可防止将内联函数考虑在内。如果函数没有副作用,那么除了内联之外,还有一些优化会导致函数调用被优化掉,尽管函数调用是实时的。为了防止此类调用被优化,请放入asm ("");

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

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

它奏效了!该循环尚未优化,并且未插入额外的nop指令。

更重要的是,如果你使用volatile,gcc会将这些变量存储在RAM中,并添加一堆lddstd将它们复制到临时寄存器。另一方面,这种方法不使用volatile并且不产生这样的开销。


更新:如果您使用-ansi-std编译代码,则必须将asm关键字替换为__asm__,如described in GCC documentation

此外,如果你的__asm__ __volatile__(""),你也可以使用assembly statement must execute where we put it, (i.e. must not be moved out of a loop as an optimization)


21
投票

ij变量声明为volatile。这将阻止编译器优化涉及这些变量的代码。

unsigned volatile char i, j;

5
投票

我不确定为什么还没有提到这种方法完全被错误引导并且很容易被编译器升级等打破。确定你想要等待的时间值并旋转轮询当前值会更有意义。超过所需值的时间。在x86上你可以使用rdtsc来实现这个目的,但更便携的方法是调用clock_gettime(或非POSIX OS的变体)来获取时间。当前的x86_64 Linux甚至可以避免clock_gettime的系统调用并在内部使用rdtsc。或者,如果您可以处理系统调用的成本,只需使用clock_nanosleep开始...


3
投票

如果编译器的avr版本支持full set of #pragmas(链接中有趣的那些日期来自gcc版本4.4),我不知道我的头脑,但这是你通常会开始的地方。


2
投票

对我来说,在GCC 4.7.0上,无论如何使用-O3对空asm进行了优化(没有尝试使用-O2)。并且在寄存器或volatile中使用i ++会导致很大的性能损失(在我的情况下)。

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

基本上这个:

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

void donotoptimize(){}

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

while (...) { donotoptimize();}

这给了我最好的结果(根据我的信念,根本没有开销,但无法测试,因为没有它我的程序将无法工作:))

我认为它也适用于icc。如果启用链接优化,可能不会,但是使用gcc可以。


1
投票

将该循环放在单独的.c文件中,不要优化那个文件。更好的是在汇编程序中编写该例程并从C调用它,无论是优化器都不参与。

我有时做易变的东西,但通常创建一个asm函数,简单地返回调用该函数,优化器将使for / while循环紧,但它不会优化它,因为它必须对虚函数进行所有调用。来自DenilsonSá的nop回答做了同样的事情,但更加紧张......


1
投票

放波动的asm应该会有所帮助。你可以在这里阅读更多内容: -

http://www.nongnu.org/avr-libc/user-manual/optimization.html

如果您正在使用Windows,您甚至可以尝试将代码放在编译指示下,如下面详细说明: -

https://www.securecoding.cert.org/confluence/display/cplusplus/MSC06-CPP.+Be+aware+of+compiler+optimization+when+dealing+with+sensitive+data

希望这可以帮助。


-1
投票

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

在你的情况下:

register unsigned char i, j;
j = 0;
while(--j) {
    i = 0;
    while(--i);
}
© www.soinside.com 2019 - 2024. All rights reserved.