Die Funktion wird mit 1kHz aufgerufen.
Dabei passiert es, dass im Falle Zeit >= 6000 im case 2 das Digit nur ein sehr kurzen Moment angeschaltet wird (210µs statt wie bei den Anderen 490µs), da offensichtlich für etwas vorauslaufendes sehr viel Zeit benötigt wird. Ändert man die Optimierung vo -Os auf -O1 oder -O2, ist die Einschaltzeit >950µs, bei allen Digits gleich.
Tauscht man bei -Os die Zeile 38 gegen 24 wandert die lange Bearbeitungszeit zu dem anderen Digit. Offensichtlich stört sich der Compiler erheblich an
1
PORTD=Ziffer[Minuten%10];
Mit -Os:
Program Memory Usage : 1784 bytes 5,4 % Full
Data Memory Usage : 49 bytes 2,4 % Full
1
PORTD=Ziffer[Minuten%10];
2
12a:2ae0ldir18,0x0A;10
3
12c:622fmovr22,r18
4
12e:0e943603call0x66c;0x66c<__udivmodqi4>
5
132:e92fmovr30,r25
6
134:f0e0ldir31,0x00;0
7
136:ef5fsubir30,0xFF;255
8
138:fe4fsbcir31,0xFE;254
9
13a:8081ldr24,Z
10
13c:8bb9out0x0b,r24;11
Mit -O1:
Program Memory Usage : 1992 bytes 6,1 % Full
Data Memory Usage : 41 bytes 2,0 % Full
1
PORTD=Ziffer[Minuten%10];
2
2aa:8decldir24,0xCD;205
3
2ac:e89fmulr30,r24
4
2ae:812dmovr24,r1
5
2b0:1124eorr1,r1
6
2b2:8695lsrr24
7
2b4:8695lsrr24
8
2b6:8695lsrr24
9
2b8:982fmovr25,r24
10
2ba:990faddr25,r25
11
2bc:990faddr25,r25
12
2be:890faddr24,r25
13
2c0:880faddr24,r24
14
2c2:9e2fmovr25,r30
15
2c4:981bsubr25,r24
16
2c6:892fmovr24,r25
17
2c8:e92fmovr30,r25
18
2ca:f0e0ldir31,0x00;0
19
2cc:ef5fsubir30,0xFF;255
20
2ce:fe4fsbcir31,0xFE;254
21
2d0:9081ldr25,Z
22
2d2:9bb9out0x0b,r25;11
Der hier speichersparende Aufruf von __udivmodqi4> führt offensichrlich zu einer erheblichen Verlangsamung gegenüber der direkten Berechnung ohne Funktionsaufruf. Das Verhalten zeigte sich mit dem AVR GCC 7.4. und 9.2 identisch. Dieses Beispiel soll zeigen, dass die Optimierung durchaus nennenswert dazwischen grätschen kann. Evtl. hilft das ja irgendwem irgendwann einmal weiter ;)
P.S.:
Die Abfrage
1
if(Zeit>=6000){// mm:ss
ist notwendig, weil es noch andere Behandlungen von Zeit gibt, bei der die Problematik jedoch nicht auftaucht, nur falls jemand fragt...
Da kein Mensch 1000 Werte je Sekunde ablesen kann, nimmt man einfach die ganzen Berechnungen aus dem kritischen Teil raus und führt sie nur einmalig je neuem Wert aus. Ergonomisch ist eine Anzeigerate von 2..5Hz.
Besonders die Divisionen sind teuer, da der AVR keinen Befehl dafür hat.
Dieses Beispiel soll zeigen, dass die Optimierung
durchaus nennenswert dazwischen grätschen kann.
Was heisst dazwischengraetschen? Du sagst dem Compiler, er soll auf Groesse optimieren und dann moserst du rum, dass er nicht auf Geschwindigkeit optimiert?
Da kann der Compiler nix dazu, finde ich. Der macht genau das, was du ihm sagst. Und nicht das, was du vielleicht meinst.
Der hier speichersparende Aufruf von __udivmodqi4> führt offensichrlich
zu einer erheblichen Verlangsamung gegenüber der direkten Berechnung
ohne Funktionsaufruf.
Da trägst du Eulen nach Athen. Ist eigentlich bekannt.
Funktionsaufrufe kosten Zeit da Register gesichert und restauriert
werden müssen, und die Parameter Übergabe braucht auch ein paar
Takte.
Das alles bei einem 8-Bitter ist eben ganz schön aufwendig.
Da kein Mensch 1000 Werte je Sekunde ablesen kann, nimmt man einfach die
ganzen Berechnungen aus dem kritischen Teil raus und führt sie nur
einmalig je neuem Wert aus.
Genau. Allein dass hier:
if ( Zeit >= 6000 ){ // mm:ss
Minuten = Zeit / 6000;
Sekunden = (Zeit/100) % 60;
wird 1000 mal pro Sekunde gemacht, obwohl sich die Werte nur jede Sekunde ändern - für die Minuten sogar nur alle 60 Sekunden.
Die Division und Modulo-Rechnung mit 10 macht das Ganze nicht beser:
Hier empfiehlt sich, einfach folgende Variablen zu nutzen:
Minuten_1 und Minuten_10 für Einer- und Zehnerstelle
Sekunden_1 und Sekunden_10 für Einer- und Zehnerstelle
Diese 4 Variablen inkrementiert man zu geeigneter Zeit (bzw. setzt sie zurück), dann entfallen sowohl die Divisionen mit 6000 und 10 und die Modulo-Operationen mit 60 und 10 ebenso.
Der hier speichersparende Aufruf von __udivmodqi4> führt offensichrlich
zu einer erheblichen Verlangsamung gegenüber der direkten Berechnung
ohne Funktionsaufruf.
Du vergleichst Äpfel mit Birnen. Einmal wird eine Divisionsroutine /x aufgerufen. Das andere Mal wird ein optimierter Code für /10 eingefügt.
welchen Einfluss hat der Optimizer bei ggf. Ungünstiger Programmierung
Hier in dem Beispiel, hat er signifikanten Einfluss. Dass man das Problem hätte umschiffen können, ganz klar. Aber bei einer Stoppuhr, die im 10ms Bereich auflöst und weiter nichts macht, habe ich die wenigen Zeilen direkt in die ISR gepackt. Hätte, wenn und aber…
welchen Einfluss hat der Optimizer bei ggf. Ungünstiger Programmierung
Wie Dein Nickname schon sagt, scheinst Du so gar keine Ahnung von den kleinen µC zu haben. Operationen wie / oder %, die nicht durch ein shift und/oder Maskierung realisiert werden können, müssen dabei immer(!) ein Alarmzeichen sein.
Nun, dazu kommt die Erwartungshaltung, die man an den Optimizer hat. Wenn Du eine Optimierung auf Geschwindigkeit erwartest, aber eine Optimierung auf Codegröße veranlasst, dann wird Dich das Ergebnis halt ... überraschen.
Hast Du denn Deine Untersuchung mit einer anderen Einstellung für den Optimizer wiederholt?
Es heißt zwar oft, dass man keine Mikrooptimierungen betreiben und das lieber dem Compiler überlassen soll, aber das bedeutet nicht, dass man seinen Code schreiben kann wie man will, und der Optimizer richtet das dann schon irgendwie. Gerade auf so kleinen µCs muss man da etwas umsichtiger agieren.
Hast Du denn Deine Untersuchung mit einer anderen Einstellung für den
Optimizer wiederholt?
Ja, jede andere Einstellung, sogar -Oo, funktioniert besser als -Os
Das erstaunt mich, denn so eine Einstellung gibt es nicht.
Was verstehst du unter "besser als -Os"? Wie schon mehrfach gesagt wurde: Das s in -Os steht für size, also wird darauf optimiert, den Code kleiner zu machen, auch wenn er dadurch langsamer werden sollte. Exakt das passiert bei dir.
Das erstaunt mich, denn so eine Einstellung gibt es nicht.
-Oo => ohne Optimierung
Was verstehst du unter "besser als -Os"?
Das Programm ist deutlich schneller
Wie schon mehrfach gesagt
wurde: Das s in -Os steht für size, also wird darauf optimiert, den
Code kleiner zu machen, auch wenn er dadurch langsamer werden sollte.
Exakt das passiert bei dir.
Aber wozu dann hier das Ganze?
Ich mach' doch auch nicht einen Thread in einem Fastfoodforum auf, weil ich, immer wenn ich am Imbiss Pommes bestelle, dann keine Currywurst bekomme, sondern - oh Wunder - Pommes.
Das erstaunt mich, denn so eine Einstellung gibt es nicht.
-Oo => ohne Optimierung
Nein, -Oo => Fehlermeldung
Ohne Optimierung ist -O0 (Null, kein o).
Was verstehst du unter "besser als -Os"?
Das Programm ist deutlich schneller
Da es nicht die primäre Aufgabe von -Os ist, das Programm schneller zu machen, würde "besser als -Os" nicht "schneller", sondern "kleiner" bedeuten.
Wie schon mehrfach gesagt
wurde: Das s in -Os steht für size, also wird darauf optimiert, den
Code kleiner zu machen, auch wenn er dadurch langsamer werden sollte.
Exakt das passiert bei dir.
Ja, dass habe ich nie abgestritten
Warum zeigst du dich dann so überrascht darüber, dass -Os das tut, was es soll?