Frage an die Compilerbauer

#4063250
Lesenswert?

Hallo,

ich habe folgenden Testcode:
1
#include <avr/io.h>
2

3
volatile uint16_t Temp,Temp2;
4

5
int main(void)
6
{
7
    while(1)
8
    {
9
    Temp++;
10
    asm volatile ("NOP");
11
    Temp2 = Temp * 60;
12
    asm volatile ("NOP"); 
13
    }
14
}

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
#4063269
Lesenswert?

Bei Os wird die <__mulhi3> aufgerufen währen bei den anderen Stufen dies 
wohl nicht erfolgt.

O2:
1
    Temp2 = Temp * 60;
2
  58:  20 91 60 00   lds  r18, 0x0060
3
  5c:  30 91 61 00   lds  r19, 0x0061
4
  60:  c9 01         movw  r24, r18
5
  62:  82 95         swap  r24
6
  64:  92 95         swap  r25
7
  66:  90 7f         andi  r25, 0xF0  ; 240
8
  68:  98 27         eor  r25, r24
9
  6a:  80 7f         andi  r24, 0xF0  ; 240
10
  6c:  98 27         eor  r25, r24
11
  6e:  82 1b         sub  r24, r18
12
  70:  93 0b         sbc  r25, r19
13
  72:  88 0f         add  r24, r24
14
  74:  99 1f         adc  r25, r25
15
  76:  88 0f         add  r24, r24
16
  78:  99 1f         adc  r25, r25
17
  7a:  90 93 63 00   sts  0x0063, r25
18
  7e:  80 93 62 00   sts  0x0062, r24

Os:
1
  Temp2 = Temp * 60;
2
  58:  80 91 60 00   lds  r24, 0x0060
3
  5c:  90 91 61 00   lds  r25, 0x0061
4
  60:  6c e3         ldi  r22, 0x3C  ; 60
5
  62:  70 e0         ldi  r23, 0x00  ; 0
6
  64:  06 d0         rcall  .+12       ; 0x72 <__mulhi3>
7
  66:  90 93 63 00   sts  0x0063, r25
8
  6a:  80 93 62 00   sts  0x0062, 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
Moderator #4063285
Lesenswert?

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.
Gast #4063288
Lesenswert?

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.
#4063377
Lesenswert?

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.....   (   ;-)   )
Gast #4063384
Lesenswert?

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.
Gast #4063393
Lesenswert?

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_t Temp;
4
volatile uint16_t Temp2;
5

6
int main(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.
#4063437
Lesenswert?

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...
Moderator #4063476
Lesenswert?

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_t Temp3 = 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.
Gast #4063487
Lesenswert?

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.
Gast #4063499
Lesenswert?

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.
#4063503
Lesenswert?

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.
#4066092
Lesenswert?

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.
1
        lds r18,Temp
2
        lds r19,Temp+1
3
        mul r20,r18
4
        movw r24,r0
5
        mul r20,r19
6
        add r25,r0
7
        clr __zero_reg__
8
        sts Temp2+1,r25
9
        sts Temp2,r24

Antwort schreiben

Bitte melde dich an, um einen Beitrag zu schreiben.

oder

Mit Google-Account einloggen

Die Registrierung ist kostenlos und dauert nur eine Minute.

Jetzt registrieren