mikrocontroller.net

Forum: Mikrocontroller und Digitale Elektronik Zeit problem bei schleifen


Autor: max (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Hallo,

Ich habe ein Zeit Problem mit scheifen! Ich verstehe das verhalten vom 
gcc nicht.
Folgendes Beispiel:

//Einfache Test Funktion in test.c
//aufgerufen von der main Funktion in der Main.c Datei

unsigned char funktion2(unsigned char *buffer)
{
  unsigned int i;

  for(i = 0; i < 512; ++i)
  {
    *buffer++ = SPDR; //z.B.
    
  }
  return 1;
}


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

//Einfache Test Funktion in test.c
//aufgerufen von der main Funktion in der Main.c Datei

unsigned char funktion(unsigned char *buffer, unsigned int zahl)
{
  unsigned int i;

  for(i = 0; i < zahl; ++i)
  {
    *buffer++ = SPDR;
    
  }
  return 1;
}

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?

Autor: Rufus Τ. Firefly (rufus) (Moderator) Benutzerseite
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Mal den erzeugten Assemblercode angesehen? Der sollte Aufschluss geben.

Autor: max (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert

@00000057: main
---- main.c ---------------------------------------------------------------------------------------
5:        {
+00000057:   93DF        PUSH    R29              Push register on stack
+00000058:   93CF        PUSH    R28              Push register on stack
+00000059:   B7CD        IN      R28,0x3D         In from I/O location
+0000005A:   B7DE        IN      R29,0x3E         In from I/O location
+0000005B:   50C0        SUBI    R28,0x00         Subtract immediate
+0000005C:   40D2        SBCI    R29,0x02         Subtract immediate with carry
+0000005D:   B60F        IN      R0,0x3F          In from I/O location
+0000005E:   94F8        CLI                      Global Interrupt Disable
+0000005F:   BFDE        OUT     0x3E,R29         Out to I/O location
+00000060:   BE0F        OUT     0x3F,R0          Out to I/O location
+00000061:   BFCD        OUT     0x3D,R28         Out to I/O location
9:          funktion2(buffer);
+00000062:   01CE        MOVW    R24,R28          Copy register pair
+00000063:   9601        ADIW    R24,0x01         Add immediate to word
+00000064:   940E007F    CALL    0x0000007F       Call subroutine
11:       }
+00000066:   E080        LDI     R24,0x00         Load immediate
+00000067:   E090        LDI     R25,0x00         Load immediate
+00000068:   50C0        SUBI    R28,0x00         Subtract immediate
+00000069:   4FDE        SBCI    R29,0xFE         Subtract immediate with carry
+0000006A:   B60F        IN      R0,0x3F          In from I/O location
+0000006B:   94F8        CLI                      Global Interrupt Disable
+0000006C:   BFDE        OUT     0x3E,R29         Out to I/O location
+0000006D:   BE0F        OUT     0x3F,R0          Out to I/O location
+0000006E:   BFCD        OUT     0x3D,R28         Out to I/O location
+0000006F:   91CF        POP     R28              Pop register from stack
+00000070:   91DF        POP     R29              Pop register from stack
+00000071:   9508        RET                      Subroutine return
@00000072: funktion
---- test.c ---------------------------------------------------------------------------------------
5:        {
+00000072:   01FC        MOVW    R30,R24          Copy register pair
+00000073:   E020        LDI     R18,0x00         Load immediate
+00000074:   E030        LDI     R19,0x00         Load immediate
+00000075:   C004        RJMP    PC+0x0005        Relative jump
10:           *buffer++ = SPDR;
+00000076:   B58E        IN      R24,0x2E         In from I/O location
+00000077:   9381        ST      Z+,R24           Store indirect and postincrement
8:          for(i = 0; i < zahl; ++i)
+00000078:   5F2F        SUBI    R18,0xFF         Subtract immediate
+00000079:   4F3F        SBCI    R19,0xFF         Subtract immediate with carry
+0000007A:   1726        CP      R18,R22          Compare
+0000007B:   0737        CPC     R19,R23          Compare with carry
+0000007C:   F3C8        BRCS    PC-0x06          Branch if carry set
14:       }
+0000007D:   E081        LDI     R24,0x01         Load immediate
+0000007E:   9508        RET                      Subroutine return
@0000007F: funktion2
18:       {
+0000007F:   01FC        MOVW    R30,R24          Copy register pair
+00000080:   E020        LDI     R18,0x00         Load immediate
+00000081:   E030        LDI     R19,0x00         Load immediate
23:           *buffer++ = SPDR;
+00000082:   B58E        IN      R24,0x2E         In from I/O location
+00000083:   9381        ST      Z+,R24           Store indirect and postincrement
21:         for(i = 0; i < zahl; ++i)
+00000084:   5F2F        SUBI    R18,0xFF         Subtract immediate
+00000085:   4F3F        SBCI    R19,0xFF         Subtract immediate with carry
+00000086:   E082        LDI     R24,0x02         Load immediate
+00000087:   3020        CPI     R18,0x00         Compare with immediate
+00000088:   0738        CPC     R19,R24          Compare with carry
+00000089:   F7C1        BRNE    PC-0x07          Branch if not equal
27:       }
+0000008A:   E081        LDI     R24,0x01         Load immediate
+0000008B:   9508        RET                      Subroutine return
+0000008C:   94F8        CLI                      Global Interrupt Disable
+0000008D:   CFFF        RJMP    PC-0x0000        Relative jump
+0000008E:   83FF        STD     Y+7,R31          Store indirect with displacement
+0000008F:   8389        STD     Y+1,R24          Store indirect with displacement
+00000090:   8129        LDD     R18,Y+1          Load indirect with displacement
+00000091:   813A        LDD     R19,Y+2          Load indirect with displacement
+00000092:   818D        LDD     R24,Y+5          Load indirect with displacement
+00000093:   819E        LDD     R25,Y+6          Load indirect with displacement
+00000094:   1728        CP      R18,R24          Compare
+00000095:   0739        CPC     R19,R25          Compare with carry
+00000096:   F348        BRCS    PC-0x16          Branch if carry set
+00000097:   E081        LDI     R24,0x01         Load immediate
+00000098:   9626        ADIW    R28,0x06         Add immediate to word
+00000099:   B60F        IN      R0,0x3F          In from I/O location
+0000009A:   94F8        CLI                      Global Interrupt Disable
+0000009B:   BFDE        OUT     0x3E,R29         Out to I/O location
+0000009C:   BE0F        OUT     0x3F,R0          Out to I/O location
+0000009D:   BFCD        OUT     0x3D,R28         Out to I/O location
+0000009E:   91CF        POP     R28              Pop register from stack
+0000009F:   91DF        POP     R29              Pop register from stack
+000000A0:   9508        RET                      Subroutine return
+000000A1:   93DF        PUSH    R29              Push register on stack
+000000A2:   93CF        PUSH    R28              Push register on stack
+000000A3:   D000        RCALL   PC+0x0001        Relative call subroutine
+000000A4:   D000        RCALL   PC+0x0001        Relative call subroutine
+000000A5:   B7CD        IN      R28,0x3D         In from I/O location
+000000A6:   B7DE        IN      R29,0x3E         In from I/O location
+000000A7:   839C        STD     Y+4,R25          Store indirect with displacement
+000000A8:   838B        STD     Y+3,R24          Store indirect with displacement
+000000A9:   821A        STD     Y+2,R1           Store indirect with displacement
+000000AA:   8219        STD     Y+1,R1           Store indirect with displacement
+000000AB:   C010        RJMP    PC+0x0011        Relative jump
+000000AC:   E4EE        LDI     R30,0x4E         Load immediate
+000000AD:   E0F0        LDI     R31,0x00         Load immediate
+000000AE:   8180        LDD     R24,Z+0          Load indirect with displacement
+000000AF:   81EB        LDD     R30,Y+3          Load indirect with displacement
+000000B0:   81FC        LDD     R31,Y+4          Load indirect with displacement
+000000B1:   8380        STD     Z+0,R24          Store indirect with displacement
+000000B2:   818B        LDD     R24,Y+3          Load indirect with displacement
+000000B3:   819C        LDD     R25,Y+4          Load indirect with displacement
+000000B4:   9601        ADIW    R24,0x01         Add immediate to word
+000000B5:   839C        STD     Y+4,R25          Store indirect with displacement
+000000B6:   838B        STD     Y+3,R24          Store indirect with displacement
+000000B7:   8189        LDD     R24,Y+1          Load indirect with displacement
+000000B8:   819A        LDD     R25,Y+2          Load indirect with displacement
+000000B9:   9601        ADIW    R24,0x01         Add immediate to word
+000000BA:   839A        STD     Y+2,R25          Store indirect with displacement
+000000BB:   8389        STD     Y+1,R24          Store indirect with displacement
+000000BC:   8189        LDD     R24,Y+1          Load indirect with displacement
+000000BD:   819A        LDD     R25,Y+2          Load indirect with displacement
+000000BE:   E022        LDI     R18,0x02         Load immediate
+000000BF:   3080        CPI     R24,0x00         Compare with immediate
+000000C0:   0792        CPC     R25,R18          Compare with carry
+000000C1:   F350        BRCS    PC-0x15          Branch if carry set
+000000C2:   E081        LDI     R24,0x01         Load immediate
+000000C3:   900F        POP     R0               Pop register from stack
+000000C4:   900F        POP     R0               Pop register from stack
+000000C5:   900F        POP     R0               Pop register from stack
+000000C6:   900F        POP     R0               Pop register from stack
+000000C7:   91CF        POP     R28              Pop register from stack
+000000C8:   91DF        POP     R29              Pop register from stack
+000000C9:   9508        RET                      Subroutine return
+000000CA:   94F8        CLI                      Global Interrupt Disable
+000000CB:   CFFF        RJMP    PC-0x0000        Relative jump
+000000CC:   FFFF        ???                      Data or unknown opcode


Hier mal der komplette asm code

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

und dies macht der bei der schnellen schleife

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



Ich finde die Zeitunterschiede schon extrem

Autor: Lothar Miller (lkmiller) (Moderator) Benutzerseite
Datum:

Bewertung
0 lesenswert
nicht 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?

Autor: max (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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.

Autor: Micha (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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?

Autor: max (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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

Autor: Detlev T. (detlevt)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Wenn du i in deiner Schleife nicht explizit brauchst, kannst du das auch 
so machen:
unsigned char funktion3(unsigned char *buffer)
{
  unsigned int i;

  for(i = 512; i; i--)
  {
    *buffer++ = SPDR; //z.B.
    
  }
  return 1;
}

Autor: Lothar Miller (lkmiller) (Moderator) Benutzerseite
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Oder so:
unsigned char funktion3(unsigned char *buffer)
{
  unsigned char c;
  c = 255;
  do { *buffer++ = SPDR; } while (c--);
  c = 255;
  do { *buffer++ = SPDR; } while (c--);
  return 1;
}
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.

Autor: Detlev T. (detlevt)
Datum:

Bewertung
0 lesenswert
nicht 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.

Autor: yalu (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Kompakt und schnell:
  unsigned char c;

  c = 255;
  do {
    *buffer++ = SPDR;
    *buffer++ = SPDR;
  } while(c--);

Autor: Matthias Lipinsky (lippy)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
>Kompakt und schnell: ..

ergibt doch 510 Durchläufe.

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

Autor: holger (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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.

Autor: holger (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
>Differenz = 34ms.

Sind natürlich 34us :(

Autor: yalu (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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 ;-)

Autor: Matthias Lipinsky (lippy)
Datum:

Bewertung
0 lesenswert
nicht 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.

Autor: max (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Danke an alle!


Ich habe jetzt

  unsigned char c = 0;
  do {
    *buffer++ = SPDR;
    *buffer++ = SPDR;
  } 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

Autor: Lothar Miller (lkmiller) (Moderator) Benutzerseite
Datum:

Bewertung
0 lesenswert
nicht 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.

Antwort schreiben

Die Angabe einer E-Mail-Adresse ist freiwillig. Wenn Sie automatisch per E-Mail über Antworten auf Ihren Beitrag informiert werden möchten, melden Sie sich bitte an.

Wichtige Regeln - erst lesen, dann posten!

  • Groß- und Kleinschreibung verwenden
  • Längeren Sourcecode nicht im Text einfügen, sondern als Dateianhang

Formatierung (mehr Informationen...)

  • [c]C-Code[/c]
  • [avrasm]AVR-Assembler-Code[/avrasm]
  • [code]Code in anderen Sprachen, ASCII-Zeichnungen[/code]
  • [math]Formel in LaTeX-Syntax[/math]
  • [[Titel]] - Link zu Artikel
  • Verweis auf anderen Beitrag einfügen: Rechtsklick auf Beitragstitel,
    "Adresse kopieren", und in den Text einfügen




Bild automatisch verkleinern, falls nötig
Bitte das JPG-Format nur für Fotos und Scans verwenden!
Zeichnungen und Screenshots im PNG- oder
GIF-Format hochladen. Siehe Bildformate.
Hinweis: der ursprüngliche Beitrag ist mehr als 6 Monate alt.
Bitte hier nur auf die ursprüngliche Frage antworten,
für neue Fragen einen neuen Beitrag erstellen.

Mit dem Abschicken bestätigst du, die Nutzungsbedingungen anzuerkennen.