Hallo!
Nachdem mein Atmel Studio ja buggt wie blöd (siehe anderer Thread) hab
ich mal mit dem Programmers Notepad weiterprobiert. Die avr8-Toolchain
ersetzt grad meine alte Toolchain -> damit kann der atxmegaa1u auch
erkannt und programmiert werden (hello-wolrd-blink-led-Programm läuft).
Allerdings ist beim kompilieren mehrerer Dateien noch was faul:
Es wird nur das main-File kompiliert, nicht die anderen c-Files, die
eingebunden sind. Dadurch kommen Fehler zustande weil er versucht Zeug
einzubinden das er nicht übersetzt hat (hier zB eine usb_init-Funktion).
Den Aufruf des avr-gcc macht ein Makefile. Was muss man da dazufügen bzw
ändern, damit er alle c-Dateien in diesem Ordner übersetzt? Habs schon
mit "avr-gcc *.c" probiert, aber das führt zu Fehlern (wohl mit dem -o).
Michael D. schrieb:> Allerdings ist beim kompilieren mehrerer Dateien noch was faul:> Es wird nur das main-File kompiliert, nicht die anderen c-Files, die> eingebunden sind.
Wie sind sie denn eingebunden?
Michael D. schrieb:> Habs schon mit "avr-gcc *.c" probiert, aber das führt zu Fehlern (wohl> mit dem -o).
Also,
1
avr-gcc -Os -mmcu=avr0815 -o foo.elf *.c
würde in der Tat funktionieren, aber ich stimme den anderen zu, etwas
Wissen um Makefiles kann nicht schaden.
Irgendwo sollte auch noch mein "Mfile" herumfliegen als kleiner
Makefile-Generator, falls dir das hilft.
Wissen über Makefiles kann echt nicht schaden hab ich gemerkt. Läuft
jetzt und kompiliert alles.
War allerdings erstmal die Billiglösung mit Auflisten der Files (noch
sind es wenige). Das Makefile das ich hatte ist nämlich mit Variablen
und so geschrieben, um den Aufruf selbst zusammenzubasteln. Hab aber
jetzt grob verstanden wie sich das aufbaut.
Noch als zusätzlicher Hinweis, falls du später mal optimieren musst: Die
Reihenfolge der angegebenen c-Dateien hat starken Einfluss auf die Größe
des Binarys.
Unter welchen Bedingungen beobachtest Du das?
Es ist nämlich nicht erklärbar; der Compiler übersetzt jedes Sourcefile
separat, und erst der Linker fügt alle Objektdateien und benötigte
Libraries zu einem gemeinsamen Klumpen zusammen.
Die Reihenfolge der Objektdateien hat dabei keinen Einfluss auf die
Größe des Endergebnisses, wie auch, was soll da passieren?
Naja, nimm einfach mal ein größeres Projekt und kompiliere es so wie
hier vorgeschlagen. Also mit *.c
Dann nochmal mit separat angegebenen Files. Also z.B. 1.c 2.c 3.c oder
auch 2.c 1.c 3.c und vergleiche.
Ich verwende -flto, eventuell hat das einen Einfluss. Ein einzelner
gcc-Aufruf, also kompilieren und linken in einem.
Wie gesagt, erklären kann ich es auch nicht. Es ist einfach so.
Udo schrieb:> Naja, nimm einfach mal ein größeres Projekt und kompiliere es so wie> hier vorgeschlagen. Also mit *.c> Dann nochmal mit separat angegebenen Files. Also z.B. 1.c 2.c 3.c oder> auch 2.c 1.c 3.c und vergleiche.> Ich verwende -flto, eventuell hat das einen Einfluss. Ein einzelner> gcc-Aufruf, also kompilieren und linken in einem.>> Wie gesagt, erklären kann ich es auch nicht. Es ist einfach so.
Der Unterschied rührt aber nicht von anderer Linkreihenfolge, sondern
weil gerne mal vergessen wird, auch beim Linken Optimierungsoptionen wie
-Os mitzugeben. Das resultiert dann natürlich in einem "riesigen
Unterschied", weil ohne -Os compiliert wird.
Udo schrieb:> Hmm... Nein... ich mache doch alles in einem Rutsch. Mit einem einzelnen> Aufruf. Da gebe ich nur 1x -Os an, aber das reicht doch?!
Und wie unterscheiden sich die beiden Szenarien denn, die du beschrieben
hast?
Udo schrieb:> Naja, nimm einfach mal ein größeres Projekt und kompiliere es so wie> hier vorgeschlagen. Also mit *.c> Dann nochmal mit separat angegebenen Files. Also z.B. 1.c 2.c 3.c oder> auch 2.c 1.c 3.c und vergleiche.> Ich verwende -flto, eventuell hat das einen Einfluss. Ein einzelner> gcc-Aufruf, also kompilieren und linken in einem.>> Wie gesagt, erklären kann ich es auch nicht. Es ist einfach so.
Hab ich so verstanden, dass bei 2. ein separater Link-Aufruf erfolgt.
Johann L. schrieb:> Wäre interessant, dies als Testfall zu haben, um zu untersuchen,> woher> der "starke Einfluss auf die Größe des Binarys" kommt.
Vielleicht lädt Udo mal paar Files hoch, damit man das beschrieben
Verhalten mal selbst nachvollziehen kann? Wäre ganz nett, denn ich kann
mir auch keinen Grund für dieses Verhalten vorstellen.
Das tritt bei allen Projekten auf, die ich hier durch den Compiler jage
und die mehr als eine .c-Datei haben, aber die Quelltexte stehen unter
Schutz...
Daher nehme ich an, dass jeder es schnell selbst mal mit eigenen Werken
testen kann.
Wenn ich die Zeit finde, kann ich demnächst trotzdem eventuell mal ein
Dummy-Projekt zusammenschustern, dass den Effekt hervorruft. Dauert aber
etwas.
Es sind übrigens alle Compilerversionen betroffen. 4.x, 5.x, 6.x.
Udo schrieb:> Daher nehme ich an, dass jeder es schnell selbst mal mit eigenen Werken> testen kann.
Das ist es eben genau: wir alle hier können deine Beobachtung so
nicht nachvollziehen.
Um mal das Beispiel zu bringen, an dem ich sowieso gerade arbeite:
Udo schrieb:> Ist das auch so mit .c-Dateien anstatt .o-Dateien im Aufruf> (Compile+Link in einem)?
Mache ich nie. Allerdings wüsste ich nicht, warum sich dadurch nun
was ändern sollte, denn der Compiler muss sie ja ohnehin erstmal
separat in Assemblercode umwandeln, diesen danach durch den Assembler
schieben, und erst dann kann er sie dem Linker vorwerfen.
...weil nicht sein kann, was nicht sein darf...? Versuche es doch mal.
Wenn es keinen Unterschied macht, versuche ich in den nächsten Tagen mal
ein Beispiel zu basteln.
Udo schrieb:> ...weil nicht sein kann, was nicht sein darf...?
Nein, weil ich lieber an meinem eigentlichen Projekt weiterkommen
möchte statt deine Behauptungen beweisen zu müssen.
Ich verwende noch eine ganze Reihe von Parametern. Ich habe das oben mit
"[...]" deutlich gemacht, weil ich nicht weiß, was da jetzt Einfluss hat
und was nicht.
Hier die Liste:
-DF_CPU=20000000UL
-fdata-sections
-ffunction-sections
-flto
-fpack-struct
-fshort-enums
-funsigned-bitfields
-funsigned-char
-Os
-std=gnu99
-Wl,-gc-sections
Poste mal ein vollständige Beispiel.
Entweder hast Du einen Bug im gcc gefunden oder einen Bug in Deinem
Build-Script oder in Deinen Sourcen (einmal zu oft weak verwendet?). Auf
jeden Fall würd ich dem nachgehen wenn ich Du wäre. Vor allem würd ich
als erstes mal nachschauen worin genau die unterschiedlichen Kompilate
sich den nun unterscheiden.
Also, ich habe jetzt mal in mühevoller Kleinstarbeit (es sind mehr als
hundert Funktionen) eine Funktion herausgesucht, die durch eine andere
Reihenfolge größer wird.
In der .lss sieht man, dass die Unterschiede darin liegen, dass z.B.
rcall zu call und rjmp zu jmp wird. Das sind dann schonmal je 2 Byte
mehr. Das Ganze läppert sich halt über die vielen Funktionen.
Na ja, da kommen dann vielleicht 100 - 200 Byte zusammen, denn manche
Sprünge werden ja auch kleiner. Ein Unterschied mag da sein, riesig ist
der nicht.
Johann L. schrieb:> Der Unterschied rührt aber nicht von anderer Linkreihenfolge, sondern> weil gerne mal vergessen wird, auch beim Linken Optimierungsoptionen wie> -Os mitzugeben.
Hattest du nicht mal geschrieben, das gcc ab 5.irgendwas das nicht mehr
braucht?
Oliver
Udo schrieb:> In der .lss sieht man, dass die Unterschiede darin liegen, dass z.B.> rcall zu call und rjmp zu jmp wird.
Das ist zumindest erklärlich. Allerdings müsste man wohl ziemlich
stark herumzirkeln, wenn man solche Effekte gezielt ausnutzen möchte.
Udo schrieb:> In der .lss sieht man, dass die Unterschiede darin liegen, dass z.B.> rcall zu call und rjmp zu jmp wird. Das sind dann schonmal je 2 Byte> mehr. Das Ganze läppert sich halt über die vielen Funktionen.
Ok, also erwartete Effekte von -mrelax. Was "starker Einfluss auf die
Größe des Binarys" ist liegt ja im Auge des Betrachters :-)
Jörg W. schrieb:> Allerdings müsste man wohl ziemlich stark herumzirkeln, wenn man solche> Effekte gezielt ausnutzen möchte.
Vielleicht ohne Rumzirkeln mit LTO Partitioning und -flto=2 oder
-flto=3?
https://gcc.gnu.org/onlinedocs/gcc/Optimize-Options.html#index-flto-936
Der Call-Tree wird dann aufgespalten in Komponenten; vielleicht kann
dadurch erreicht werden, dass Funktionen, die in einer Call-Beziehung
stehen, in der gleichen ltrans-Komponente landen und damit im Binary
dichter beieibander, so dass die Wahrscheinlichkeit von RCALL erhöht
wird.
Außerdem kann man an anderen Optionen rumspielen, die dafür bekannt
sind, die Codegröße positiv zu beeinflussen wie -fno-caller-saves oder
-fno-tree-loop-optimize
AVR-GCC-Codeoptimierung: Feinabstimmung der OptimizerOliver S. schrieb:> Johann L. schrieb:>> Der Unterschied rührt aber nicht von anderer Linkreihenfolge, sondern>> weil gerne mal vergessen wird, auch beim Linken Optimierungsoptionen>> wie -Os mitzugeben.>> Hattest du nicht mal geschrieben, das gcc ab 5.irgendwas das nicht mehr> braucht?
Ja, ab GCC 5:
1
Command-line optimization and target options are now streamed on a
2
per-function basis and honored by the link-time optimizer. This change
3
makes link-time optimization a more transparent replacement of per-file
4
optimizations. It is now possible to build projects that require different
5
optimization settings for different translation units. Contrary to earlier
6
GCC releases, the optimization and target options passed on the link
7
command line are ignored.
8
9
Note that this applies only to those command-line options that can be
10
passed to optimize and target attributes. Command-line options affecting
11
global code generation (such as -fpic), warnings, optimizations affecting
12
the way static variables are optimized (such as -fcommon), debug output,
13
and --param parameters can be applied only to the whole link-time
14
optimization unit. In these cases, it is recommended to consistently
15
use the same options at both compile time and link time.
Udo schrieb:> Das Ganze läppert sich halt über die vielen Funktionen.
Kannst Du mal Angaben machen, wieviel % das ausmacht.
Bei <1% würde ich da kein Gewese drum machen.
Ich benutze noch den WINAVR 2010. Die Codedichte ist für meine
Anwendungen völlig ausreichend (typisch 5..30% Flash), inklusive float
und sprintf.
Das macht ca. 2-3% aus und entscheidet bei zwei Projekten (ATmega168)
darüber, ob das Binary noch gerade so in den Flash passt oder eben
nicht.
Nachdem der Code schon bis an die Kotzgrenze optimiert war, haben wir,
was die Optimierungsparameter angeht noch einen ziemlichen Aufwand
getrieben und sogar ein internes Tool geschrieben, dass ca. 200
Parameter nacheinander durchnudelt und dann die besten nochmal
untereinander kombiniert. Das brachte durchschlagenden Erfolg.
Der nächste Schritt war halt, dass das mit der Reihenfolge aufgefallen
ist. Angefangen hat es, glaube ich, damit, dass stupides Kompilieren mit
"*.c" als Parameter auf manchen Rechnern anderen Code erzeugte... da
wohl nach dem Einlesen der Dateiliste nicht sortiert wird und die
Reihenfolge vom Dateisystem vorgegeben wird.
PS: -fno-caller-saves macht keinen Unterschied. Mit
-fno-tree-loop-optimize braucht das Projekt ca. 200 Bytes mehr. :P
Udo schrieb:> Mit -fno-tree-loop-optimize braucht das Projekt ca. 200 Bytes mehr.
Manchmal hilt die Kombination mit -fno-move-loop-invariants, aber es
hängt natürlich auch viel vom konkreten Code ab, den wir ja nochj
nichtmal auszugsweise kennen.
Manchmal hilft auch an der Registerpräperenz rumzudiddln mit -morder1/2.
Vor allem wenn ihr verschiedene Optionen durchnudelt, und jeweils die
kleinsten Objekte kombiniert (mit -flto funktioniert das dann aber
offensichtlich nicht mehr).
-fno-move-loop-invariants gehört in der Tat zu den Parametern, welche
unser Tool ausspuckt.
Was nicht gemacht wird, ist mit --param name=value irgendwelche
Kostenfunktionen zu verändern. Das sind auch so unüberschaubar viele,
dass es nicht so ein No-Brainer ist, wie einfach Parameter
durchzunudeln.
...und was auch helfen kann: Im List-File nach "Nestern" von 32-Bit
instruktionen LDS, STS suchen und indirekte Zugriffe forcieren. Das ist
natürlich nur dann sinnvoll, wenn das gleiche Objekt mehrfach
zugegriffen wird, z.B. wenn man Daten als Struktur abgebildet hat oder
viel 32-Bit Werte im Static Storage hat.
Der Quellcode wird dadurch zwar nicht schoner, aber wenn ein
Legacy-Projekt dadurch übersetzbar wird, sei's eben drum...
Johann L. schrieb:> Vielleicht ohne Rumzirkeln mit LTO Partitioning und -flto=2 oder> -flto=3?
Welche Bedeutung hat das n in -flto=n? Ich kann keine Erwähnung finden
in der Doku.
If you specify the optional n, the optimization and code generation done
6
at link time is executed in parallel using n parallel jobs by utilizing
7
an installed make program. The environment variable MAKE may be used to
8
override the program used. The default value for n is 1.
Der entsprechende Schalter scheint aber --param lto-partitions=2 zu
sein, aber mit einem kleinen Projekt mit 2 simpel-Modulen wird trotzdem
nur 1 Partition erzeugt... Vermutlich muss man da noch mehr drehen, z.B.
--param lto-max-partition=, Ob das irgendwie sinnvoll ist, kann ich
nicht sagen; war nur so eine Idee wegen -mrelax...
Das Problem lässt sich mit einem einzigen Parameter komplett lösen:
-mmu=Mega328
Für den Zeitaufwand, den ihr (wer auch immer ihr ist) da schon betrieben
habt, lassen sich viele passende Prozessoren kaufen.
In diesem Sinne...
Oliver
Oliver S. schrieb:> Das Problem lässt sich mit einem einzigen Parameter komplett lösen:
Davon abgesehen, dass du nichtmal in der Lage bist, die Option richtig
zu schreiben, hast du offenbar noch nie ein Industrieprojekt mit
bereits beim Kunden ausgerollter Hardware vor dir gehabt … Dann wäre
es aber besser, wenn du dich mit derart klugen Ratschlägen ein wenig
zurück hieltest.
Je nun, ihr seit doch eh am Ende der Fahnenstange.
Selbst, wenn das aktuelle Update dann doch noch mit allen Tricks in den
Prozessor passt, der Aufwand für das nächste wird noch mal exponentiell
höher sein, ohne Garantie auf Erfolg.
Oliver
Ich kann dir nur sagen: Selbst wenn kein Update unbedingt notwendig ist,
neue Funktionen zu bieten und kleinere Fehler zu beseitigen, kommt immer
super an. Dann kauft der Kunde eher auch neue Hardware beim gleichen
Hersteller. Davon könnte sich so mancher TV-Hersteller eine Scheibe
abschneiden. Wie schrottig die Software da oft ist und schon nach einem
Jahr wird nix mehr dran gemacht! Das ist echt arm. Aber jetzt auch OT.
;)
Ist ja alles unbestritten richtig.
Wieviel Aufwand da gerechtfertigt ist, ist die unternehmerische
Entscheidung desjenigen, der die unternehmerische Verantwortung dafür
trägt. Wenns passt, dann habt ihr zumindest auf jeden Fall noch lange
Zeit einen Job ;)
Oliver
Die besten Optimierungsergebnisse habe ich immer auf Sourcecode-Ebene
erreicht. D.h. durch Vermeidung von Copy&Paste und bessere
Modularisierung.
Es lohnt sich oft schon ab 2 ähnlichen Codesequenzen eine Unterfunktion
daraus zu machen. Auch sollte man zusammenhängende Daten als Struct
definieren, dann kann der Compiler sie über Pointer mit Displacement
adressieren.