Hier habe ich eine einfache Routine, welche ich mit der Option -O3
compiliere.
Mir geht es dabei in erster Linie darum, was der Compilter mit dem
Inhalt der while-Schleife anstellt.
1
unsigned char *readptr = UART0.RxReadPtr;
2
unsigned char size = (*readptr)+1;
3
if (MQReserve(size)) {
4
while(size--) {
5
MQAdd8(*readptr++);
6
}
7
UART0.RxReadPtr=readptr;
8
}
Der Disassembler Code hierzu lautet
1
+00000195: 930F PUSH R16 Push register on stack
2
+00000196: 931F PUSH R17 Push register on stack
3
+00000197: 93CF PUSH R28 Push register on stack
4
+00000198: 93DF PUSH R29 Push register on stack
5
59: unsigned char *readptr = UART0.RxReadPtr;
6
+00000199: 91C00000 LDS R28,0x0000 Load direct from data space
7
+0000019B: 91D001CD LDS R29,0x01CD Load direct from data space
8
60: unsigned char size = (*readptr)+1; // add one for message lenght byte UNLOCK_IRQ
9
+0000019D: 8108 LDD R16,Y+0 Load indirect with displacement
Ups, da fehlt noch was.
Seht Euch mal den Inhalt der whileschleife an. R17 hält die Variable
"size".
Aber anstatt die size (R17) zu decrementieren, wird ein unnützer 2 Byte
großer Addresspointer mit einem Zielwert verglichen. Dadurch werden 3
Befehler mehr benötigt, als R17 zu decrementieren.
Grüße,
Dirk
Kannst du das mal irgendwie lesbar darstellen? Die Markierung für
C-Code heißt nicht code sondern c, wenn du schon unbedingt
Disassembler posten möchtest, dann mit pre markieren. Die völlig
unnützen Kommentare des AVR-Studio-Disassemblers kannst du aber
eigentlich auch gleich entfernen: wer nicht weiß, dass BRNE ein
"branch if not equal" ist, dem wird der Kommentar auch kaum zum
Verständnis helfen...
Besser wäre es eigentlich, wenn du dein Beispiel soweit minimal
runterbrechen kannst, dass man es selbst compilieren kann. Mir
ist persönlich die Assembler-Ausgabe des Compilers deutlich lieber
zu lesen als all der Disassembler-Kram, egal ob es der vom GNU
Disassembler ist (das typische .lst-File) oder vom AVR Studio.
Viele Optimierungen von GCC gehen wie die Sprache C selbst davon aus,
dass die Wortbreite der zugrunde liegenden Maschine für "int" und für
Pointer ausreicht. AVR ist die m.W. einzige Architektur mit offizieller
GCC Portierung auf die das nicht zutrifft.
Hier zeigt sich das exemplarisch. Der Compiler geht im Rahmen der
maschinenunabhängigen Optimierung davon aus, dass Operationen mit dem
Pointer nicht teurer sind als Operationen mit dem Zähler. Dass diese
Annahme bei AVR falsch ist, das ist Pech.
Man kann auch sonst nicht vom Compiler erwarten, dass er jeden
Codewunsch optimal erfüllt. Insgesamt ist der obige Code garnicht übel
geraten. Wenn du höhere Ansprüche hast, dann ist ein Compiler, dessen
Optimierungsstrategien insbesondere der 4er Versionen eher auf
High-Performance-Maschinen abziehlen, möglicherweise die falsche Wahl.
Aber probier die Alternativen lieber erst aus, ob die wirklich besser
sind.
Was mich an dem Code übrigens eher stört, ist die fehlende
Registeroptimierung eines der beiden Pointer. Allerdings stecke ich
nicht tief genug im Registermodell vom avr-gcc drin. Sieht so aus, als
ob er nicht die Freiheit hat, das Z-Register entsprechend belegen zu
dürfen.
Ansonsten ist die Schleife unter der Annahme einer wortverarbeitenden
Maschine optimal umgesetzt.
STS 0x0473,R31 ; hier wird der pointer extra geholt
4
STS 0x0472,R30
5
CP R26,R18 ; um ihn dann als 16 Bit Zahl zu vergleichen
6
CPC R27,R19
7
BRNE PC-0x08
Mal abgesehen von der Optimierung für 16 Bit Rechner. Das dekrementieren
eines Zähler mittels "subtract" ist auch auf 16-Bit Rechnern nur ein
Befehl. Der Vergleich kann in diesem Fall entfallen, da ja das Zero-Flag
diese Aufgabe automatisch bei
Optimal im Sinn von GCC umgesetzt wäre
LD R24,X+
ST Z+,R24
CPW R26,R18 (wenn es den Befehl gäbe)
BRNE PC-0x08
was sich der Anzahl Befehle nach nicht von
LD R24,X+
ST Z+,R24
DEC R17
BRNE PC-0x08
unterscheidet.
Dass CPW einen Befehl länger und eine Takt langsamer ist, das ist der
fehlenden Wortverarbeitung zuzurechnen.
STS läd übrigens nicht, sondern speichert.
Ja, der GCC 4.3 hat ein AVR-Dilemma. Das liegt vor allem daran, dass
die GCC-Entwickler keine Möglichkeit haben, ihre neuen Entwicklungen
überhaupt daraufhin zu testen, ob diese auf einem AVR u. U. eine
nennenswerte Verschlechterung darstellen. Sie merken das dann erst
über Bugreports.
Was dich stört, ist ja offenbar:
1
.L4:
2
.LM6:
3
ld r24,X+
4
st Z+,r24
5
sts (WritePtr)+1,r31
6
sts WritePtr,r30
7
.LM7:
8
cp r26,r18
9
cpc r27,r19
10
brne .L4
GCC 4.2.2 hat das noch besser gekonnt:
1
.L5:
2
.LM7:
3
ld r24,X+
4
st Z+,r24
5
sts (WritePtr)+1,r31
6
sts WritePtr,r30
7
.LM8:
8
subi r25,1
9
brcc .L5
Du kannst ihm aber auch durch cachen von WritePtr in einer lokalen
Variablen helfen (wie du es ja für RxReadPtr auch schon machst).
Dann sieht der generierte Code so aus:
GCC 4.3.1
1
.L4:
2
.LM4:
3
ld r24,X+
4
st Z+,r24
5
.LM5:
6
cp r30,r18
7
cpc r31,r19
8
brne .L4
GCC 4.2.2:
1
.L5:
2
.LM5:
3
ld r24,Z+
4
st X+,r24
5
.LM6:
6
subi r25,1
7
brcc .L5
OK, GCC 4.2 ist immer noch 12,5 % schneller als GCC 4.3.
Ok, kann er Z also doch belegen. Hätte eigentlich gedacht, dass er das
selber hinkriegen könnte, da aufgrund verschiedener Datentypen und
-grössen kein Risiko von Aliasing besteht. Oder ist er standardmässig
auf "paranoid" eingestellt?
Jörg Wunsch wrote:
> Ja, der GCC 4.3 hat ein AVR-Dilemma. Das liegt vor allem daran, dass> die GCC-Entwickler keine Möglichkeit haben, ihre neuen Entwicklungen> überhaupt daraufhin zu testen, ob diese auf einem AVR u. U. eine> nennenswerte Verschlechterung darstellen. Sie merken das dann erst> über Bugreports.
Ob sich dieser inzwischen nicht ganz unerhebliche Missstand wohl wieder
bessert? Weiß jemand, ob daran gearbeitet wird, dass der GCC die
"kleinen" CPUs besser unterstützt in Zukunft bzw. besteht überhaupt noch
ein Interesse seitens der GCC-Entwickler die 8-Bit CPUs noch sinnvoll zu
unterstützen? So langsam ist es schon bitter was da kommt. Ich meine
gut, GCC kostet nichts und der Compiler ist leistungsfähig, aber was da
an Codegröße erzeugt wird, da kann man nur mit gutem Willen von
Optimierung sprechen.
Hat jemand das Wissen, wo es hingehen soll mit dem GCC?
Und wiese können Sie das nicht testen? Wie wird es denn mit anderen CPUs
getestet? Ich hab da leider keinen Einblick.
Es gibt ,,die GCC-Entwickler'' nicht, die sich darum kümmern könnten.
Es gibt zwei oder drei Entwickler, die sich um den AVR-GCC kümmern,
und die hatten in letzter Zeit alle Hände voll zu tun, den Xmega
einzubinden, da bei dem doch einiges anders ist als bei den vorigen
AVRs.
,,Die GCC-Entwickler'' brauchen insbesondere einen passenden Simulator,
mit dem man eine Testumgebung für den AVR-GCC so aufbauen kann, dass
sie bei Veränderungen am Compiler ermitteln können, ob der AVR-Code
dadurch schlechter wird. Mit einer solchen Umgebung hätte der AVR-Port
die Chance, aus der Kategorier ,,ferner liefen'' in der Wahrnehmung im
GCC in die zweite Stufe aufzusteigen. Dann wäre eine Verschlechterung
des AVR-Codes bei grundsätzlichen Änderungen automatisch erkennbar und
würde erst einmal zu einer Art Ausrufezeichen führen: ,,das, was jetzt
gerade passiert, ergibt für wenigstens ein "tier 2 target" eine
Regression''.
Bislang, also als "tier 3" geht sowas nur nach dem Release über
Bugreports. Dann ist die grundsätzliche Änderung aber schon drin,
und man kann ihr nur noch hinterherlaufen.
Es gibt übrigens durchaus Codebeispiele, bei denen der GCC 4.3 besseren
Code auch für den AVR liefert als seine Vorgänger, ist also nicht so,
dass neue Optimierungen prinzipiell Mist sind. Aber die treibenden
Kräfte in der GCC-Entwicklung sind (dank der Masse) natürlich auf
i386 und x86_64 konzentriert und dort in vielen Fällen wieder auf die
Dinge, die die Welt derzeit am meisten benutzt, also neben C auch noch
C++.
Jörg Wunsch wrote:
> Es gibt ,,die GCC-Entwickler'' nicht, die sich darum kümmern könnten.> Es gibt zwei oder drei Entwickler, die sich um den AVR-GCC kümmern,
Das sind dann doch "die GCC Entwickler" für den AVR. Die meinte ich
jedenfalls :-)
> und die hatten in letzter Zeit alle Hände voll zu tun, den Xmega> einzubinden, da bei dem doch einiges anders ist als bei den vorigen> AVRs.
Jetzt sind die Xmegas doch drin. D.h. es kann besser werden.
> ,,Die GCC-Entwickler'' brauchen insbesondere einen passenden Simulator,> mit dem man eine Testumgebung für den AVR-GCC so aufbauen kann, dass
Hmmm... verstanden.
> Bislang, also als "tier 3" geht sowas nur nach dem Release über> Bugreports. Dann ist die grundsätzliche Änderung aber schon drin,> und man kann ihr nur noch hinterherlaufen.
Das ist ja wenigstens ein winziger Trost, dass es grundsätzlich nicht so
bleiben soll.
> Es gibt übrigens durchaus Codebeispiele, bei denen der GCC 4.3 besseren> Code auch für den AVR liefert als seine Vorgänger, ist also nicht so,> dass neue Optimierungen prinzipiell Mist sind.
Ja das habe ich auch nicht so gemeint. Aber "unterm Strich" ist es
zumindest bei meinen Versuchen längerer Code geworden. Ich habe ein paar
von meinen Projekten mit unterschiedlichen GCC Versionen kompilert und
auchdie hier im Forum vorgeschlagenen Optimierungen probiert und "unterm
Strich" war der Code länger. Also es mag sicher Optimierungen geben, die
bei den neuen Compilern besser sind aber "unterm Strich" über ein
Projekt gesehen war es bei mir zumindest lägerer Codeder erzeugt wurde.
> Aber die treibenden> Kräfte in der GCC-Entwicklung sind (dank der Masse) natürlich auf> i386 und x86_64 konzentriert und dort in vielen Fällen wieder auf die> Dinge, die die Welt derzeit am meisten benutzt, also neben C auch noch> C++.
Da ATMEL ja auch unter anderem auf den GCC setzt, sind sie meiner
Meinung nach ein wenig "in der Pflicht", auch mit zu arbeiten. Sprich
mehr als nur 3 Leute an dem AVR-GCC arbeiten zu lassen. GCC ist zwar
Open-Source, aber auch ATMEL wird damit seinen Umsatz ankurbeln. Aber
wahrscheinlich kommen die 3 Leute schon von ATMEL :-)
Das wiederum bedeutet, bei ATMEL gibt es ein paar freie Arbeitsplatze
für Softwareentwickler ;-) Also bewerbt Euch dort :-)
Nun, ich will/wollte den GCC ganz bestimmt nicht schlecht machen (habe
auch an anderen Stellen hier im Forum den GCC immer eher gelobt). Mich
hat eher interessiert, wie es zu diesem Umstand kommen kann und warum es
nicht besser wird im Moment.
Aber diese Optimierungsgeschichten verdienen den Namen im Moment nur zum
Teil. Ich finde dieser Punkt ist imzwischen schon mehr als eine kleine
Schwäche. Nun, hoffen wir, dass das irgendwann wieder besser wird oder
eher dass es irgendwann eine passende Testumgebung zum testen geben
wird.
Ja ja, ich könnte da auch mithelfen. Es steht auch mir frei ;-)
Dirk wrote:
> Die Optimierung -O3 ist auf Geschwindigkeit ausgelegt. Wenn Du allein> die Code-Länge vergleichst, nim -Os.
Danke für den Tip, aber dass war mir bekannt. Diese Option (-Os)
benutze ich auch fast ausschließlich. Damit habe ich auch getestet.
Ich war mal neugierig, was z.B. der CodeVision-Compiler von der Schleife
macht. Der ist ja vom Preis noch eher im "Bastlerbereich" :-)
Habe mir die aktuelle Eval-Version installiert und zweimal den Compiler
laufen lassen.
1. Optimierung SPEED (1. Auszug im Listing unten)
2. Optimierung SIZE (2. Auszug im Listing unten)
Beides mal der gleiche Code. Ich war so verdutzt, dass ich es nochmal
probiert habe. Und wieder das Ergebnis. Und es sieht immer noch deutlich
schlechter aus, als der "schlechte" Code des GCC.
Es sei denn, in der Eval-Version funktioniert die Optimierung nicht.
Ich habe aber auf der Homepage wie auch in dem Manual keinen Hinweis auf
diese Limitierung gefunden. Es war nur die Limitierung der generierten
Codegröße erwähnt.
Also da kann ich ja trotz dieses Dilemmas im GCC weiter ruhig schlafen
;-)
[Edit:]
Man müßte allerdings mal ein ganzes Projekt vergleichen. So hinkt der
Vergleich ja eigentlich.
Oh je! Alles zurück. Ich habe eben erst bemerkt, dass der Codeauszug im
Posting von Jörg oben nur ein Auszug der Schleife ist. Sollte wohl doch
noch erst ein "Hallo Wach" nehmen. Also bei dem Vergleich der Codegröße
der gesamten Schleife ist der GCC doch schlechter. Bei GCC mit -Os aber
allerdings nicht. Sorry für die Verwirrung.
Hier der Auszug vom GCC über die gesamte Schleife
900ss D. wrote:
> Da ATMEL ja auch unter anderem auf den GCC setzt, sind sie meiner> Meinung nach ein wenig "in der Pflicht", auch mit zu arbeiten.
Kennst du jemanden, der GCC intern genug versteht und sich von Atmel
in Trondheim anheuern lassen würde? :-)
GCC ist leider in seinen Interna nicht gerade leicht zu verstehen,
was die natürliche Auslese etwas beschränkt.
Wichtiger wäre ja wohl fast noch ein vernünftiger Opensource-Simulator,
damit die GCC-Entwickler etwas in der Hand haben, das sie zum Testen
benutzen können. Da sieht's bald noch dünner aus als mit Leuten, die
AVR-GCC verstehen.
Jörg Wunsch wrote:
> Kennst du jemanden, der GCC intern genug versteht und sich von Atmel> in Trondheim anheuern lassen würde? :-)
Nee, kenne ich nicht und ich selber bin auch absolut kein
"Compilerbauer" und hab grad 'n neuen Job. Da ist es noch ganz gut. Aber
ich hab einen Kollegen dort (Softwareentwickler), der mag Norwegen sehr
gern und macht dort jedes Jahr Urlaub :-) Er spricht sogar die Sprache.
> Wichtiger wäre ja wohl fast noch ein vernünftiger Opensource-Simulator,> damit die GCC-Entwickler etwas in der Hand haben, das sie zum Testen> benutzen können.
Das sehe ich auch so. Dann müßten die Entwickler nicht auf Bug-Reports
warten. Es müßten automatische Tests laufen können, am besten noch mit
einer automatischen Analyse hinterher. träum....
> Kennst du jemanden, der GCC intern genug versteht und sich von Atmel> in Trondheim anheuern lassen würde? :-)
Wieso Trondheim? Ist das ein Insider, dank der Internets/Mail dürfte der
Ort unwichtig sein?
1) Die Schleifenvariable 'size' ist eliminiert. Prinzipiell mal gut,
weil das Register spart
2) WritePtr wird nicht aus der Schleife gezogen. Erstens weiß ich net,
ob GCC das schon optimieren könnte (das am SSA zu erkennen ist kein
Akt), allerdinge geht es nicht wegen möglichem Aliasing: *WritePrt.0=...
könnte WritePrt verändern.
Das .o ist 80 Byte groß. Den Code habe ich in eine eigene Funktion
gefasst.
avr-gcc 3.4.6 macht mit -Os übrigens einen Code, der um die erwarteten
10% kleiner ist: 72 Bytes. Dort wird die Schleifenvariable nicht
eliminiert.
Evtl kann man die "Optimierung" verhindern, aber das ist dann ein Schlag
mit der Keule auf's ganze Projekt/Modul.
Um zu sehen, welche Teiloptionen zB -O3 mit sich bringt, einfach mit
-fverbose-asm übersetzen und in der cc1-Ausgabe (*.s) stehen die
Optionen, für die man nacheinander Deaktivierung versuchen kann.
Johann L. wrote:
> Akt), allerdinge geht es nicht wegen möglichem Aliasing: *WritePrt.0=...> könnte WritePrt verändern.
Hatte ich zunächst ja auch vermutet, aber laut GCC Doku ist ab -O2/-Os
die Option -fstrict-aliasing aktiv, in der Aliasing nur bei gleichen
oder ähnlichen Daten (wie int<=>unsigned) berücksichtigt werden muss.
Was hier nicht zutrifft. Seitens der Sprachdefinition wäre es
ausreichend, den Pointer hinter der Schleife zu aktualisieren.
Johann L. wrote:
> 1) Die Schleifenvariable 'size' ist eliminiert. Prinzipiell mal gut,> weil das Register spart
Nö. Da nun die Enderkennung über die Endadresse erfolgt, wird alternativ
dafür ein Register verbraten.
Jörg Wunsch wrote:
> Ja, der GCC 4.3 hat ein AVR-Dilemma. Das liegt vor allem daran, dass> die GCC-Entwickler keine Möglichkeit haben, ihre neuen Entwicklungen> überhaupt daraufhin zu testen, ob diese auf einem AVR u. U. eine> nennenswerte Verschlechterung darstellen. Sie merken das dann erst> über Bugreports.
Was sind denn die Maßzahlen, an denen eine solche
Verschlechterung/Verbesserung festgemacht wird?
Die Codegrösse ist ohne Tester ersichtlich.
Zum Abschätzen des Stackbedarfs könnte das neue Tool von Benedikt (=>
Codesammlung) beitragen
Für die Ausführungszeit bräuchte man einen Simulator, wobei die Frage
ist, was similiert werden muss bzw. wie die Testsuite aussieht. Muss der
komplette Prozessor inkl. IO und Schnittstellen oder "nur" die
Grundfunktionen simuliert/emuliert werden?
Stefan B. wrote:
> Was sind denn die Maßzahlen, an denen eine solche> Verschlechterung/Verbesserung festgemacht wird?>> Die Codegrösse ist ohne Tester ersichtlich.
Das Problem ist, dass du bei GCC einfach nicht in der "tier 2"-
Kategorie landest, wenn der generierte Code nicht durch alle
GCC-Entwickler auch simuliert werden kann.
IOs müssen sicher nicht simuliert werden bzw. nur in begrenztem
Maße (ich könnte mir ein Rücklesen von Portregistern vorstellen).
Welche Anforderungen genau nötig sind, müsste ich auch nachlesen.
-ffreestanding ist zwiespältig. Die avr-libc bietet doch eine recht
nette Untermenge der Standardbibliothek, und mit -fhosted (default)
kann der Compiler Optimierungen auf Basis des internen Wissens über
die Bibliothek vornehmen. Diese Möglichkeit vergibt man sich mit
-ffreestanding. Ein Beispiel wäre strlen("Hello, world!\n"), das
wäre mit -fhosted die Konstante 14, mit -ffreestanding dagegen ein
zur Laufzeit vorzunehmender Funktionsaufruf.
A. K. wrote:
> Johann L. wrote:>>> Akt), allerdinge geht es nicht wegen möglichem Aliasing: *WritePrt.0=...>> könnte WritePrt verändern.>> Hatte ich zunächst ja auch vermutet, aber laut GCC Doku ist ab -O2/-Os> die Option -fstrict-aliasing aktiv, in der Aliasing nur bei gleichen> oder ähnlichen Daten (wie int<=>unsigned) berücksichtigt werden muss.> Was hier nicht zutrifft. Seitens der Sprachdefinition wäre es> ausreichend, den Pointer hinter der Schleife zu aktualisieren.
Ein char* kann doch alias für alles sein? Bin nicht sooo tief in der
Spez ;-)
>> 1) Die Schleifenvariable 'size' ist eliminiert. Prinzipiell mal gut,>> weil das Register spart> Nö. Da nun die Enderkennung über die Endadresse erfolgt, wird alternativ> dafür ein Register verbraten.
Stimmt, Register spart's keine weil size in BB4 noch rechts von !=
auftaucht. Allerdings werden Operationen gespart, weil der Vergleich in
BB4 ne Schleifeininvariante geworden ist. Das alles wie gesagt aus der
Sicht des Middleend.
Johann L. wrote:
> Ein char* kann doch alias für alles sein? Bin nicht sooo tief in der> Spez ;-)
Grad mal in C99 reingesehen. Hast wohl recht, für "char" gelten offenbar
Sonderregeln.
Wofor ist den -fwhole-program dar? Ich konnte in der WinAVR
Dokumentation nichts finden.
Allerdings verhindert dieser Compilerschalter, daß ich mein Programm
kompilieren kann. Plötzlich sind irgendwelche Funktionen oder Variablen
nicht mehr bekannt.
Dirk wrote:
> Wofor ist den -fwhole-program dar? Ich konnte in der WinAVR> Dokumentation nichts finden.
Hast du beim gcc-Aufruf auch alle Quellen angegeben?
Sonst landet unbenutztes Zeug gnadenlos in der Tonne, auch wenn's extern
ist.
> Hast du beim gcc-Aufruf auch alle Quellen angegeben?> Sonst landet unbenutztes Zeug gnadenlos in der Tonne, auch wenn's extern> ist.
Macht das AVR Studio das nicht automatisch?