Forum: Compiler & IDEs Optimierung -Os versus -O1


von Stefan F. (Gast)


Lesenswert?

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?

: Verschoben durch Admin
von (prx) A. K. (prx)


Lesenswert?

Gewohnheit. Wenn -O1 passt, dann mach es so.

von holger (Gast)


Lesenswert?

>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.

von Nop (Gast)


Lesenswert?

Stefan U. schrieb:
> Und Stack Überläufe sind besonders eklig.

Was besonders bei common subexpression elimination droht. Solange man 
nicht durch -Os den nächstkleineren Mikropoezossor wählen kann, was die 
Flashgröße angeht, ist das eh witzlos.

Siehe auch Nigel Jones zu dem Thema: 
http://embeddedgurus.com/stack-overflow/2008/09/efficient-c-tips-4-use-speed-optimization/

von Peter D. (peda)


Lesenswert?

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.

von Stefan F. (Gast)


Lesenswert?

> 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?

von (prx) A. K. (prx)


Lesenswert?

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.

: Bearbeitet durch User
von Nop (Gast)


Lesenswert?

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".

von Stefan F. (Gast)


Lesenswert?

> 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.

von (prx) A. K. (prx)


Lesenswert?

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 ...

: Bearbeitet durch User
von m.n. (Gast)


Lesenswert?

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.

von Stefan F. (Gast)


Lesenswert?

> 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.

von MaWin. (Gast)


Lesenswert?

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.

von Peter D. (peda)


Lesenswert?

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.

: Bearbeitet durch User
von Bernd K. (prof7bit)


Lesenswert?

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)

von m.n. (Gast)


Lesenswert?

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 ;-)

von Bernd K. (prof7bit)


Lesenswert?

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.

: Bearbeitet durch User
von Peter D. (peda)


Lesenswert?

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.

von (prx) A. K. (prx)


Lesenswert?

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.

von m.n. (Gast)


Lesenswert?

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 
;-)

von Yalu X. (yalu) (Moderator)


Lesenswert?

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.

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.