Optimizer Bug mit (-O3) bei WinAVR-20090313

#1287684
Lesenswert?

Vermutlich handelt es sich nicht wirklich um einen Bug, aber dennoch um 
einen sehr sonderbaren Effekt: Mit Optimierung -O3 kann der erzeugte 
Code riesengross werden, bzw. wesentlich grösser als ohne Optimierung!

Bei einem kleinem Testprogramm:

-O0  574 Bytes
-O1  392 Bytes
-O2  390 Bytes
-O3 4850 Bytes!
-Os  386 Bytes

Woran kann das liegen?

Test-Programm
1
#include <stdlib.h>
2
#include <avr/io.h>
3

4
unsigned int root(unsigned long a)  //Calculate Square-root
5
{
6
  unsigned long rem = 0;
7
  unsigned long root = 0;
8
  for (int i=0; i<16; i++)
9
  {
10
    root <<= 1;
11
    rem = ((rem << 2) + (a >> 30));
12
    a <<= 2;
13
    root++;
14
    if (root <= rem)
15
    {
16
      rem -= root;
17
      root++;
18
    }
19
    else
20
    {
21
      root--;
22
    }
23
  }
24
  return (unsigned int)(root >> 1);
25
}
26

27
int main(void)
28
{
29
  volatile unsigned int result;
30
  for(unsigned int i=0; i<(0xffff); i=i+1)
31
  {
32
    result = root(i);
33
  }
34
}

Compiler-Output
1
**** Build of configuration Release for project Test ****
2

3
make all 
4
Building file: ../main.c
5
Invoking: AVR Compiler
6
avr-gcc -Wall -O3 -fpack-struct -fshort-enums -std=gnu99 -funsigned-char -funsigned-bitfields 
7
        -mmcu=atmega16 -DF_CPU=1000000UL -MMD -MP -MF"main.d" -MT"main.d" -c -o"main.o" "../main.c"
8
Finished building: ../main.c
9
 
10
Building target: Test.elf
11
Invoking: AVR C Linker
12
avr-gcc -Wl,-Map,Test.map -mmcu=atmega16 -o"Test.elf"  ./main.o   
13
Finished building target: Test.elf
14
 
15
Invoking: AVR Create Extended Listing
16
avr-objdump -h -S Test.elf  >"Test.lss"
17
Finished building: Test.lss
18
 
19
Create Flash image (ihex format)
20
avr-objcopy -R .eeprom -O ihex Test.elf  "Test.hex"
21
Finished building: Test.hex
22
 
23
Create eeprom image (ihex format)
24
avr-objcopy -j .eeprom --no-change-warnings --change-section-lma .eeprom=0 -O ihex Test.elf  "Test.eep"
25
Finished building: Test.eep
26
 
27
Invoking: Print Size
28
avr-size --format=avr --mcu=atmega16 Test.elf
29
AVR Memory Usage
30
----------------
31
Device: atmega16
32

33
Program:    4850 bytes (29.6% Full)
34
(.text + .data + .bootloader)
35

36
Data:          0 bytes (0.0% Full)
37
(.data + .bss + .noinit)
38

39
Finished building: sizedummy
Moderator Persönliche Seite #1287687
Lesenswert?

Peter S. schrieb:
> Vermutlich handelt es sich nicht wirklich um einen Bug, aber dennoch um
> einen sehr sonderbaren Effekt: Mit Optimierung -O3 kann der erzeugte
> Code riesengross werden, bzw. wesentlich grösser als ohne Optimierung!

Ja, -O3 ist letztlich sowas wie ``optimize for speed''.  Da werden
Schleifen entrollt, Funktionen inline expandiert etc. pp. was das
Zeug hält.
Persönliche Seite #1287942
Lesenswert?

Jede Optimierung kann nach hinten los gehen.

Ob eine Optimierung wirklich diesen Namen verdient, ist immer auch 
abhängig von der Zielarchitektur.

Einfach mal bedenken, daß die Voreinstellungen der aktivierten 
Optimierungsstrategien in avr-gcc die gleichen sind wie für 32- und 
64-Bit Systeme auch!

Und da AVR nun mal weder Date- noch Codecache hat, keine Pipeline 
(zumindest ist die Dauer von Befehlsausführungen unabhängig von deren 
Anordnung), Sprünge recht billig sind, manche Arithmetik jedoch 
vergleichsweise teuer (zB a << n, a/b, a%b) passt das eben bei weitem 
nicht immer. Nur daß der Code reisengroß wird, heisst nicht, daß er 
schneller wird ;-)

Es vergrößern sich zum Beispiel Sprungweiten. Während in kompaktem Code 
oft ein relativer, Sprung genommen werden kann, muss bei ausladendem 
Code die Bedingung umgedreht werden (kostet nix), und über einen 
absoluten Sprung gesprungen werden, welcher das eigentliche Sprungziel 
adressiert. Das ist langsamer und breiter. Wenn eine aufgerolle Schleife 
dann zig solcher Konstrukte enthält (Schleifenbedingung, break, ...), 
schiesst sich die Optimierung selbst ins Knie...

Unangenehm ist auch, daß viele Optimierungen die Lebensdauer von 
Registern erhöhen (zB loop invariant motion etc). Das macht den Code 
theoretisch schneller, aber wenn die Register ausgehen und stattdessen 
die Werte zum RAM hin- und hergeschaufelt werden müssen, ist der Code 
deutlich größer und merklich langsamer. Der Effekt lässt sich hin und 
wieder auch bei Inlining beobachten.

Um Gefühl defür zu bekommen, welche Optimierung geeignet ist, hilft nur 
eins: Hin und wieder muss man sich die Compilerausgabe anschauen.

Irgendwann hat man dann raus, was in 90% der Fälle gut taugt. -O3 gehört 
für avr-gcc aber sehr wahrscheinlich nicht dazu.

Die einzigen Änderungen, die avr-gcc an den Optionen vornimmt, sind
1
flag_delete_null_pointer_checks = 0;
2

3
if (!PARAM_SET_P (PARAM_INLINE_CALL_COST))
4
  set_param_value ("inline-call-cost", 5);
was dem Effekt entspricht von
1
-fno-delete-null-pointer-checks  --param inline-call-cost=5 ; anstatt 12

Johann
#1288015
Lesenswert?

Sven P. schrieb:
> Jo, -O123 sind normalerweise für Geschwindigkeit, -Os für Programmgröße.

Wobei das mit der Geschwindigkeit beim AVR schnell nach hinten losgehen 
kann, -Os ist oft auch am schnellsten.

Außerdem arbeiten die Optimierungen mit der Brechstange, d.h. es wird 
alles gleich optimiert, egal, ob alle 10 Tage aufgerufen oder alle 10µs.
Man kann also leicht Verzehnfachung des Codes erreichen bei 0% 
Geschwindigkeitszuwachs.

Andere Optimierungen neben -Os würden nur Sinn machen, wenn man sie 
selektiv anwenden könnte (im Sourcecode auf einzelnen Funktionen).


Peter
Persönliche Seite #1288032
Lesenswert?

Peter Dannegger schrieb:

> Wobei das mit der Geschwindigkeit beim AVR schnell nach hinten losgehen
> kann, -Os ist oft auch am schnellsten.

Würd ich auch empfehlen als Grundlage.

> Andere Optimierungen neben -Os würden nur Sinn machen, wenn man sie
> selektiv anwenden könnte (im Sourcecode auf einzelnen Funktionen).

Das geht ab 4.4.0 per
1
#pragma GCC optimize ...
2
__attribute__((__optimize__ ...))
http://gcc.gnu.org/onlinedocs/gcc-4.4.0/gcc/Function-Specific-Option-Pragmas.html#Function-Specific-Option-Pragmas

Johann
Admin Persönliche Seite #1288197
Lesenswert?

Peter Dannegger schrieb:
> Außerdem arbeiten die Optimierungen mit der Brechstange, d.h. es wird
> alles gleich optimiert, egal, ob alle 10 Tage aufgerufen oder alle 10µs.

Nicht grundsätzlich, GCC kann Laufzeitergebnisse des Profilers gprof bei 
der Optimierung berücksichtigen. Nur leider gibt's gprof nicht für den 
AVR. Wäre ein lustiges Projekt etwas ähnliches für den AVR zu 
implementieren, ob's bei der Optimierung viel bringt sei mal 
dahingestellt.

Antwort schreiben

Bitte melde dich an, um einen Beitrag zu schreiben.

oder

Mit Google-Account einloggen

Die Registrierung ist kostenlos und dauert nur eine Minute.

Jetzt registrieren