Bei den unteschiedlichen Optimierungsstufen erhalte ich folgende
Ausführungsgeschwindigkeiten im Simulator des aktuellen Atmel Studios.
Oo = 22 Takte
O1 = 24 "
O2 = 22 "
O3 = 22 "
Os = 98 "
Warum ist das jetzt bei Os so viel mehr? Die Breakpoints sind jeweils
bei den NOPs gesetzt.
Ingo
Ingo Less schrieb:> Warum ist das jetzt bei Os so viel mehr?
einfach mal den ASM-Code anschauen.
> Die Breakpoints sind jeweils> bei den NOPs gesetzt.
da bei der Optimierung auch die Reihenfolge geändert werden kann, können
die breakpoints jetzt an anderer stelle liegen.
Ingo Less schrieb:> Warum ist das jetzt bei Os so viel mehr?
Bei -Os (Optimize space usage) versucht der Compiler wo geht Daten- und
Programmspeicher zu sparen, das kann wie du gesehen hast auch dazu
führen, dass das Programm langsamer wird.
Bei Os wird die <__mulhi3> aufgerufen währen bei den anderen Stufen dies
wohl nicht erfolgt.
O2:
1
Temp2=Temp*60;
2
58:20916000ldsr18,0x0060
3
5c:30916100ldsr19,0x0061
4
60:c901movwr24,r18
5
62:8295swapr24
6
64:9295swapr25
7
66:907fandir25,0xF0;240
8
68:9827eorr25,r24
9
6a:807fandir24,0xF0;240
10
6c:9827eorr25,r24
11
6e:821bsubr24,r18
12
70:930bsbcr25,r19
13
72:880faddr24,r24
14
74:991fadcr25,r25
15
76:880faddr24,r24
16
78:991fadcr25,r25
17
7a:90936300sts0x0063,r25
18
7e:80936200sts0x0062,r24
Os:
1
Temp2=Temp*60;
2
58:80916000ldsr24,0x0060
3
5c:90916100ldsr25,0x0061
4
60:6ce3ldir22,0x3C;60
5
62:70e0ldir23,0x00;0
6
64:06d0rcall.+12;0x72<__mulhi3>
7
66:90936300sts0x0063,r25
8
6a:80936200sts0x0062,r24
Speicherverbrauch ist bei OS 132Byte Flash und 4Byte RAM
Speicherverbrauch ist bei O2 138Byte Flash und 4Byte RAM
Somit wird hier für 6Byte weniger Flashverbrauch Faktor 4,5 bei der
Geschwindigkeit in Kauf genommen!? Krass
Daraus schliesse ich, dass wenn es nicht unbedingt auf ein paar Byte
ankommt man doch lieber O2 nutzen sollte entgegen dem Artikel hier?
AVR-GCC-Codeoptimierung
Ingo Less schrieb:> Daraus schliesse ich, dass wenn es nicht unbedingt auf ein paar Byte> ankommt man doch lieber O2 nutzen sollte entgegen dem Artikel hier?
dann kann schon wieder anders aussehen, wenn du mehrfach eine
Multiplikation im code hast.
Ingo Less schrieb:> Somit wird hier für 6Byte weniger Flashverbrauch Faktor 4,5 bei der> Geschwindigkeit in Kauf genommen!? Krass
In dem Code, den der Compiler hier sieht, beträgt der Unterschied nicht
6, sondern 20 Bytes, und das ist schon ein deutlicher Unterschied. Der
Platzbedarf für die Bibliotheksroutine dürfte schon bei zwei
Multiplikationen dieser Art mehr als kompensiert werden.
Ingo Less schrieb:> Somit wird hier für 6Byte weniger Flashverbrauch Faktor 4,5 bei der> Geschwindigkeit in Kauf genommen!?
Wenn du dem Compiler mit -Os sagst dass er es möglichst klein machen
soll, dann tut er das auch. Wenn dir das Ergebnis nicht gefällt, dann
kannst du auch eine andere Optimierung wählen.
Ingo Less schrieb:> Somit wird hier für 6Byte weniger Flashverbrauch Faktor 4,5 bei der> Geschwindigkeit in Kauf genommen!? Krass
Aber nur dann, wenn dein Programm ausschliesslich aus 16*16 Bit
Multiplikationen besteht.
Leicht off topic .....
.... aber ich finde es sehr interessant wieviele Comilerbauer es
hier im Forum gibt:
Ingo Less schrieb:> Frage an die Compilerbauer
Da muss es ja vor (erschaffenen) Compilern nur so wimmeln (wenn
jeder so im Background seine(n) Compiler baut), bzw. hier gibt es
soviel Optimierungspotential dass ja ein ganz toller optimierter
GCC für die AVRs dabei herauskommen muss..... ( ;-) )
Eberhard F. schrieb:> .... aber ich finde es sehr interessant wieviele Comilerbauer es> hier im Forum gibt:
Ist nicht die erste Frage im Forum, die falsch gestellt war. Und wird
auch nicht die letzte bleiben. Soll sie deshalb verhungern, weil sich
niemand zu antworten traut?
PS: Ex-Compilerbauer, falls dich das beruhigt. ;-)
Ingo Less schrieb:> Somit wird hier für 6Byte weniger Flashverbrauch Faktor 4,5 bei der> Geschwindigkeit in Kauf genommen!? Krass
Ich würde das Programm nicht mit -Os übersetzen, denn welcher AVR hat
schon weniger als 138 Bytes Flash? Wie, das ist gar nicht das
"eigentliche" Programm?
Wie so oft wird hier der Fehler gemacht, von einem Trivialprogramm auf
den Allgemeinfall zu schließen.
Bei größere Programmen ist die Wahrscheinlichkeit nicht so gering, daß
die Multiplikationsroutine noch öfter gebraucht wird. Sie ist dann nur
einmal da mit -Os und muss nur aufgerufen werden. Bei -O2 nutzt der
Compiler dagegen hier eine optimierte Version, da mit einer Konstanten
multipliziert wird. Die braucht zusätzlichen Code. Da in diesem
trivialen Programm sonst keinerlei Multiplikation vorkommt, kann die
Funktion __mulhi3 dafür weggelassen werden. Erst dadurch fällt der
Unterschied beim Speicherverbrauch so gering aus.
Nur der Vollständigkeit halber:
Wenn man das Konsturierte Beispiel etwas realitätsnäher gestaltet, und
vor allem den Optimizer seine Arbeit machen lässt, dann kommt ein viel
besseres Resultat heraus.
(Also: Kein Volatile wo's nicht nötig ist. Keine "asm volatile", die
"zeilen-übergreifende" optimierunen verhindert.)
Also:
1
#include<avr/io.h>
2
3
uint16_tTemp;
4
volatileuint16_tTemp2;
5
6
intmain(void)
7
{
8
Temp=0;
9
while(1)
10
{
11
Temp++;
12
Temp2=Temp*60;
13
}
14
}
wird zu:
1
.L2:
2
adiw r24,60
3
sts Temp2+1,r25
4
sts Temp2,r24
5
rjmp .L2
Moral aus der Geschichte: Dem Kompiler keine Knüppel zwischen die Beine
werfen, dann klappts auch mit der Optimierung.
OK,
der eigentlich sinn hintet diesem Banalprogramm war eigentlich,
herauszufinden ob der Optimizer aus *60 auf einem AVR ohne
Multiplikationseinheit einen Shift macht.
Nämlich führt Temp2 = Temp * 60 zum selben Ergebnis wie Temp2 = Temp<<6
- Temp<<2. Die Frage war ob er das selber herausfinden würde.
Offensichtlich jedoch nicht.
EDIT: Gut, damit hätte ich auch früher rausrücken müssen...
Ingo Less schrieb:> Nämlich führt Temp2 = Temp * 60 zum selben Ergebnis wie Temp2 = Temp<<6> - Temp<<2. Die Frage war ob er das selber herausfinden würde.> Offensichtlich jedoch nicht.
Er macht das sogar noch besser: Nämlich mit
1
uint16_tTemp3=Temp;
2
Temp2=((Temp3<<4)-Temp3)<<2;
Das liegt dem barrel-shifter-losen AVR sehr viel besser. Beachte
außerdem, auf welch geniale Weise der Shift um 4 realisiert ist.
Edit:
Habe noch einen Klammerfehler und eine kleine Unstimmigkeit korrigiert.
Peter II schrieb:> dann mach doch einfach> Temp2 = (Temp * 64) - (Temp * 4);
Warum? Um den Compiler zu schlechterem Code zu zwingen?
Wie Yalu geschrieben hat:
der gcc macht das von selbst sogar noch besser, als sich corrtexx das
für Hand-Optimierten Code ausgedacht hat.
Lasst den Compiler doch einfach seine Arbeit machen. Der kann das schon.
Hand-Optimieren kann man später immer noch, wenn's denn wirklich nötig
ist.
In meinen Projekten ist das Resultat bei Os und O2 sowohl was
Performance angeht als auch Speicherbedarf nur wenig Unterschiedlich.
Ich denke, du hast da durch den abgespeckten Minimal-Code einen
Extremfall erwischt.
Os würde ich nur im Notfall einsetzen, wenn es wirklich sein muss.
Normal nehme ich O2. Einmal musste ich bei einer bestimmten Version des
Atmel Compilers auf O1 gehen, ich vermute da einen Bug, weil eben nur
diese eine Version betroffen war.
O0 nehme ich, wenn ich debuggen möchte.
Bisher habe ich ich immer OS genutzt, wie es empfohlen wird. hatte
bisher auch nie das Bedürfnis etwas von Hand zu optimieren. Wie gesagt,
hier war nur der Versuch was der Optimizer aus dem *60 macht. Aber er
macht es ja scheinbar noch besser wie erwartet, auch wenn ich den
Assemblercode mangels Assemblerkenntnissen nicht so recht nachvollziehen
kann.
Meiner Erfahrung nach spart man bei Programmen aus der realen Welt mit
-Os prozentual deutlich mehr Flash-Bytes als mit -O2 Zyklen. Da ich
zudem häufiger Flash-Kapazitäts- als Laufzeitprobleme habe, verwende ich
normalerweise -Os.
Und was soll da anders sein als mit * 60 ???
*60 in shifts etc. zu expandieren ist nur günstig, wenn es keine MUL
Befehle gibt.
MUL mit 60 ist dann besser als Rumgeschiebe.
Das weiß die Kristallkugel.
Es ist weder Die Compilerversion genannt noch für welches Device
übersetzt wurde.
Und ja, avr-gcc macht nicht immer den besten denkbaren Code, und das
gilt auch für den Code, den er für Multiplikationen erzeugt — auch mit
avr-gcc 4.9 oder 5.
SF6 schrieb:> Denkst du nicht, der Compiler hätte den MUL Befehl eingebaut wenn es> einen gäbe? Beitrag "Re: Frage an die Compilerbauer"
Der Code dort ist vermutlich für einen attiny übersetzt worden. Wenn man
stattdessen einen atmega auswählt, wird auch MUL verwendet, und zwar
sowohl bei -Os als auch bei -O2.