PDA

Archiv verlassen und diese Seite im Standarddesign anzeigen : Optimierungsfrage



thewulf00
06.10.2008, 13:37
Hallo,

ich sehe mir in letzter Zeit gern mal die assemblierten Varianten des C-Codes an und da ist mir eines aufgefallen. Vielleicht weiß jemand, wie man das umgehen kann?

Ich habe folgendes: Einen globlen (also statischen) Zähler in uint8_t, der mir die Anzahl der bisherigen Interrupts zählt. Ab einer bestimmten Interruptzahl möchte ich Code ausführen. Also kommt logischerweise sowas hier zum Einsatz:

if (++interrrupt_num_10ms == IRQS_PER_10MS) {...}

Wenn ich das simuliere und analysiere (also den ASM-Code anschaue), kommt dabei viel Code zum Vorschein. Nun habe ich die erste Optimierung über google gefunden: Ich mache diesen globalen Interrupt-Zähler zu einem Register mittels

register uint8_t interrupt_num_10ms asm("r5");

Wenn ich das jetzt wieder vergleiche, macht er folgendes:

72: if (++interrupt_num_1ms == IRQS_PER_10MS)
+0000005C: 2D85 MOV R24,R5 Copy register
+0000005D: 3081 CPI R24,0x01 Compare with immediate
+0000005E: F4C9 BRNE PC+0x1A Branch if not equal

D.h. er kopiert erst R5 in sein temporäres Register und vergleicht es dann. Kann man ihm irgendwie sagen, dass er gleich R5 vergleichen soll/kann?

Grüße,
Wulfi

sternst
06.10.2008, 15:21
Ich mache diesen globalen Interrupt-Zähler zu einem Register
Ich würde davon generell abraten. Der Compiler weiß im Allgemeinen schon ganz gut, was er macht. Du bekommst in 99% der Fälle einen insgesamt besseren Code, wenn du dem Compiler völlig freie Hand bei der Registerwahl lässt.


Kann man ihm irgendwie sagen, dass er gleich R5 vergleichen soll/kann?

Geht nicht, weil der Befehl CPI nur mit den Registern 16 bis 31 funktioniert. Wie gesagt, der Compiler weiß in der Regel, was er tut.


Wenn ich das simuliere und analysiere (also den ASM-Code anschaue), kommt dabei viel Code zum Vorschein
Es wäre interessant, diesen ASM-Code mal zu sehen, dann könnte dir sicher besser geholfen werden.

sternst
06.10.2008, 15:33
Nachtrag:
Bei mir sieht der generierte Code so aus:

if (++count == 10)
da: 80 91 00 01 lds r24, 0x0100
de: 8f 5f subi r24, 0xFF ; 255
e0: 80 93 00 01 sts 0x0100, r24
e4: 8a 30 cpi r24, 0x0A ; 10
e6: 09 f4 brne .+2

Also optimal. Wenn er bei dir deutlich anders aussieht, dann hast du wahrscheinlich die Compiler-Optimierung nicht aktiviert.

thewulf00
06.10.2008, 15:37
Nein, Du hast schon recht, es ist nicht ratsam, aber mit ein paar Optimierungen war der Hex-Code nur noch halb so groß... (Das mache ich nur jetzt mal zum Kennenlernen)

Bei größeren Programmen muss man das mit Vorsicht genießen.
Aber angenommen, ich finde ein Register zwischen 16 und 31, das der Compiler nicht nutzt, und ich weise es der Variable zu, dann würde er den Vergleich in einem Zuge machen?

sternst
06.10.2008, 15:42
aber mit ein paar Optimierungen war der Hex-Code nur noch halb so groß
Dann hast du mit ziemlicher Sicherheit die compilerinterne Optimierung nicht aktiviert. Nicht mal mit handgeschriebenem Assembler könnte man bei dem Code, den der Compiler dann generiert, 50% rausholen.

thewulf00
06.10.2008, 15:48
Doch, das hatte ich.
Es war nur ein kleiner Code von ca. 700 Bytes.
Nachdem ich die globalen Variablen zu Registern verändert hatte und hier und da noch ein paar Optimierungen vornahm, waren es noch 320 Byte.

Die Optimierung war in beiden Fällen -Os. Ich habe auch die anderen Optimierungen ausprobiert, -Os gab in diesem Fall das beste Ergebnis.

markusj
06.10.2008, 15:49
Nein, Du hast schon recht, es ist nicht ratsam, aber mit ein paar Optimierungen war der Hex-Code nur noch halb so groß... (Das mache ich nur jetzt mal zum Kennenlernen)

Bei größeren Programmen muss man das mit Vorsicht genießen.
Aber angenommen, ich finde ein Register zwischen 16 und 31, das der Compiler nicht nutzt, und ich weise es der Variable zu, dann würde er den Vergleich in einem Zuge machen?
Du bekommst noch andere Schwierigkeiten. Afaik "rettet" der Compiler beim Eintritt in eine ISR immer ALLE (auch die in der ISR ungenutzten) Register und stellt sie beim verlassen wieder her.
Dementsprechend macht es meiner Meinung nach nicht viel Sinn, eine globale Variable in einem Register abzulegen, außer du schreibst Pro- und Epilog für jede zugreifende ISR selbst.

mfG
Markus

thewulf00
06.10.2008, 15:57
Nein, er speichert nur die genutzten und die temporären (R0, R1).
Siehe hier:

@00000054: __vector_7
76: {
+00000054: 921F PUSH R1 Push register on stack
+00000055: 920F PUSH R0 Push register on stack
+00000056: B60F IN R0,0x3F In from I/O location
+00000057: 920F PUSH R0 Push register on stack
+00000058: 2411 CLR R1 Clear Register
+00000059: 938F PUSH R24 Push register on stack
81: if (++interrupt_num_10ms == IRQS_PER_10MS)
+0000005A: 91800063 LDS R24,0x0063 Load direct from data space
+0000005C: 5F8F SUBI R24,0xFF Subtract immediate
+0000005D: 93800063 STS 0x0063,R24 Store direct to data space
+0000005F: 3184 CPI R24,0x14 Compare with immediate
+00000060: F559 BRNE PC+0x2C Branch if not equal

sternst
06.10.2008, 15:58
Afaik "rettet" der Compiler beim Eintritt in eine ISR immer ALLE (auch die in der ISR ungenutzten) Register und stellt sie beim verlassen wieder her.
Nein, nur die, die er benutzt. Etwas anders sieht es aus, wenn du in der ISR andere Funktionen aufrufst, dann muss er auch die Call-Clobbered-Register sichern.

sternst
06.10.2008, 16:05
@thewulf00:

Ist das obige etwa das, was du mit "kommt dabei viel Code zum Vorschein" meintest?
Also wenn dein Timing so knapp ist, dass es auf 4 Takte mehr oder weniger ankommt, dann schreibe besser die ganze ISR selber in Assembler. Das bringt dir mehr, als mit "register" rumzuspielen.

thewulf00
06.10.2008, 16:26
@Stefan:
Nicht in den falschen Hals bekommen. Die Sicherheitsaspekte und der tatsächliche Gewinn sind mir sehr wohl bewusst.


(Das mache ich nur jetzt mal zum Kennenlernen)

Gock
06.10.2008, 17:14
D.h. er kopiert erst R5 in sein temporäres Register und vergleicht es dann. Kann man ihm irgendwie sagen, dass er gleich R5 vergleichen soll/kann?
Wulfi
Wie bereits erwähnt verlangt CPI ein höheres Register.
Prinzipiell ist die Idee gut, ein spezielles Register zuzuweisen, solange der Code übersichtlich bleibt. Allerdings habe ich keine Erfahrung, wie der Compiler dann reagiert, wenn es komplexer wird. Er produziert auch in Standardsituationen mal ineffizienten Code.
Was Du aber eventuell machen könntest (ich kenne den Rest des Codes nicht...), ist, die "Konstante" nicht als solche zu speichern, sondern in einem unteren Register zu halten. Dann könnte der Compiler nämlich den CP-Befehl verwenden und sich die Rumkopiererei sparen, weil er jetzt keine Imidiate benutzen muss. Außerdem bräuchte er dann kein zusätzliches Register zu retten, weil R5 mit der Konstante ja konstant bleibt.
Das ist die Theorie, würde mich mal interessieren, ob der Compiler die auch kennt...Ich bezweifele es geringfügig.
Gruß