Im Artikel
https://www.mikrocontroller.net/articles/AVR-GCC-Codeoptimierung steht
zu -Os dass dies bei AVR die bevorzugte Einstellung sei, allerdings
vermisse ich dazu eine Begründung.
Ich sehe in meinen Projekten, dass -Os den Flash-Bedarf um 5% bis 10%
verringert. Dafür ist die Anzahl von Funktionsaufrufen und der RAM
Bedarf höher.
Mir geht viel häufiger das RAM aus, als der Flash. Und Stack Überläufe
sind besonders eklig.
Darum leuchtet mir nicht ein, warum -Os pauschal bevorzugt wird. Kennt
jemand den Grund?
>Darum leuchtet mir nicht ein, warum -Os pauschal bevorzugt wird. Kennt>jemand den Grund?
Es gibt keinen. Wähle die Optimierung die dir am besten passt.
Stefan U. schrieb:> Dafür ist die Anzahl von Funktionsaufrufen und der RAM> Bedarf höher.
Ein Call kostet 2 Byte RAM, ist also nicht teuer.
Wie hast Du festgestellt, daß mehr RAM benötigt wird?
Stefan U. schrieb:> Darum leuchtet mir nicht ein, warum -Os pauschal bevorzugt wird. Kennt> jemand den Grund?
Atmel war lange Zeit sehr knauserig mit Flash, die AT90S hatten nur
2..8kB Flash.
Und die ersten AVR-GCCs optimierten auch nicht besonders.
Ich hatte mal versucht, ein Programm vom AT89C2051 auf den AT90S2313 zu
portieren. Das Ergebnis war ~4kB groß, also keine Chance.
> Wie hast Du festgestellt, daß mehr RAM benötigt wird?
Das denke ich mir, weil Funktionsaufrufe auch Parameter haben. Und wenn
diese Funktionen wieder Funktionen aufrufen die wiederum Funktionen
aufrufen, steigt der Stack Bedarf deutlich an. Oder etwa nicht?
Hättest du ein Szenario, in denen das in signifikantem Umfang auftritt?
Also so, dass es abhängig von -Os vs -O1 einen erheblichen Unterschied
macht? Mir fällt da allenfalls die Auflösung von Endrekursion ein, aber
die sollte es in beiden geben.
A. K. schrieb:> Hättest du ein Szenario, in denen das in signifikantem Umfang auftritt?
Jones schreibt dazu im verlinkten Artiktel "common sub-expression
elimination".
> Hättest du ein Szenario, in denen das in signifikantem Umfang auftritt?
Nein, ich habe kein konkretes Szenario.
> Ein Call kostet 2 Byte RAM, ist also nicht teuer.
Doch, mindestens 2 Bytes pro Call. Beim ATtiny13 (mein am häufigsten
verwendeter µC) tut jedes Byte RAM weh. Und bei den größeren
Alternativen habe ich so viel Flash, das ich nicht das Gefühl habe, dort
jemand -Os zu benötigen.
Nop schrieb:> Jones schreibt dazu im verlinkten Artiktel "common sub-expression> elimination".
Tut er. Aber erstens liefert er zwar eine zu Halloween passende
Horrorbeschreibung, aber leider kein Beispiel.
Und zweitens ist das, was er an dieser Stelle schreibt, blühender
Unsinn. Beim in Compilerbau wohlbekannten Begriff "common subexpression
elimination" werden mehrfach auftretende Teilrechnungen mit gleichem
Ergebnis nur einmal durchgeführt und ggf. zwischengespeichert. Spart
sowohl (Code-) Platz als auch Zeit. Mit Unterprogrammen hat das nichts
zu tun.
Das führt allerdings zu zusätzlichen Variablen, vorzugsweise Registern,
für diese Teilergebnisse. Die können dann ggf. etwas mehr Stack
verbraten, zur Speicherung oder wg. push/pull der Register. Wer den
Stack an der Kante der Kapazität fährt, der kann das mit viel Pech zu
spüren kriegen.
https://en.wikipedia.org/wiki/Common_subexpression_elimination
Was er schreibt kenne ich aber von Microchips Compilern. Speziell die
8-Bit PICs neigen zu identischen Codestückchen an vielen Stellen. Die
durch Unterprogramme zu ersetzen kann sich bei Optimierung auf (Code-)
Platz lohnen. In Architekturen mit Registern variieren die Register,
weshalb das da weniger bringt. Keine Ahnung, wie Microchip das nennt. In
verschachtelter Form ist mir das dort aber nicht begegnet, also mit
einem Call in einem solchen Codestückchen, der wiederum auf ein solches
Codestückchen mit einem Call drin, der wiederum ...
Stefan U. schrieb:> Beim ATtiny13 (mein am häufigsten> verwendeter µC) tut jedes Byte RAM weh.
Leg beim nächsten Einkauf einen Groschen drauf und nimm einen ATtiny25.
Oder programmier das Teil einfach in Assembler.
> Leg beim nächsten Einkauf einen Groschen drauf und nimm einen ATtiny25.
Ich komme doch klar, kein Bedarf.
Unter anderem komme ich deswegen klar, weil ich mich mit den
Compiler-Optionen auseinander setze, anstatt einfach einen größeren
Controller zu verwenden.
Da es für mich ein Hobby ist, kann und möchte ich mir den Zeitaufwand
leisten.
Aber interessant ist doch:
Du hast mir den nächst größeren ATtiny25 empfohlen, um RAM Problemen
wegen -Os aus dem Weg zu gehen. Der hat jedoch auch doppelt so viel
Flash, also macht dann -O1 doch viel mehr Sinn - zumindest als
Standardempfehlung. Denn so gehe ich meinem Stack-Engpass aus dem Weg,
und bekomme auch noch bessere Performance obendrauf.
Warum sollte ich einen größeren und teureren Controller nehmen, wenn es
mit -Os sowohl beim Flash als auch beim RAM reicht? Selbst wenn es nur
10 ct sind, macht das viel aus bei Stückzahlen in den Millionen. Und
wenn ich den Auftrag nur dadurch akquirieren kann, dass ich eben 10 ct
preiswerter als die Konkurrenz bin, ist der Gewinn noch größer.
Stefan U. schrieb:> Beim ATtiny13 (mein am häufigsten> verwendeter µC) tut jedes Byte RAM weh.
Mir überhaupt nicht.
Bei 1kB Flash stoße ich immer nur an die Flashgrenze. Die RAM-Größe hat
mir beim ATtiny13 noch nie Probleme bereitet.
Wenn man bei der Programmierung sparsam mit globalen Variablen umgeht,
ist der RAM kaum der Flashenhals.
Ich lege sogar oft konstante Arrays und Strings zugunsten der besseren
Lesbarkeit in den RAM und nehme für Flagvariablen ein ganzes Byte.
Funktionsaufrufe kosten oft nur 2 Byte, da die Variablen in Registern
übergeben werden. Manchmal zwingen sie auch den Compiler, die Variablen
in Registern zu halten, d.h. sparen RAM. Ich hab jedenfalls keine
merkbaren Unterschiede festgestellt.
Deutlich Mehrbelastung des RAM bewirkt aber die abgeschaltete
Optimierung -O0.
Da ich keine Millionen Stückzahlen programmiere, ist der MC-Preis nur
wenig relevant. Ich nehme daher als 8-Pinner bevorzugt den ATtiny85 (8kB
Flash) und rechne auch float darauf. Der ATtiny13 steckt nur noch in
alten Projekten, als es den ATtiny85 noch nicht gab.
Also ich sag euch mal was bei mir (winzige Sensoren im industriellen
Umfeld, Platinen teilweise kleiner als ein Fingernagel) die häufigsten
Gründe sind warum der kleine Controller für eine Anwendung nicht
ausreicht und man etwas anderes (gößeres) braucht:
1. Zu wenig Flash für die Anwendung (manchmal sind 1536 Bytes halt doch
etwas knapp)
2. Fehlende oder unzureichende Peripherie
[lange nichts]
3. Zuwenig RAM (z.B. steht von vornherein (!) fest daß ich für eine ganz
bestimmte Anwendung mindestens einen 4k Puffer oder so brauchen werde,
zusätzlich zum sonstigen Kleinkram, dann bin ich mit einem Schlag dick
drüber während ich sonst immer dick drunter bin, egal was ich mache)
Bernd K. schrieb:> die häufigsten> Gründe sind warum der kleine Controller für eine Anwendung nicht> ausreicht und man etwas anderes (gößeres) braucht:
Reden wir schon über M4 oder zunächst noch über M0 ;-)
m.n. schrieb:> Reden wir schon über M4 oder zunächst noch über M0 ;-)
M0 wäre schon einer der "großen". Also zum Beispiel ein kleiner Kinetis
mit 8k Flash, fast schon überdimensioniert für die meisten Anwendungen.
Jedoch wenn ich über Hobbyprojekte nachdenke wäre ein M0+ für mich die
Untergrenze, mit nem 8051 und dem schrottigen Keil-Compiler möchte ich
mich privat nicht herumschlagen und auch den AVRs weine ich keine Träne
nach. Mit nem MKE04 kann man schon vernünftig und angenehm arbeiten und
es gibt ne exzellente und aktuelle Toolchain und Debuggingmöglichkeiten
dafür, alles was kleiner ist fass ich nur gegen Bezahlung an, nochmal 20
Cent pro Exemplar zu sparen als alleiniger Vorteil wäre im Hobby
vollkommen irrelevant.
Bernd K. schrieb:> mit nem 8051 und dem schrottigen Keil-Compiler möchte ich> mich privat nicht herumschlagen
Naja, der Keil C51 war so sau effizient, daß er erst den Weg für C auf
MCs bereitet hat. Vorher haben alle nur in Assembler ihre OTP-PICs usw.
mühsam programmiert.
Ich fand den Keil C51 einfach nur geil. Float war mit ~1kB auch auf nem
AT89C2051 einsetzbar.
Der 8051 ist ideal für Steuerungen und da ist auch der RAM völlig
ausreichend.
Für Grafik, Ethernet, Webinterface ist der 8051 aber nie gedacht
gewesen.
Peter D. schrieb:> Ich fand den Keil C51 einfach nur geil. Float war mit ~1kB auch auf nem> AT89C2051 einsetzbar. Der 8051 ist ideal für Steuerungen und da ist auch> der RAM völlig
128-256 Bytes RAM ist die optimale Grösse für einen 51er. Für mehr war
die Architektur nicht konstruiert.
Peter D. schrieb:> Ich fand den Keil C51 einfach nur geil.
Gut war auch, daß man an richtiger Stelle 2 x 0x90 in die C51.EXE
schreiben konnte und anschließend das Dongle den LPT1 nicht blockierte
;-)
A. K. schrieb:> Nop schrieb:>> Jones schreibt dazu im verlinkten Artiktel "common sub-expression>> elimination".>> Tut er. Aber erstens liefert er zwar eine zu Halloween passende> Horrorbeschreibung, aber leider kein Beispiel.>> Und zweitens ist das, was er an dieser Stelle schreibt, blühender> Unsinn. Beim in Compilerbau wohlbekannten Begriff "common subexpression> elimination" werden mehrfach auftretende Teilrechnungen mit gleichem> Ergebnis nur einmal durchgeführt und ggf. zwischengespeichert.
Ja, das ist mir auch aufgefallen.
Die Zusammenfassung gleichartigen Codes zu Unterprogrammen nennt sich
"Code Factoring" oder beim Keil C51 "Common Block Subroutines".
Der GCC macht kein Code Factoring (und wenn doch, gäbe es ganz sicher
eine Option, um es unabhängig von anderen Optimierungen abzuschalten).
Es gab zwar vor vielen Jahren einen Anlauf in diese Richtung, die
Entwicklung wurde aber bald wieder eingestellt, da kein wirklicher
Nutzen erkennbar war.