Forum: Mikrocontroller und Digitale Elektronik Zeit problem bei schleifen


von max (Gast)


Lesenswert?

Hallo,

Ich habe ein Zeit Problem mit scheifen! Ich verstehe das verhalten vom 
gcc nicht.
Folgendes Beispiel:
1
//Einfache Test Funktion in test.c
2
//aufgerufen von der main Funktion in der Main.c Datei
3
4
unsigned char funktion2(unsigned char *buffer)
5
{
6
  unsigned int i;
7
8
  for(i = 0; i < 512; ++i)
9
  {
10
    *buffer++ = SPDR; //z.B.
11
    
12
  }
13
  return 1;
14
}

Diese Schleife braucht, mit allem drum und dran, 352,51us.
Bei -Os 14.75Mhz
1
//Einfache Test Funktion in test.c
2
//aufgerufen von der main Funktion in der Main.c Datei
3
4
unsigned char funktion(unsigned char *buffer, unsigned int zahl)
5
{
6
  unsigned int i;
7
8
  for(i = 0; i < zahl; ++i)
9
  {
10
    *buffer++ = SPDR;
11
    
12
  }
13
  return 1;
14
}

Diese Schleife braucht, mit allem drum und dran, 318,33us.
Bei -Os 14.75Mhz




Jetzt verstehe ich nicht wieso die eine schleife schneller ist als die 
andere?

von Rufus Τ. F. (rufus) Benutzerseite


Lesenswert?

Mal den erzeugten Assemblercode angesehen? Der sollte Aufschluss geben.

von max (Gast)


Lesenswert?

1
@00000057: main
2
---- main.c ---------------------------------------------------------------------------------------
3
5:        {
4
+00000057:   93DF        PUSH    R29              Push register on stack
5
+00000058:   93CF        PUSH    R28              Push register on stack
6
+00000059:   B7CD        IN      R28,0x3D         In from I/O location
7
+0000005A:   B7DE        IN      R29,0x3E         In from I/O location
8
+0000005B:   50C0        SUBI    R28,0x00         Subtract immediate
9
+0000005C:   40D2        SBCI    R29,0x02         Subtract immediate with carry
10
+0000005D:   B60F        IN      R0,0x3F          In from I/O location
11
+0000005E:   94F8        CLI                      Global Interrupt Disable
12
+0000005F:   BFDE        OUT     0x3E,R29         Out to I/O location
13
+00000060:   BE0F        OUT     0x3F,R0          Out to I/O location
14
+00000061:   BFCD        OUT     0x3D,R28         Out to I/O location
15
9:          funktion2(buffer);
16
+00000062:   01CE        MOVW    R24,R28          Copy register pair
17
+00000063:   9601        ADIW    R24,0x01         Add immediate to word
18
+00000064:   940E007F    CALL    0x0000007F       Call subroutine
19
11:       }
20
+00000066:   E080        LDI     R24,0x00         Load immediate
21
+00000067:   E090        LDI     R25,0x00         Load immediate
22
+00000068:   50C0        SUBI    R28,0x00         Subtract immediate
23
+00000069:   4FDE        SBCI    R29,0xFE         Subtract immediate with carry
24
+0000006A:   B60F        IN      R0,0x3F          In from I/O location
25
+0000006B:   94F8        CLI                      Global Interrupt Disable
26
+0000006C:   BFDE        OUT     0x3E,R29         Out to I/O location
27
+0000006D:   BE0F        OUT     0x3F,R0          Out to I/O location
28
+0000006E:   BFCD        OUT     0x3D,R28         Out to I/O location
29
+0000006F:   91CF        POP     R28              Pop register from stack
30
+00000070:   91DF        POP     R29              Pop register from stack
31
+00000071:   9508        RET                      Subroutine return
32
@00000072: funktion
33
---- test.c ---------------------------------------------------------------------------------------
34
5:        {
35
+00000072:   01FC        MOVW    R30,R24          Copy register pair
36
+00000073:   E020        LDI     R18,0x00         Load immediate
37
+00000074:   E030        LDI     R19,0x00         Load immediate
38
+00000075:   C004        RJMP    PC+0x0005        Relative jump
39
10:           *buffer++ = SPDR;
40
+00000076:   B58E        IN      R24,0x2E         In from I/O location
41
+00000077:   9381        ST      Z+,R24           Store indirect and postincrement
42
8:          for(i = 0; i < zahl; ++i)
43
+00000078:   5F2F        SUBI    R18,0xFF         Subtract immediate
44
+00000079:   4F3F        SBCI    R19,0xFF         Subtract immediate with carry
45
+0000007A:   1726        CP      R18,R22          Compare
46
+0000007B:   0737        CPC     R19,R23          Compare with carry
47
+0000007C:   F3C8        BRCS    PC-0x06          Branch if carry set
48
14:       }
49
+0000007D:   E081        LDI     R24,0x01         Load immediate
50
+0000007E:   9508        RET                      Subroutine return
51
@0000007F: funktion2
52
18:       {
53
+0000007F:   01FC        MOVW    R30,R24          Copy register pair
54
+00000080:   E020        LDI     R18,0x00         Load immediate
55
+00000081:   E030        LDI     R19,0x00         Load immediate
56
23:           *buffer++ = SPDR;
57
+00000082:   B58E        IN      R24,0x2E         In from I/O location
58
+00000083:   9381        ST      Z+,R24           Store indirect and postincrement
59
21:         for(i = 0; i < zahl; ++i)
60
+00000084:   5F2F        SUBI    R18,0xFF         Subtract immediate
61
+00000085:   4F3F        SBCI    R19,0xFF         Subtract immediate with carry
62
+00000086:   E082        LDI     R24,0x02         Load immediate
63
+00000087:   3020        CPI     R18,0x00         Compare with immediate
64
+00000088:   0738        CPC     R19,R24          Compare with carry
65
+00000089:   F7C1        BRNE    PC-0x07          Branch if not equal
66
27:       }
67
+0000008A:   E081        LDI     R24,0x01         Load immediate
68
+0000008B:   9508        RET                      Subroutine return
69
+0000008C:   94F8        CLI                      Global Interrupt Disable
70
+0000008D:   CFFF        RJMP    PC-0x0000        Relative jump
71
+0000008E:   83FF        STD     Y+7,R31          Store indirect with displacement
72
+0000008F:   8389        STD     Y+1,R24          Store indirect with displacement
73
+00000090:   8129        LDD     R18,Y+1          Load indirect with displacement
74
+00000091:   813A        LDD     R19,Y+2          Load indirect with displacement
75
+00000092:   818D        LDD     R24,Y+5          Load indirect with displacement
76
+00000093:   819E        LDD     R25,Y+6          Load indirect with displacement
77
+00000094:   1728        CP      R18,R24          Compare
78
+00000095:   0739        CPC     R19,R25          Compare with carry
79
+00000096:   F348        BRCS    PC-0x16          Branch if carry set
80
+00000097:   E081        LDI     R24,0x01         Load immediate
81
+00000098:   9626        ADIW    R28,0x06         Add immediate to word
82
+00000099:   B60F        IN      R0,0x3F          In from I/O location
83
+0000009A:   94F8        CLI                      Global Interrupt Disable
84
+0000009B:   BFDE        OUT     0x3E,R29         Out to I/O location
85
+0000009C:   BE0F        OUT     0x3F,R0          Out to I/O location
86
+0000009D:   BFCD        OUT     0x3D,R28         Out to I/O location
87
+0000009E:   91CF        POP     R28              Pop register from stack
88
+0000009F:   91DF        POP     R29              Pop register from stack
89
+000000A0:   9508        RET                      Subroutine return
90
+000000A1:   93DF        PUSH    R29              Push register on stack
91
+000000A2:   93CF        PUSH    R28              Push register on stack
92
+000000A3:   D000        RCALL   PC+0x0001        Relative call subroutine
93
+000000A4:   D000        RCALL   PC+0x0001        Relative call subroutine
94
+000000A5:   B7CD        IN      R28,0x3D         In from I/O location
95
+000000A6:   B7DE        IN      R29,0x3E         In from I/O location
96
+000000A7:   839C        STD     Y+4,R25          Store indirect with displacement
97
+000000A8:   838B        STD     Y+3,R24          Store indirect with displacement
98
+000000A9:   821A        STD     Y+2,R1           Store indirect with displacement
99
+000000AA:   8219        STD     Y+1,R1           Store indirect with displacement
100
+000000AB:   C010        RJMP    PC+0x0011        Relative jump
101
+000000AC:   E4EE        LDI     R30,0x4E         Load immediate
102
+000000AD:   E0F0        LDI     R31,0x00         Load immediate
103
+000000AE:   8180        LDD     R24,Z+0          Load indirect with displacement
104
+000000AF:   81EB        LDD     R30,Y+3          Load indirect with displacement
105
+000000B0:   81FC        LDD     R31,Y+4          Load indirect with displacement
106
+000000B1:   8380        STD     Z+0,R24          Store indirect with displacement
107
+000000B2:   818B        LDD     R24,Y+3          Load indirect with displacement
108
+000000B3:   819C        LDD     R25,Y+4          Load indirect with displacement
109
+000000B4:   9601        ADIW    R24,0x01         Add immediate to word
110
+000000B5:   839C        STD     Y+4,R25          Store indirect with displacement
111
+000000B6:   838B        STD     Y+3,R24          Store indirect with displacement
112
+000000B7:   8189        LDD     R24,Y+1          Load indirect with displacement
113
+000000B8:   819A        LDD     R25,Y+2          Load indirect with displacement
114
+000000B9:   9601        ADIW    R24,0x01         Add immediate to word
115
+000000BA:   839A        STD     Y+2,R25          Store indirect with displacement
116
+000000BB:   8389        STD     Y+1,R24          Store indirect with displacement
117
+000000BC:   8189        LDD     R24,Y+1          Load indirect with displacement
118
+000000BD:   819A        LDD     R25,Y+2          Load indirect with displacement
119
+000000BE:   E022        LDI     R18,0x02         Load immediate
120
+000000BF:   3080        CPI     R24,0x00         Compare with immediate
121
+000000C0:   0792        CPC     R25,R18          Compare with carry
122
+000000C1:   F350        BRCS    PC-0x15          Branch if carry set
123
+000000C2:   E081        LDI     R24,0x01         Load immediate
124
+000000C3:   900F        POP     R0               Pop register from stack
125
+000000C4:   900F        POP     R0               Pop register from stack
126
+000000C5:   900F        POP     R0               Pop register from stack
127
+000000C6:   900F        POP     R0               Pop register from stack
128
+000000C7:   91CF        POP     R28              Pop register from stack
129
+000000C8:   91DF        POP     R29              Pop register from stack
130
+000000C9:   9508        RET                      Subroutine return
131
+000000CA:   94F8        CLI                      Global Interrupt Disable
132
+000000CB:   CFFF        RJMP    PC-0x0000        Relative jump
133
+000000CC:   FFFF        ???                      Data or unknown opcode

Hier mal der komplette asm code

Bei der langsamen schleife macht er:
1
23:           *buffer++ = SPDR;
2
+00000082:   B58E        IN      R24,0x2E         In from I/O location
3
+00000083:   9381        ST      Z+,R24           Store indirect and postincrement
4
21:         for(i = 0; i < 512; ++i)
5
+00000084:   5F2F        SUBI    R18,0xFF         Subtract immediate
6
+00000085:   4F3F        SBCI    R19,0xFF         Subtract immediate with carry
7
+00000086:   E082        LDI     R24,0x02         Load immediate
8
+00000087:   3020        CPI     R18,0x00         Compare with immediate
9
+00000088:   0738        CPC     R19,R24          Compare with carry
10
+00000089:   F7C1        BRNE    PC-0x07          Branch if not equal
11
27:       }

und dies macht der bei der schnellen schleife
1
10:           *buffer++ = SPDR;
2
+00000076:   B58E        IN      R24,0x2E         In from I/O location
3
+00000077:   9381        ST      Z+,R24           Store indirect and postincrement
4
8:          for(i = 0; i < zahl; ++i)
5
+00000078:   5F2F        SUBI    R18,0xFF         Subtract immediate
6
+00000079:   4F3F        SBCI    R19,0xFF         Subtract immediate with carry
7
+0000007A:   1726        CP      R18,R22          Compare
8
+0000007B:   0737        CPC     R19,R23          Compare with carry
9
+0000007C:   F3C8        BRCS    PC-0x06          Branch if carry set
10
14:       }


Ich finde die Zeitunterschiede schon extrem

von Lothar M. (Firma: Titel) (lkmiller) (Moderator) Benutzerseite


Lesenswert?

> Ich finde die Zeitunterschiede schon extrem
Was ist daran extrem?
Wenn er statt 10 Zylken 11 machen muß, dann gehts halt so aus.
Das sind gerade mal 10%, extrem ist für mich anders.

Bei der schnellen Schleife behält er die Variable im Register, bei der 
Konstanten-Variante muß er erst nachladen.

Diese Ineffizienz wird sich bei aufwendigeren Schleifen (wenn die 
Vergleichsvariable nicht so einfach im Register behalten werden kann) 
nicht so extram auswirken.

> 352,51us ... 318,33us
Wozu die Nanosekundenschinderei?

von max (Gast)


Lesenswert?

>Wozu die Nanosekundenschinderei?

Das ganze ist für in SD-Karten "Treiber"

bei der schnellen schleife braucht ich ungefähr 40ms weniger zeit, bei 
1MB, als bei der langsamen scheifen.

von Micha (Gast)


Lesenswert?

>> Wozu die Nanosekundenschinderei?
> Das ganze ist für in SD-Karten "Treiber"
Bleibt die Funktion so oder macht die was sinnvolles wenn sie fertig 
ist?

von max (Gast)


Lesenswert?

>Bleibt die Funktion so oder macht die was sinnvolles wenn sie fertig
ist?

das war ein beispiel?! und das hat nicht mir meinen code zutun

von Detlev T. (detlevt)


Lesenswert?

Wenn du i in deiner Schleife nicht explizit brauchst, kannst du das auch 
so machen:
1
unsigned char funktion3(unsigned char *buffer)
2
{
3
  unsigned int i;
4
5
  for(i = 512; i; i--)
6
  {
7
    *buffer++ = SPDR; //z.B.
8
    
9
  }
10
  return 1;
11
}

von Lothar M. (Firma: Titel) (lkmiller) (Moderator) Benutzerseite


Lesenswert?

Oder so:
1
unsigned char funktion3(unsigned char *buffer)
2
{
3
  unsigned char c;
4
  c = 255;
5
  do { *buffer++ = SPDR; } while (c--);
6
  c = 255;
7
  do { *buffer++ = SPDR; } while (c--);
8
  return 1;
9
}
Dann ist die Laufvariable nur ein char, und damit könnte der Vergleich 
schneller sein. Allerdings solltest du das ausprobieren, der gcc ist 
recht int-lastig.

von Detlev T. (detlevt)


Lesenswert?

@Lothar Miller
Auch eine Möglichkeit. Allerdings kollidiert das mit der Absicht, 
möglichst kompakten Code zu erzeugen. Sonst würde max ja kaum die 
Compileroption -Os verwenden und eher -O2 nehmen.

von yalu (Gast)


Lesenswert?

Kompakt und schnell:
1
  unsigned char c;
2
3
  c = 255;
4
  do {
5
    *buffer++ = SPDR;
6
    *buffer++ = SPDR;
7
  } while(c--);

von Matthias L. (Gast)


Lesenswert?

>Kompakt und schnell: ..

ergibt doch 510 Durchläufe.

Für 512 mach das so:
1
  unsigned char c = 0;
2
  do {
3
    *buffer++ = SPDR;
4
    *buffer++ = SPDR;
5
  } while(--c);

von holger (Gast)


Lesenswert?

>> 352,51us ... 318,33us

>bei der schnellen schleife braucht ich ungefähr 40ms weniger zeit, bei
>1MB, als bei der langsamen scheifen.

Man kann sich alles schön rechnen.
Hier gehts ja wohl um SPI. In den Schleifen oben
fehlt das wichtigste ja noch.

1) 0xFF in SPDR schreiben um die Daten aus der Karte zu takten
2) Warten auf SPIF bis die Daten auch wirklich in SPDR stehen

Bei einem SPI Takt von 8MHz kann man theoretisch 1MB/s
übertragen. Also ein Byte pro us. Bei 512 Bytes sind das
schon mal 512us. Die muss man zu den Zeiten oben dazuzählen.
512us + 352us = 864us.
512us + 318us = 830us.
Differenz = 34ms. Also weniger als 5% Unterschied.
Und ich hab die Zeiten von 14,75MHz noch nicht auf 16MHz umgerechnet.
Dann fehlt aber noch die Abfrage auf SPIF!
Der Unterschied wird also noch kleiner werden.

Kleiner Code ist ja gut und schön, aber schnell
wird man bei SPI nur wenn man statt gleich auf SPIF zu warten
erstmal etwas anderes tut. Schleifenzähler bearbeiten oder
Daten speichern zum Beispiel.

von holger (Gast)


Lesenswert?

>Differenz = 34ms.

Sind natürlich 34us :(

von yalu (Gast)


Lesenswert?

Matthias Lipinsky schrieb:
>> Kompakt und schnell: ..
>
> ergibt doch 510 Durchläufe.

Ich habe das Schleifenkonstrukt ohne nachzudenken von Lothar Miller
abgeschrieben, also stimmt es wahrscheinlich und weist SPDR tatsächlich
512mal zu. Deine Variante ist hingegen nur dann richtig, wenn ein
unsigned char den Wertebereich 0..255 hat ;-)

von Matthias L. (Gast)


Lesenswert?

>nur wenn man statt gleich auf SPIF zu warten erstmal etwas anderes tut.

GEnau. Und wenn man das "etwas anderes" gleich so abstimmt, kann man 
sich das ABfragen von SPIF sparen. Das muss dann aber gut abgestimmt 
sein.

von max (Gast)


Lesenswert?

Danke an alle!


Ich habe jetzt
1
  unsigned char c = 0;
2
  do {
3
    *buffer++ = SPDR;
4
    *buffer++ = SPDR;
5
  } while(--c);

verwendet

Jetzt braucht das ganze nur noch 196,19us statt 352,51us.

ich glaube ich werde nur noch solche schleifen verwenden.

danke noch ma

von Lothar M. (Firma: Titel) (lkmiller) (Moderator) Benutzerseite


Lesenswert?

@  Matthias Lipinsky (lippy)
>> Kompakt und schnell: ..
> ergibt doch 510 Durchläufe.
> Für 512 mach das so:...
Nein, das sind auch mit dem Startwert 255 schon 512 Durchläufe wegen dem 
Post-Decrement und dem abschliessenden Vergleich. Denn auch mit der 0 
wird die Schleife nochmal durchlaufen     ;-)

@ max
> ich glaube ich werde nur noch solche schleifen verwenden.
Ich glaube das kannst du nicht :-(
Das geht in diesem Fall nur so schnell, wenn die Zählvariable in ein 
char passt.

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.