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...
Ingo L. schrieb:> Die Funktion wird mit 1kHz aufgerufen.
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.
Der 328 kann 20MHz.
Statt die Zeit in einem Zähler zu verwalten,nimm gleich die zerbrochene
Darstellung in sec, min,Std,... . Dann vermeidest du divmod .
Moin,
Ingo L. schrieb:> 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.
Gruss
WK
Ingo L. schrieb:> 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.
Ingo L. schrieb:> Dieses Beispiel soll zeigen, dass die Optimierung> durchaus nennenswert dazwischen grätschen kann.
Die mit Abstand beste Optimierung macht immer noch Brain 1.0.
Einfach mal überlegen, welche Abläufe man unnötig oft ausführt.
Ingo L. schrieb:> if ( Zeit >= 6000 ){ // mm:ss> // Update Digits> Minuten = Zeit / 6000;> Sekunden = (Zeit/100) % 60;
wenn es so zeitkritsch wird würde ich zu oft auf Teilen und Modulo
verzichten
Ingo L. schrieb:> Die Funktion wird mit 1kHz aufgerufen.
1000x pro Sekunde ist Unfug, 5x bis 10x zu STRING wäre mehr als
ausreichend und STRING Vergleiche "HH:MM:SS"geht viel schneller
Peter D. schrieb:> 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:
> PORTD = Ziffer[Sekunden/10];> if ((Sekunden % 10 ) % 2 ) PORTD |= SEG_DP;
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.
Ingo L. schrieb:> 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.
Es ging nicht darum:
- wie gehts besser
- wie macht man es richtig
sondern:
- 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…
Ingo L. schrieb:> Es ging nicht darum:> - wie gehts besser> - wie macht man es richtig> sondern:> - 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?
Harald K. schrieb:> Hast Du denn Deine Untersuchung mit einer anderen Einstellung für den> Optimizer wiederholt?
Ja, jede andere Einstellung, sogar -Oo, funktioniert besser als -Os
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.
Ingo L. schrieb:> Harald K. schrieb:>> 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.
Rolf M. schrieb:> 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.
Ja, dass habe ich nie abgestritten
Moin,
Ingo L. schrieb:> Ja, dass habe ich nie abgestritten
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.
Gruss
WK
Ingo L. schrieb:> Rolf M. schrieb:>> 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?
Ingo L. schrieb:>> Was verstehst du unter "besser als -Os"?> Das Programm ist deutlich schneller
"Besser" oder "schlechter" kennt der Compiler nicht.
Wenn du "schneller" möchtest, musst du ihm das sagen.