Forum: Compiler & IDEs Optimierung reduziert Code um 2/3, ist das normal?


von Jens N. (midibrain)


Lesenswert?

Hallo,

wenn ich im AVR-Studio die Optimierung AUSschalte und mein Programm 
compiliere ist der Flash so gut wie voll. Compiliere ich aber mit der 
Optimierungsstufe 0s reduziert sich der Code um 2/3 auf etwa 30% der 
Flashgröße.

Mein Code ist wahrlich nicht sehr optimal aber so eine Einsparung durch 
Optimierung wundert mich doch.

Ist das normal oder code ich so übel?

Dank für Antworten.

von D. I. (Gast)


Lesenswert?

Je nach Code nichts ungewöhnliches. Moderne Compiler haben schon ein 
paar gute Tricks und Kniffe. Und Compilerbau ist immer noch ein recht 
aktiver Forschungszweig :)

von Peter D. (peda)


Lesenswert?

Jens Neumann schrieb:
> Mein Code ist wahrlich nicht sehr optimal

Es ist nicht Deine Aufgabe den Code optimal zu schreiben, sondern 
lesbar.

Das Optimieren muß der Compiler machen.
Allerdings ist der Compiler kein Mensch, man muß ihm manchmal auf die 
Sprünge helfen.

Man kann ihn aber auch nicht zum Optimieren zwingen. Auch wenn man Code 
umstellt, macht er es trotzdem wie er es will. Seine Gewichtung, welcher 
Ausdruck ist teuer, entspricht manchmal nicht der Realität.


Peter

von Karl H. (kbuchegg)


Lesenswert?

Jens Neumann schrieb:

> Mein Code ist wahrlich nicht sehr optimal aber so eine Einsparung durch
> Optimierung wundert mich doch.
>
> Ist das normal oder code ich so übel?

Kann man so nicht sagen ohne den Code zu sehen.
Wenn du zb _delay_ms verwendest, dann verwundert diese Reduktion 
überhaupt nicht. Im Nicht-Optimierten Fall zieht dir das die komplette 
Floating Point Library rein, im optimierten Fall optimiert der Compiler 
die komplett weg (und das ist für ihn sogar noch eine einfache Übung).

von Jens N. (midibrain)


Lesenswert?

Hallo,

danke für die schnellen Antworten.

Ich verwende in der Tat einige _delay_ms(), allerdings nur in der 
Initialisierung vom LCD.

Wie sieht den das mit Eurem Code aus, Peter, Karl-Heinz.
Wird da auch soooo viel optimiert?

von Falk B. (falk)


Lesenswert?

@  Peter Dannegger (peda)

>> Mein Code ist wahrlich nicht sehr optimal

>Es ist nicht Deine Aufgabe den Code optimal zu schreiben, sondern
>lesbar.

Naja.

>Das Optimieren muß der Compiler machen.

Kann er nur bedingt.

>Allerdings ist der Compiler kein Mensch, man muß ihm manchmal auf die
>Sprünge helfen.

So in der Richtung. Wer Bubble-Sort hinschreibt kann nicht erwarten, 
dass der Compiler Quicksort drausmacht. Sinngemäß gilt das für alle 
anderen Algorithmen. Brain 2.0 schlägt den Compiler bei komplexen 
Aufgaben IMMER!

http://www.mikrocontroller.net/articles/AVR-GCC-Codeoptimierung#Prinzipien_der_Optimierung

von Karl H. (kbuchegg)


Lesenswert?

Jens Neumann schrieb:
> Hallo,
>
> danke für die schnellen Antworten.
>
> Ich verwende in der Tat einige _delay_ms(), allerdings nur in der
> Initialisierung vom LCD.

Völlig wurscht wo.
_delay_ms ohne aktivierter Optimierung zieht die Floating Point Library 
rein.
Ist aber auch so dokumentiert.

> Wie sieht den das mit Eurem Code aus, Peter, Karl-Heinz.
> Wird da auch soooo viel optimiert?

Wenn ich bei _delay_ms die Optimierung wegschalte, wird mein Code 
genauso größer :-)

Im Ernst. Kleb jetzt nicht an den 2/3. Du hast einen Fehler gemacht, 
wenn du bei _delay_ms die Optimierung abschaltest. Das ist keine Frage 
von Optimierung ja/nein, sondern von Fehler ja/nein.

Denn ohne aktivierte OPtimierung stimmen dann auch die Zeiten von 
_delay_ms nicht. Und ja, bei einer LCD Ansteuerung verwende ich genauso 
_delay_us wie alle anderen auch.

von Peter D. (peda)


Lesenswert?

Falk Brunner schrieb:
>>Es ist nicht Deine Aufgabe den Code optimal zu schreiben, sondern
>>lesbar.
>
> Naja.

Ich wollte das Optimieren nicht verbieten. Es sollte aber nachrangig 
sein. Die Funktion und die Lesbarkeit haben Vorrang.


Peter

von Jens N. (midibrain)


Lesenswert?

Hey,

hab die Optimierung nur mal so zum nachschauen was die so rausholt 
weggeschaltet und war dann eben sehr verwundert.

Hab jetzt mal die _delay_ms() auskommentiert und komme OHNE Optimierung 
auf
"nur" 80% Flashbelegung, mit sind es immer noch ca. 30 %.

Vielleicht sollte man sich ja das Programmieren mit _delay_ms() sowiso 
abgewöhnen da das ja unschöne Warterei erzeugt. Hab mir mit Hilfe des 
Timers eine pause() - Funktion gebaut die auch funktioniert.

Also scheint mein übriger Code nicht so grotten schlecht zu sein wie ich 
dachte, eine Optimierung um 5 / 8 sieht auch schon besser aus.

Was sind noch Speicherplatzfresser, was sollte man noch vermeiden.

Danke

von Karl H. (kbuchegg)


Lesenswert?

Peter Dannegger schrieb:
> Falk Brunner schrieb:
>>>Es ist nicht Deine Aufgabe den Code optimal zu schreiben, sondern
>>>lesbar.
>>
>> Naja.
>
> Ich wollte das Optimieren nicht verbieten. Es sollte aber nachrangig
> sein. Die Funktion und die Lesbarkeit haben Vorrang.

Ausserdem war da sicher nicht "algorithmisches Optimieren" im Gegensatz 
zu "blödsinnig den schlechtest möglichen Algorithmus und den noch so 
umständlich wie möglich implementieren" gemeint.

Für den einen ist es Optimieren. Für den anderen ist es ganz normales 
handwerkliches Können.

von Karl H. (kbuchegg)


Lesenswert?

Jens Neumann schrieb:

> Hab jetzt mal die _delay_ms() auskommentiert und komme OHNE Optimierung
> auf
> "nur" 80% Flashbelegung, mit sind es immer noch ca. 30 %.

Dann muss man weiter schauen.

> Vielleicht sollte man sich ja das Programmieren mit _delay_ms() sowiso
> abgewöhnen

Das sowieso.
Ausser für ganz kurze Wartezeiten, wie man sie zb bei einem LCD oder bei 
I2C benötigt, ist _delay_ms meistens nicht die Lösung. Ganz im 
Gegenteil: _delay_ms ist das Problem.

> Timers eine pause() - Funktion gebaut die auch funktioniert.

Das könnte ein Hinweis darauf sein, dass das noch nicht weit genug geht. 
Warten ist normalerweise genau das, was man nicht machen will. Warten 
ist passives 'vergeuden' von Rechenzeit. Etwas, das man sich auf einem 
µC in der AVR KLasse nicht leisten will und meistens auch nicht muss.

Der Ansatz lautet:
'Auswertug von Events, sobald sie anfallen' und nicht 'Warten'.
Eine Statemachine ist zb ein Ansatz, der fast immer gute Ergebnisse 
bringt.

von Jens N. (midibrain)


Lesenswert?

So,

nun habe ich mal das ganze _delay_xx() - Gedöhns rausgeschmissen und 
komme OHNE Optimieren auf gerade einmal 45% belegten Flash.

MIT Optimierung bleibt es bei ca. 30%. Das heist das der Code nur noch 
um 1/3 reduziert aus der Optimierung kommt was ich auf jeden Fall besser 
finde als die urschprünglichen 2/3.

Vielen Dank Euch für den Hinweis mit dem _delay_xx()!

von xfr (Gast)


Lesenswert?

Mach Dir da mal keine Sorgen drum. C ist nunmal eine Hochsprache, in der 
man seinen Willen abstrakter ausdrückt als in Assembler. Es gibt nicht 
für jeden Assembler-Befehl ein Äquivalent in C.

Auch wenn man noch so "optimalen" C-Code schreiben würde, ohne 
Compileroptimierung wird daraus kein effizientes Programm. Einfach 
deswegen, weil der Compiler dann nur ein paar einfache 
Standardschablonen benutzt, um jede C-Anweisung "wörtlich" in Assembler 
zu übersetzen. Dieser Modus ist eigentlich nur dafür da, wenn man im 
Simulator Zeile für Zeile nachvollziehen möchte, was das Programm macht.

von Karl H. (kbuchegg)


Lesenswert?

Jens Neumann schrieb:
> So,
>
> nun habe ich mal das ganze _delay_xx() - Gedöhns rausgeschmissen und
> komme OHNE Optimieren auf gerade einmal 45% belegten Flash.
>

?
Das ging aber schnell.
Funktioniert das Programm noch?

> MIT Optimierung bleibt es bei ca. 30%. Das heist das der Code nur noch
> um 1/3 reduziert aus der Optimierung kommt was ich auf jeden Fall besser
> finde als die urschprünglichen 2/3.

reiicht schon.
Immer dran denken: Für nicht benutztes Flash kriegst du kein Geld 
zurück. Die oberste Direktive ist: ein Programm muss in erster Linie 
fehlerfrei das gewünschte erledigen (ha, ha. fehlerfrei ... der war gut)

von Jens N. (midibrain)


Lesenswert?

Ja,

das Programm funktioniert noch, habe die _delay_`s mit meiner 
PausenFunktion ersetzt. Waren ja wirklich nur die in im lcd_init(), im 
übrigen Programm hatte ich schon die PausenFunktion verwendet.

Zugegebenermaßen ist die auch nicht so richtig toll:

Aufruf mit einem uint8_t Wert für die Anzahl der Timer0 Takte die 
pausiert werden soll,z.B.:

pause(4)

In der Funktion wird dann abgewartet bis das Timer0Register seinen Wert 
um die übergebene Anzahl an Takten erhöt hat:

while(TCNT0 < (TCNT0 + takte))
  {
    // tu nix
  }

Natürlich fallen einem dann auch gleich zwei Dinge auf:

1. das leidige "tu nix", also verschleudere wertvolle Zeit und
2. was wenn das Timer0Register schon bei z.B. 253 ist und die Pause aber 
4 Takte lang sein soll? In diesem Fall würde ja das while auf den 
Registerwert 257 warten der aber nicht erreicht werden kann!

Naja, das Programm läuft aber so richtig sauber sieht mir das selber 
nicht aus.

Wie macht Ihr das?

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

Jens Neumann schrieb:

> 2. was wenn das Timer0Register schon bei z.B. 253 ist und die Pause aber
> 4 Takte lang sein soll? In diesem Fall würde ja das while auf den
> Registerwert 257 warten der aber nicht erreicht werden kann!

Ja.  Wenn man nun die rechte Seite auf 8 Bit begrenzt, läuft
man in ein anderes Dilemma: es würde dann gegen 1 verglichen
(da die 256 wegfällt), und gewartet werden "solange TCNT0 kleiner
als 1".  Diese Bedingung ist aber sofort erfüllt.

Besser ist:
1
  uint8_t target = TCNT0 + takte;
2
  while (TCNT0 != target) { /* warten */ }

Aber für kurze Verzögerungen (wie man sie typisch bei der Bedienung
von LCDs hat) kannst du wirklich _delay_us() benutzen.  Der Timer
bringt da keinen Vorteil.  Längere Timings dagegen wertet man in der
ISR des entsprechenden Timers aus und führt dann die nächste Aktion
aus.  Dann muss man die Zwischenzeit nicht sinnlos verwarten.

von Karl H. (kbuchegg)


Lesenswert?

Jens Neumann schrieb:

> Wie macht Ihr das?

Du musst unterscheiden zwischen Initialisierung und regulärem Arbeiten.

Deine Pause Funktion unterscheidet sich konzeptionell ja in nichts von 
einem _delay_ms. In den einen Fall wird unproduktiv gewartet und im 
anderen Fall wird unproduktiv gewartet. D.h. das ist Jacke wie Hose. 
Egal wie du es implementierst - das Ergebnis ist ja in beiden Fällen 
identisch.

Aber: In der Initialisierungsphase ist das schon ok. Denn da muss das 
Komplettsystem ja sowieso warten, bis alle Komponenten hochgefahren und 
initialisiert sind. Und solange das dauert, solange dauert es eben.

Was anderes ist es dann in der Hauptschleife.
Und DAVON ist die Rede, wenn es heißt: ausser für kurzes Warten ist 
_delay_ms verboten.

ZB. eine Zeitschaltuhr
Nach Tastendruck soll ein Relais anziehen und 5 Sekunden später wieder 
abfallen. Und nebenbei gibt es noch ein paar Zusatzfunktionalitäten.
Wenn du die 5 Sekunden mit _delay_ms realisiert, bist du schon in der 
falschen Gasse gelandet.

von xfr (Gast)


Lesenswert?

Jens Neumann schrieb:
> Wie macht Ihr das?

Du musst Dich einfach von der prozeduralen Denkweise "mach dies, warte 
bis es fertig ist, dann mach das" verabschieden. Deine pause()-Funktion 
ist ja kein Stück besser als delay_ms().

Wie das im Einzelfall aussieht, hängt stark vom Programm ab. Aber beim 
einfachsten Beispiel, eine LED blinken lassen, würde es so aussehn:

Statt:
- LED an
- 1000 ms warten
- LED aus

Machst Du:
- Timer mit Intervall 1000 ms starten
- Andere Dinge erledigen
- In der Timer-ISR wird die LED umgeschaltet

Oder:
- Timer mit Intervall 1000 ms starten
- Andere Dinge erledigen
- Nachschauen ob Timer abgelaufen ist (Flag prüfen)
  => Nein, nichts tun
- Andere Dinge erledigen
- Nachschauen ob Timer abgelaufen ist (Flag prüfen)
  => Nein, nichts tun
- ...
- Timer-ISR setzt das Flag auf 1
- ...
- Nachschauen ob Timer abgelaufen ist (Flag prüfen)
  => Ja, LED umschalten
- ...

Das Hauptprogramm kann in der Zwischenzeit also alle möglichen anderen 
Dinge erledigen. Das Prinzip kann man auf fast alle anderen Aufgaben 
übertragen, bei denen Du bisher delay_ms benutzt hast. Allerdings muss 
man sich dann meistens ein paar Zustände speichern, damit man später an 
der Stelle weitermachen kann, an der man sonst gewartet hätte. Deshalb 
ist es für sehr kurze Delays manchmal sinnvoller, delay_us() zu 
verwenden. Für alles andere gibt es Timer.

von Jens N. (midibrain)


Lesenswert?

Dank für die Hinweise.

Viele Zeiten summiere ich im TimerInterrupt und setze dann ein Job_Flag 
welches global ist. So für die EchtZeit(sekunden) und die 
Tastenabfrage(100Hz).  Denke so kann man einen Timer vernünftig 
benutzen.

Meine PausenFunktion macht -so muss ich es leider einsehen- tatsächlich 
auch nix anderes als rumtrödeln.

Zumindest aber ist ohne die _delay`s der Code nicht so aufgepustet was 
ja mein Anliegen zu Beginn war.

von foreninterner troll (Gast)


Lesenswert?

Jens Neumann schrieb:
> Zumindest aber ist ohne die _delay`s der Code nicht so aufgepustet was
> ja mein Anliegen zu Beginn war.

Nochmal: Wenn du _delay_ms benutzt MUSS die Optimierung eingeschaltet 
sein, sonst ist das ein Fehler (die Zeiten stimmen nicht). Außerdem 
sehe ich keinen Grund die Optimierung abzuschalten (außer fehlerhaften 
Code der mit Optimierung nicht mehr läuft - vergessene volatile und so).

von Karl H. (kbuchegg)


Lesenswert?

Jens Neumann schrieb:

> Viele Zeiten summiere ich im TimerInterrupt und setze dann ein Job_Flag
> welches global ist. So für die EchtZeit(sekunden) und die
> Tastenabfrage(100Hz).  Denke so kann man einen Timer vernünftig
> benutzen.

Passt.

> Zumindest aber ist ohne die _delay`s der Code nicht so aufgepustet was
> ja mein Anliegen zu Beginn war.

Na ja. Mit OPtimizer ist der auch nicht wirklich aufgepustet.
Das "Problem" bei _delay_ms besteht darin, dass das im Grunde 
Warteschleifen sind. Die Anzahl der Wiederholungen wird aus der 
angegebenen Zeit und der Taktfrequenz ausgerechnet. Und damit man da ein 
wenig flexibel zu Rande geht, ist das erst mal eine 
Gleitkomma-Berechnung.

Was ja auch nicht weiter schlimm ist. Denn in dieser Berechnung ist 
alles bekannt, alle Zahlenwerte sind fix. D.h. der Optimizer kann 
hergehen und diese Berechnung durchführen und dann nur das Endergebnis 
einsetzen.
Was er normalerweise auch tut.
Nur: Wenn kein Optimizer aktiv ist, dann findet dieses 
Berechnung-Auswerten nicht im Optimizer statt, sondern zur Laufzeit. Und 
das bedeutet:
* Laufzeit (wodurch die Zeiten nicht stimmen)
* die enstprechend notwendige Funktionalität bzw. der 'Unterbau', der
  zur Berechnung des Gleitkommaausdrucks notwendig ist, muss mit
  ins Progamm eingebunden werden.

Das Ergebnis hast du gesehen :-)

von Mark B. (markbrandis)


Lesenswert?

Peter Dannegger schrieb:
> Ich wollte das Optimieren nicht verbieten. Es sollte aber nachrangig
> sein. Die Funktion und die Lesbarkeit haben Vorrang.

"Premature optimization is the root of all evil." (Donald Knuth)

Es mag Fälle geben, in denen ein leicht falsches, aber schnell 
geliefertes Ergebnis einem korrekten Ergebnis (das länger braucht) 
vorzuziehen ist. In der Regel legt man aber doch erst mal Wert auf 
korrektes Funktionieren, und danach wird optimiert. Oder auch gar nicht, 
wenn es nicht nötig ist.

Gut lesbarer bzw. wartbarer Quellcode scheint vielen Programmierern 
völlig am Arsch vorbeizugehen... und wird leider Gottes auch in vielen 
Firmen so akzeptiert. :-(

von Bronco (Gast)


Lesenswert?

Mark Brandis schrieb:
> Gut lesbarer bzw. wartbarer Quellcode scheint vielen Programmierern
> völlig am Arsch vorbeizugehen... und wird leider Gottes auch in vielen
> Firmen so akzeptiert. :-(

Dein Wort in Gottes Ohr!
Da scheint es ein ungeschriebenes Gesetz zu geben, daß sich die 
Genialität eines Programmierers in seiner Unfähigkeit zur Strukturierung 
ausdrückt...

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.