Forum: Mikrocontroller und Digitale Elektronik Gleiche Funktion. 23 cycles anstelle von 6?


von Simon M. (simon2)


Lesenswert?

Frage:

Das ganze läuft auf einem 16Bit, PIC24F

Meiner Ansicht nach würde der zweite code deutlich schneller ablaufen
als der erste.

Jedoch ist genau das gegenteil der Fall.
Warum?

1
// 6 cycles
2
typedef union _ade_status1_t
3
{
4
    DWORD Val;
5
    struct __PACKED
6
    {
7
//...
8
            UINT8 SEQERR:1;
9
//...
10
    } bits;
11
} status1_t;
12
13
    if (status.bits.SEQERR) {
14
        Nop();
15
    }

1
// 23 Cycles
2
    #define STATUS1_SEQERR   0x00080000
3
4
    if (x & STATUS1_SEQERR) {
5
        Nop();
6
    }


ASM1:
1
0x2406: MOV [W14+2], W4
2
0x2408: AND W4, #0x8, W4
3
0x240A: SUB W4, #0x0, [W15]
4
0x240C: BRA Z, 0x2410

ASM2:
1
0x2412: MOV [W14+4], W6
2
0x2414: MOV [W14+6], W7
3
0x2416: MOV #0x0, W4
4
0x2418: MOV #0x8, W5
5
0x241A: MOV W6, W0
6
0x241C: MOV W7, W6
7
0x241E: MOV W4, W7
8
0x2420: MOV W5, W4
9
0x2422: AND W0, W7, W0
10
0x2424: AND W6, W4, W4
11
0x2426: MOV #0x0, W5
12
0x2428: SL W4, #0, W5
13
0x242A: MOV #0x0, W4
14
0x242C: MOV.D W4, W6
15
0x242E: MUL.UU W0, #1, W4
16
0x2430: IOR W6, W4, W6
17
0x2432: IOR W7, W5, W7
18
0x2434: SUB W6, #0x0, [W15]
19
0x2436: SUBB W7, #0x0, [W15]
20
0x2438: BRA Z, 0x243C
21
0x243A: NOP
22
0x243C: NOP

: Bearbeitet durch User
von Rainer U. (r-u)


Lesenswert?

Simon M. schrieb:
> Warum?

Weil es der Compiler so übersetzt hat, dass er sinnloserweise mit 16bit 
statt 8 bit Variablen arbeitet?

von Simon M. (simon2)


Lesenswert?

Naja nur das ist schon nen gewaltiger unterschied.

Ich will mich ja nicht über die paar instruction cycles aufregen, nur 
wenn der das überall so macht, mir ist das ja nur durch zufall 
aufgefallen.

von M. K. (sylaina)


Lesenswert?

Simon M. schrieb:
> Meiner Ansicht nach würde der zweite code deutlich schneller ablaufen
> als der erste.

Warum sollte es das? Beim 2. If machst du eine &-Verknüpfung, beim 1. 
übergibst du nur ein Bit. Ist doch klar, dass der 1. Code viel schneller 
sein muss.

von Peter D. (peda)


Lesenswert?

Und nun sollen wir alle mal raten, welchen Typ x hat oder wie?

von Markus F. (mfro)


Lesenswert?

Wenn Nop() das macht, was ich denke, daß es macht: warum muß das 
besonders schnell machen?

von Klaus (Gast)


Lesenswert?

Rainer U. schrieb:
> Weil es der Compiler so übersetzt hat, dass er sinnloserweise mit 16bit
> statt 8 bit Variablen arbeitet?

Die PIC24 sind 16-Bitter

Simon M. schrieb:
> #define STATUS1_SEQERR   0x00080000

Ich sehe da 32 Bit

MfG Klaus

von Peter II (Gast)


Lesenswert?

Klaus schrieb:
> Die PIC24 sind 16-Bitter
>
> Simon M. schrieb:
>> #define STATUS1_SEQERR   0x00080000
>
> Ich sehe da 32 Bit

wo?

auch eine 000000000000000000000008 sind nur 8 bit.

ein define ist überhaupt keine Variable und hat sogar 0 bits.

von Random .. (thorstendb) Benutzerseite


Lesenswert?

Sind struct/union Maps generell für ne gute Idee, weil man dann dem 
Compiler ermöglicht, aufzudrehen?

von ui (Gast)


Lesenswert?

Peter II schrieb:
> wo?
>
> auch eine 000000000000000000000008 sind nur 8 bit.
>
> ein define ist überhaupt keine Variable und hat sogar 0 bits.

absolut richtig. Wenn dann müsstest du schon explizit casten, wenn du 
sicher 32bit haben willst

von Klaus (Gast)


Lesenswert?

Peter II schrieb:
> wo?

da:

Simon M. schrieb:
> 0x2416: MOV #0x0, W4
> 0x2418: MOV #0x8, W5

MfG Klaus

von Axel S. (a-za-z0-9)


Lesenswert?

Simon M. schrieb:
> Meiner Ansicht nach würde der zweite code deutlich schneller ablaufen
> als der erste.

Mit dieser Ansicht bist du deutlich in der Minderheit.

> Jedoch ist genau das gegenteil der Fall.
> Warum?

Weil der Compiler aus der & Verknüpfung von Variable und Bitmaske nicht 
abliest, daß er in Wirklichkeit nur ein Bit testen muß. Ein exzellent 
optimierender Compiler könnte die konstante Bitmaske natürlich daraufhin 
abklopfen. Aber der real existierende Compiler macht es offensichtlich 
nicht, sondern implementiert das

1
if (variable & maske)

auf die kanonische Weise, indem er alle Bits der Variable mit allen Bits 
der Maske ver-UND-et und dann testet, ob irgend ein Bit gesetzt ist.

Auf einer Architektur mit einer 32-Bit ALU wäre das gleich schnell. Dein 
16-Bitter braucht aus offensichtlichen Gründen länger.

Interessant wäre, was der Compiler hieraus macht:

1
    if (((x>>16)&0xFF) & 0x08) {
2
        Nop();
3
    }

und ob das schneller ist als

1
    if ((x>>16) & 0x08) {
2
        Nop();
3
    }


PS: man könnte auch noch probieren:

1
    if (((uint8_t)(x>>16)) & 0x08) {
2
        Nop();
3
    }

: Bearbeitet durch User
von NurEinGast (Gast)


Lesenswert?

Und
0x00080000 ist auch nicht mit
0x00000008 zu vergleichen.

von (prx) A. K. (prx)


Lesenswert?

Axel S. schrieb:
> Ein exzellent optimierender Compiler

... ist das in diesem Fall jedenfalls nicht - oder die Optimierung war 
abgeschaltet. Denn der ASM2 Code ist für optimierten Code wirklich unter 
aller Sau.

Ich meine mich zu erinnern, dass Microchips freie Compiler-Version bei 
Optimierung eingeschränkt ist. Intern ist der Compiler von Microchip der 
GCC plus etwas proprietärem Kram. Und der GCC sollte sich nicht so doof 
anstellen. Wenn man ihn lässt.

: Bearbeitet durch User
von Ste N. (steno)


Lesenswert?

So, ich hab das mal kurz probiert, MBLAP IDE 8.87, Compiler Microchip 
c30
1
// Code 1
2
typedef union _ade_status1_t
3
{
4
    unsigned long val;
5
    struct __PACKED
6
    {
7
      unsigned char bla:3;
8
      unsigned char SEQERR:1;
9
    } bits;
10
} status1_t;
11
12
int main (void)
13
{    
14
    volatile status1_t status;
15
  
16
    if (status.bits.SEQERR) {
17
        Nop();
18
    }
19
    
20
    while (1) {
21
    };
22
}

Asm1 ohne Optimierung:
1
00296  78401E     mov.b [0x001c],0x0000
2
00298  604068     and.b 0x0000,#8,0x0000
3
0029A  E00400     cp0.b 0x0000
4
0029C  320001     bra z, 0x0002a0
5
0029E  000000     nop

Asm1 mit Optimierung -Os:
1
00296  97F8CF     mov.b [0x001e-4],0x0002
2
00298  60C068     and.b 0x0002,#8,0x0000
3
0029A  320001     bra z, 0x00029e
4
0029C  000000     nop



1
// Code 2
2
#define STATUS1_SEQERR   0x00080000
3
  
4
int main (void)
5
{
6
    volatile unsigned long x;
7
    if (x & STATUS1_SEQERR) {
8
        Nop();
9
    }
10
  
11
    while (1) {  
12
    };
13
}

Asm2 ohne Optimierung:
1
00296  90001E     mov.w [0x001c+2],0x0000
2
00298  9000AE     mov.w [0x001c+4],0x0002
3
0029A  DE0843     lsr 0x0002,#3,0x0000
4
0029C  200001     mov.w #0x0,0x0002
5
0029E  600061     and.w 0x0000,#1,0x0000
6
002A0  E00400     cp0.b 0x0000
7
002A2  320001     bra z, 0x0002a6
8
002A4  000000     nop

Asm2 mit Optimierung -Os:
1
00296  97B86F     mov.w [0x001e-4],0x0000
2
00298  97B8FF     mov.w [0x001e-2],0x0002
3
0029A  A33801     btst.z 0x0002,#3
4
0029C  320001     bra z, 0x0002a0
5
0029E  000000     nop

Asm2 mit Optimierung -Os:
Einzige Änderung:
1
#define STATUS1_SEQERR   0x0008
2
volatile unsigned int x;
1
00296  97B87F     mov.w [0x001e-2],0x0000
2
00298  A33800     btst.z 0x0000,#3
3
0029A  320001     bra z, 0x00029e
4
0029C  000000     nop

Welchen Compiler benutzt Du? Von Microchip scheint es ja keiner zu sein.

: Bearbeitet durch User
von Peter D. (peda)


Lesenswert?

Steffen N. schrieb:
> volatile unsigned long x;

Das "volatile" zwingt den Compiler, alle 32 Bit zu lesen. Ansonsten wäre 
es ein Fehler.

Besser ist für vergleichende Tests, den Code jeweils in eine 
Unterfunktion zu packen mit den Variablen als Argument bzw. Returnwert. 
Dann entfallen alle Seiteneffekte.
Und ein Leser kann den Code compilieren, ohne hellsehen zu müssen, 
welchen Typ Deine Variablen haben.

von Ste N. (steno)


Lesenswert?

Hallo Peter,

Danke für den Tipp. Ja ich hatte die Variable als volatile deklariert, 
da sonst das ganze if-Konstrukt wegoptimiert wurde. Jetzt hab ich es 
nochmal, wie von dir vorgeschlagen, als Funktion getestet. Hier das 
Ergebnis.

1
typedef union _ade_status1_t
2
{
3
    unsigned long val;
4
    struct __PACKED
5
    {
6
      unsigned char :3;
7
      unsigned char SEQERR:1;
8
    } bits;
9
} status1_t;
10
11
#define STATUS1_SEQERR   0x00080000
12
13
void func1 (status1_t status);
14
void func2 (unsigned long x);
15
  
16
int main (void)
17
{    
18
    status1_t status;
19
    unsigned int x;
20
21
    status.val = 0;
22
    x = 0;
23
    
24
    func1 (status);
25
    func2 (x);
26
  
27
    while (1) {
28
    };
29
}
30
31
void func1 (status1_t status)
32
{
33
    if (status.bits.SEQERR) {
34
        Nop();
35
    }
36
}
37
38
void func2 (unsigned long x)
39
{
40
    if (x & STATUS1_SEQERR) {
41
        Nop();
42
    }
43
}


Asm mit Optimierung -Os:
1
34:                void func1 (status1_t status)
2
35:                {
3
36:                  if (status.bits.SEQERR) {
4
 00294  600068     and.w 0x0000,#8,0x0000
5
 00296  320001     bra z, 0x00029a
6
37:                    Nop();
7
 00298  000000     nop
8
 0029A  060000     return
9
38:                  }
10
39:                }
11
40:                
12
41:                void func2 (unsigned long x)
13
42:                {
14
43:                  if (x & STATUS1_SEQERR) {
15
 0029C  A33801     btst.z 0x0002,#3
16
 0029E  320001     bra z, 0x0002a2
17
44:                        Nop();
18
 002A0  000000     nop
19
 002A2  060000     return

von Sebastian S. (amateur)


Lesenswert?

So lange der Vorrat reicht.

... ist eine Strategie, die die Compiler klaglos unterstützen.

Der Bug sitzt meist an der Tastatur.
Werden einfach, kritiklos Variablen definiert, so gehen die Compiler, im 
Allgemeinen, davon aus, dass das Ganze auch ernst gemeint ist. Ihn 
interessiert nicht, ob eine 32-Bit Variable nur im Bereich vom 0 bis 20 
genutzt wird. Setzt also die passenden Rechenalgorithmen ein.

Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.