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.
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 :)
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
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).
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?
@ 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
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.
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
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
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.
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.
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()!
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.
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)
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?
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.
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.
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.
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.
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).
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 :-)
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. :-(
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
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.