Forum: Compiler & IDEs Optimizer Bug mit (-O3) bei WinAVR-20090313


von Peter S. (psavr)


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

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


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.

von Kai G. (runtimeterror)


Lesenswert?

Was sagt denn das Assembler-Listing?

Wenn O3 eine Geschwindigkeitsoptimierung ist (kenne die 
Compiler-Optionen nicht gut), tippe ich auf ein partielles 
loop-unrolling im main-Programm

http://de.wikipedia.org/wiki/Hotspot-Optimierung#Loop-Unrolling

Einfach ein Schuss ins Blaue.

Schöne Grüße,
Kai

von Peter S. (psavr)


Lesenswert?

Okey, das wirds wohl sein. Somit macht -O3 bi kleinen uC wie den AVR 
kaum Sinn....

von Fabian B. (fabs)


Lesenswert?

Das würde ich gernerell so nicht unterschreiben. Wenn dein Prog extrem 
zeitkritisch ist, könnten dir genau das den erhofften Erfolg bringen, 
dein Controller hätte ja genug Flash. ;-)

Aber im Allgemeinen funktioniert O2 eigentlich am besten (meine 
Erfahrung)

Gruß
Fabian

von Sven P. (Gast)


Lesenswert?

Jo, -O123 sind normalerweise für Geschwindigkeit, -Os für Programmgröße.

Wenn noch Programmspeicher übrig ist, ruhig mal die höchste 
Optimierungsstufe ausprobieren (trotzdem sauber programmieren!) -- 
ungenutzten Flash-Speicher kann man nämlich in der Regel nicht mehr im 
Geschäft zurückgeben oder umtauschen...

von Johann L. (gjlayde) Benutzerseite


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

von Peter D. (peda)


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

von Johann L. (gjlayde) Benutzerseite


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

von Sven P. (Gast)


Lesenswert?

Peter Dannegger schrieb:
> 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.
Klar.

von Andreas S. (andreas) (Admin) Benutzerseite


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.

Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.