Größenunterschiede (output) avr-gcc zwischen linux und win

Gast #675831
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?
Gast #675836
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.
Gast #675843
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.
Gast #675852
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.
Gast #675880
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.
Gast #675897
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ß.
Angehängte Dateien:
Gast #675974
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;
Gast #675980
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?
Gast #675989
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.
Gast #675995
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.
Gast #676006
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
Gast #676026
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.
Gast #676035
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
Gast #676048
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.
Gast #676079
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 ;-)
Moderator Persönliche Seite #676411
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.
Gast #676720
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.

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