How to prevent the GCC from optimizing the busy waiting cycle?

I want to write C code firmware for Atmel AVR microcontrollers. I will compile it using GCC. In addition, I want to enable compiler optimization ( -Os or -O2 ), because I see no reason not to include them, and they will probably generate a better way to build faster than writing the assembly manually.

But I want a small piece of code not to be optimized. I want to postpone the execution of a function for some time, and therefore I wanted to write a do-nothing loop to spend some time. No need to be precise, just wait a while.

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

Since memory access in AVR is much slower, I want i and j stored in processor registers.




Update: I just found util / delay.h and util / delay_basic.h from AVR Libc . Although in most cases it would be better to use these functions, this question remains relevant and interesting.

+52
optimization c gcc avr-gcc
Aug 16 '11 at 18:55
source share
8 answers

I developed this answer after linking to the dmckee answer , but it takes a different approach than his / her answer.

Attribute documentation from GCC:

noinline This function attribute does not allow the function to be considered for embedding. If a function has no side effects, there are optimizations other than nesting that force optimization of function calls, although the function call is active. To avoid repeating such calls, put asm ("");

This gave me an interesting idea ... Instead of adding the nop instruction to the inner loop, I tried adding empty assembly code there, for example:

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

And it worked! This loop has not been optimized, and no additional nop instructions have been added.

What else, if you use volatile , gcc will store these variables in RAM and add a bunch of ldd and std to copy them to temporary registers. On the other hand, this approach does not use volatile and does not create such overhead.




Update. If you compile code using -ansi or -std , you must replace the asm keyword with __asm__ , as described in the GCC Documentation .

Alternatively, you can also use __asm__ __volatile__("") if your statement

+62
Aug 16 '11 at 19:55
source share

Declare variables i and j as volatile . This will prevent the compiler from optimizing the code using these variables.

 unsigned volatile char i, j; 
+18
Aug 16 '11 at 19:30
source share

I don’t know, at first, if the avr compiler version supports the full set of #pragma s (interesting in the link all date from gcc version 4.4), but that’s where you usually start.

+3
Aug 16 '11 at 19:30
source share

I am not sure why it has not yet been mentioned that this approach is completely erroneous and easily broken with compiler updates, etc. It would be much more sense to determine the value of the time that you want to wait, and turn off the polling of the current time until the required value is exceeded. On x86, you can use rdtsc for this purpose, but a more portable way would be to call clock_gettime (or an option for your OS other than POSIX) to get the time. Current x86_64 Linux will even avoid syscall for clock_gettime and uses rdtsc internally. Or, if you can handle the cost, just use clock_nanosleep to start with ...

+3
Sep 01 2018-11-11T00:
source share

put this loop in a separate .c file and do not optimize this file. Better yet, write this procedure in assembler and name it with C, in any case, the optimizer will not participate.

I sometimes do a mutable thing, but usually I create an asm function that simply returns put a call to this function, the optimizer will make the for / while loop tight, but it will not optimize it because it has to make all calls to the dummy function. Nop's answer from Denilson Sá does the same, but even stronger ...

+1
Aug 16 '11 at 10:16
source share

Enabling volatile asm should help. You can find out more about it here: -

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

If you work in Windows, you can even try to put the code under pragmas, as explained in detail below: -

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

Hope this helps.

+1
Aug 17 '11 at 19:18
source share

For me, on GCC 4.7.0, an empty asm was optimized anyway with -O3 (not trying with -O2). and using i ++ in case or volatility led to a big penalty for performance (in my case).

What I did was a link to another empty function that the compiler could not see when compiling the "main program"

Mainly:

Created "helper.c" with declared function (empty function)

 void donotoptimize(){} 

Then compiled "gcc helper.c -c -o helper.o" and then

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

This gave me the best results (and, in my opinion, no overhead at all, but I can’t check, because my program will not work without it :))

I think it should work with icc as well. Maybe not if you turn on link optimization, but with gcc it does.

+1
Jan 18 '14 at 20:23
source share

You can also use the register keyword. Variables declared with a register are stored in the CPU registers.

In your case:

 register unsigned char i, j; j = 0; while(--j) { i = 0; while(--i); } 
-one
Sep 06
source share



All Articles