Forum: Compiler & IDEs Größenunterschiede (output) avr-gcc zwischen linux und win


von pixis (Gast)


Lesenswert?

Hi,
ich hab grade eben avr-gcc (+stuff) nach 
http://www.nongnu.org/avr-libc/user-manual eingerichtet.
Läuft alles soweit.

Mir ist dann nach dem compilen ein recht deutlicher Größenunterschied 
aufgefallen.
Vorraussetzungen waren jeweilig aktueller avr-gcc, gleiche avr-libc, 
gleicher makefile, mit -Os
Hier der output von avr-size:
Windows:
1
   text    data     bss     dec     hex filename
2
   5298     242      26    5566    15be
Linux:
1
   text    data     bss     dec     hex filename
2
   8148     498      26    8672    21e0

ist jemand schonmal ähnliches aufgefallen? Woran kann das liegen?

von yalu (Gast)


Lesenswert?

> Vorraussetzungen waren jeweilig aktueller avr-gcc,

4.2.2 oder ein Prerelease von 4.3.0?

> gleiche avr-libc,

Beidesmal mit dem gleichen Compiler übersetzt?

> gleicher makefile, mit -Os

Und du bist sicher, dass nicht irgendein beiteiligtes Object-File oder
eine Library nicht (oder nicht neu) mit den gleichen Optionen
kompiliert worden ist? Auch mit den gleichen Linker-Optionen?

Schon ein

  make clean; make

proboert?

Ein so großer Unterschied - speziell auch in der Data-Section - deutet
normalerweise daraufhin, dass ganze Programmmodule weggelassen wurden.

von pixis (Gast)


Lesenswert?

> 4.2.2 oder ein Prerelease von 4.3.0?
4.2.2

> Beidesmal mit dem gleichen Compiler übersetzt?
nein, ich meinte die Version. Aber das dürfte nicht so einen 
Unterschied machen.

make clean war natürlich der erste Versuch, das Ergebnis steht im ersten 
Post...

Ich könnte mir das ja andersrum auch erklären. Die unter Win compilte 
Version läuft aber wie sie soll, wenn dann müsste ja da was weggelassen 
worden sein.

Was unter Linux rauskam folgte ja der Windows Version.

von yalu (Gast)


Lesenswert?

> Ich könnte mir das ja andersrum auch erklären. Die unter Win
> compilte Version läuft aber wie sie soll, wenn dann müsste ja da was
> weggelassen worden sein.

Ich habe gedacht, dass da vielleicht aus irgendeinem Grund ein
Programmmodul hineingerutscht ist, was gar nicht benötigt wird, aber
trotzem Speicher belegt.

Ansonsten kannst du natürlich mal die Einzelteile des Programms
(.o-Files und Libraries) größenmäßig miteinander vergleichen oder eine
Linker-Map-Datei erzeugen lassen und vergleichen. Dann müsstest du
sehen, wo der Speicher hin ist.

von pixis (Gast)


Lesenswert?

Okay, die map bin ich mal durchgegangen.
Es gibt hier erhebliche Unterschiede von den objects, teilweise 3-4 
fache Größe, manche auch kleiner.

Erklären kann ichs mir aber immer noch nicht.
Ich probier jetzt erstmal einige flags beim compilen des avr-gccs durch.

von yalu (Gast)


Lesenswert?

Wenn du möchtest, kannst du das Modul, bei dem der Größenfakto 3-4
beträgt, hier posten, dann sage ich dir, was mein GCC (unter Linux)
daraus macht.

von pixis (Gast)


Angehängte Dateien:

Lesenswert?

Das wäre cool.
hier noch der header:
1
#ifndef _CALCULATE_H_
2
#define _CALCULATE_H_
3
4
uint8_t chartoint(char c);
5
uint8_t calc_load(char *s);
6
uint16_t calc_rpm(char *s);
7
int16_t calc_ect(char *s);
8
uint8_t calc_speed(char *s);
9
int8_t calc_ita(char *s);
10
int8_t calc_iat(char *s);
11
float calc_afr(char *s);
12
13
#endif

hier hab ich real faktor 4, der .o file ist bei mir unter linux 16k 
groß.

von Stefan Salewski (Gast)


Lesenswert?

Übrigens: Statt dem Switch-Monster ganz unten im Code könnte man auch 
etwas in dieser Form schreiben -- ob der erzeugte Code dann kürzer ist 
hängt natürlich von den Optimierungs-Fähigkeiten des Compilers ab.

if ((c>='0') && (c<='9'))
  result = c - '0';
else if ((c>='A') && (c<='F'))
  result = c - 'A';
else result=0;

von Stefan Salewski (Gast)


Lesenswert?

Quatch!

if ((c>='0') && (c<='9'))
  result = c - '0';
else if ((c>='A') && (c<='F'))
  result = c - 'A' +10;
else result=0;

Gut, beim Switch-Monster muss man weniger überlegen.

von yalu (Gast)


Lesenswert?

Die Option -Os scheint bei deinem Programm genau das Gegenteil von dem
zu tun, was sie eigentlich sollte.

Erst mit -Os:
1
$ avr-gcc -mmcu=atmega8 -Os -c calculate.c 
2
$ ll calculate.o
3
-rw-r--r-- 1 ich users 14344 Oct 14 21:12 calculate.o
4
$ avr-size -A calculate.o 
5
calculate.o  :
6
section                 size   addr
7
.text                   2344      0
8
.data                      0      0
9
.bss                       0      0
10
.progmem.gcc_sw_table    836      0
11
Total                   3180


Dann ohne Optimierung:
1
$ avr-gcc -mmcu=atmega8 -O0 -c calculate.c 
2
$ ll calculate.o
3
-rw-r--r-- 1 ich users 4328 Oct 14 21:13 calculate.o
4
$ avr-size -A calculate.o 
5
calculate.o  :
6
section   size   addr
7
.text   1846      0
8
.data      0      0
9
.bss       0      0
10
Total   1846

40% kürzer, sehr seltsam ...

Erklärung: Der GCC inlinet bei -Os die Funktion chartoint. Da die
Funktion relativ groß ist und 18mal aufgerufen wird, gibt das schon
einige Bytes an Code zusammen.

Nun mit -Os aber ohne Inlining:
1
$ avr-gcc -mmcu=atmega8 -Os -fno-inline-functions -c calculate.c  
2
$ ll calculate.o
3
-rw-r--r-- 1 ich users 3588 Oct 14 21:17 calculate.o
4
$ avr-size -A calculate.o 
5
calculate.o  :
6
section                 size   addr
7
.text                    874      0
8
.data                      0      0
9
.bss                       0      0
10
.progmem.gcc_sw_table     44      0
11
Total                    918

Das sieht doch schon ganz gut aus.

Es erklärt zumindest, wie man aus dem gleichen Quellcode durch
geschickte Wahl der Compiler-Optionen den Objektcode um über 70%
verkleinern kann. Damit kannst du jetzt dein Programm auch unter Linux
zusammenstauchen.

Noch nicht geklärt ist, warum bei dir unter Windows weniger rauskommt
als unter Linux. Hast du vielleicht irgendwelche Compiler-Optionen in
der Enviromment-Variable CFLAGS stehen?

Ebenfalls nicht geklärt ist, warum der Compiler bei -Os die Funktion
überhaupt inlinet. Das sollte er eigentlich nur dann tun, wenn der
Funktionscode selbst kürzer als der Code für den Funktionsaufruf ist,
oder wenn die Funktion im gesamten Programm nur einmal verwendet wird.
Es ist normal, dass sich der GCC da ab und zu etwas verschätzt und
deswegen ein leicht suboptimales Ergebnis herauskommt. Aber bei einer
Funktion wie chartoint sieht doch eigentlich ein Blinder, dass sich
das Inlinen nicht lohnt. Keine Ahnung, vielleicht ein Bug?

von yalu (Gast)


Lesenswert?

Ach ja: Mit Stefans Vorschlag sparst du nochmals 12% Code:
1
$ avr-size -A calculate.o 
2
calculate.o  :
3
section   size   addr
4
.text    808      0
5
.data      0      0
6
.bss       0      0
7
Total    808

von pixis (Gast)


Lesenswert?

Jo, der code hat natürlich noch Optimierungspotential, das switchzeug 
sollte in erster Linie mal zum testen funktionieren.

Scheinbar liegst du damit trotzdem ziemlich gut, ich hab das grade 
probiert
und das waren satte 2k Unterschied in der text Sektion.
Irgendwie optimiert der avr unter Windows da besser.
Auch wenn das mein Hauptproblem erstmal löst, ist das ganze immer noch 
merkwürdig.

Ich seh grad da muss grade noch n typo reingerutscht sein,
in zeile 85 fehlt '}'. Sorry falls ich damit Suchzeit beansprucht hab.

von pixis (Gast)


Lesenswert?

Noch zu den letzten Posts (grad erst gesehen):
Das erklärt einiges, danke.
Meine CFLAGS und sonstige variablen sind alle clean, das kanns nicht 
sein.
Kannst du mal deinen output von avr-gcc -v posten? Nur mal als Abgleich.

von yalu (Gast)


Lesenswert?

1
$ avr-gcc -v
2
Using built-in specs.
3
Target: avr
4
Configured with: ../configure --prefix=/usr/local/avr --target=avr
5
--enable-languages=c,c++ --disable-nls --disable-libssp
6
Thread model: single
7
gcc version 4.2.2

von pixis (Gast)


Lesenswert?

identisch.
Ich probier das ganze mal mit gcc 4.1, mal sehen was der daraus macht.

von pixis (Gast)


Lesenswert?

Okay, hier nochmal alles mit dem original code (mit dem 
überdimensionalen switch)

gcc 4.1.0 unter linux:
1
   text    data     bss     dec     hex filename
2
   7390     242      26    7658    1dea out.elf

gcc 4.1.0 unter linux mit noinline:
1
   text    data     bss     dec     hex filename
2
   5224     242      26    5492    1574 out.elf

gcc 4.2.2 unter linux mit noinline:
1
   text    data     bss     dec     hex filename
2
   5876     498      26    6400    1900 out.elf

besonders interessant find ich die data-Sektion.

von pixis (Gast)


Lesenswert?

Nochmal nachschlag:
für .data:
das sind exakt 0x100 byte Unterschied, die kommen laut der .map
1
 .data          0x00800152      0x100 /usr/local/avr/lib/gcc/avr/4.2.2/avr4/libgcc.a(_clz.o)
2
                0x00800152                __clz_tab
daher.
Genau das fehlt in der .map mit gcc4.1

von yalu (Gast)


Lesenswert?

Interessanterweise wird Stefans Funktion nicht geinlinet, d.h. da kann
man sich das -fno-inline-functions sparen.

Wenn das Programm auf Geschwindigkeit optimiert werden sollte (-O2
oder -O3, wäre das erklärbar:

Der Compiler denkt, chartoint (pixis Version) ist sehr schnell, so
dass der Aufruf-Overhead einen bedeutenden Anteil an der gesamten
Ausführungszeit hat. Dieser Overhead kann mit Inlining auf Kosten des
Speichers eliminiert werden.

Stefans Funktion sieht für den Compiler vielleicht langsamer aus
(welche von beiden tatsächlich schneller ist, kann ich jetzt auch
nicht sagen, ist aber unerheblich), deswegen ist der Zeitgewinn durch
Inlining nicht so groß, also wird sie normal aufgerufen.

Aber bei -Os sollte der Compiler nicht die Geschwindigkeit auf Kosten
des Speichers optimieren und deswegen nicht inlinen.

Selbst bei -O2, wo das Inlining eher aktzeptiert würde als bei -Os,
wird weder pixis noch Stefans Funktion geinlinet. Erst bei -O3 ist
dies der Fall.

Alles sehr komisch. Hättest du doch nur nie diesen langen Switch
geschrieben ;-)

> besonders interessant find ich die data-Sektion.

Das muss aber an einem anderen Modul als dem geposteten liegen. Aber
interessant ist es schon, da hast du recht.

> 0x00800152                __clz_tab

clz_tab scheint irgendwie mit Floating-Point-Berechnungen zu tun zu
haben, aber was es genau damit auf sich hat, und warum der 4.1 das
nicht nötig hat, habe ich noch nicht herausgefunden.

von Benedikt K. (benedikt)


Lesenswert?

Irgendwie nimmt es der GCC 4.2.2 nicht ganz so genau mit den 
Optimierungseinstellungen. Meiner Erfahrung nach ist -O2 die beste 
Einstellung in Kompromiss auf Geschwindigkeit/Größe und erzeugt 
teilweise sogar den kompaktesten Code...

von yalu (Gast)


Lesenswert?

> Meiner Erfahrung nach ist -O2 die beste Einstellung in Kompromiss
> auf Geschwindigkeit/Größe und erzeugt teilweise sogar den
> kompaktesten Code...

-O2 nehme ich standardmäßig auch, zumindest solange, bis der
Flashspeicher voll zu werden droht.

Und dann geht halt die Detailoptimierei los. Ein paar Möglichkeiten
gibt's schon mit den -O-Optionen, wesentlich mehr mit den -f-Optionen,
wenn's noch genauer sein muss, kann man auch mit Funktionsattributen
einiges richten (auch wenn's im Quellcode nicht schön aussieht) und
schließlich kann ja der Quellcode selber von Hand optimiert werden.

Oder man nimmt einfach den nächstgrößeren AVR-Typ ;-)

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


Lesenswert?

yalu wrote:

> Ebenfalls nicht geklärt ist, warum der Compiler bei -Os die Funktion
> überhaupt inlinet. Das sollte er eigentlich nur dann tun, wenn der
> Funktionscode selbst kürzer als der Code für den Funktionsaufruf ist,
> oder wenn die Funktion im gesamten Programm nur einmal verwendet wird.
> Es ist normal, dass sich der GCC da ab und zu etwas verschätzt und
> deswegen ein leicht suboptimales Ergebnis herauskommt. Aber bei einer
> Funktion wie chartoint sieht doch eigentlich ein Blinder, dass sich
> das Inlinen nicht lohnt. Keine Ahnung, vielleicht ein Bug?

Ich denke auch, dafür sollte man einen Bugreport aufmachen.  Ich weiß,
dass die Schätzung des inline-Aufwandes einige Heuristiken beinhaltet,
aber für totale Falsschätzungen ist der Bugreport angebracht.  Das
Grundübel ist, dass es für den AVR keinen Simulator gibt, der gut
genug gepflegt ist, opensource ist, und für den sich jemand die Arbeit
machen würde, ihn an die GCC-Testumgebung dranzustricken.  Dadurch
können die GCC-Entwickler Seiteneffekte für den AVR nicht selbst testen,
d. h. wenn bei einer grundlegenden Umstrukturierung der Optimierung
(wie sie von GCC 3.x nach 4.x erfolgt ist) der für den AVR generierte
Code größer wird, während er für andere Targets kleiner wird, dann
merkt das dort einfach niemand.  Daher ist der Bugreport besonders
wesentlich, bitte auch als `regression' markieren, denn diese bekommen
in der Bearbeitung Vorrang.

von yalu (Gast)


Lesenswert?

> Dadurch können die GCC-Entwickler Seiteneffekte für den AVR nicht
> selbst testen,

Der x86-GCC verhält sich, was dieses Problem betrifft, genau gleich
komisch (es scheint also kein Fehler im Backend zu sein).

Deswegen würde es mich wundern, wenn da noch keiner gemeckert hätte.
Kann natürlich auch sein, dass ein PC-User mit 2GB-Hauptspeicher so
etwas gar nicht bemerkt oder ignoriert :-)

Ich habe mal in der Bug-Database gesucht, allerdings mangels Zeit nur
flüchtig. Mit Kombinationen der Stichwörter "-Os", "automatic",
"inline", "inlining" und "big function" habe ich allerdings nichts
passendes gefunden. Ich werde heute Abend etwas intensiver suchen.

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.