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
textdatabssdechexfilename
2
529824226556615be
Linux:
1
textdatabssdechexfilename
2
814849826867221e0
ist jemand schonmal ähnliches aufgefallen? Woran kann das liegen?
> 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.
> 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.
> 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.
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.
Ü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;
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.
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:
-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?
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.
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.
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.
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...
> 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 ;-)
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.
> 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.