Forum: Mikrocontroller und Digitale Elektronik Counter Geschwindigkeit


von Renato (Gast)


Lesenswert?

Na ihr,

Die Verpackung verspricht ja im Normalfall viel mehr als wirklich 
geleistet wird.
Heute habe ich mal aus reiner Neugierde ausgetestet, ob das auch für 
einen AVR(oder bzw den verwendeten Quarz) zutrifft.

Folgendes Programm soll einfach zählen und nach jedem Zählvorgang eine 
Led ein/ausschalten:
1
...
2
long i;
3
while (1){
4
    for(i=0;i<=6000000;i++){}   
5
    if(PINC.0==1) PORTC.0=0;
6
    else    PORTC.0=1;
7
};
8
...

Der "geht mir aufn Sac*"-Teil:
Diese Schleife sollte wie man sieht bis 6000000(6Millionen) zählen. 
Dazu(also für eine Periode) benötigt mein ATMEGA32 mit 16MHZ Quarz etwa 
20 sekunden.
-> Rechnen: 6000000/20=300000HZ=300KHZ
-> lol, nen ATMEGA mit 16MHZ-Quarz kann nicht schneller als mit 300KHZ 
zählen??
Das ist wohl oder übel ein Verhältniss von 1/53.

mfg Renato

von TestX .. (xaos)


Lesenswert?

achso klar, eine 32bit addition, sowie der schleifen counter etc 
brauchen auch nur 1nen takt oder wie ? ;)

schau die den asm code an und man siehe und staune was da für ein 
monster rauskommt ;)

von Gast (Gast)


Lesenswert?

>Die Verpackung verspricht ja im Normalfall viel mehr als wirklich
>geleistet wird.

Trifft genau auf dich zu.

von Renato (Gast)


Lesenswert?

Hab auch ne 8Bit und 16Bit Addition versucht. Die Unterschiede liegen 
unterhalb des Sekundenbereichs.

Hab mir fast gedacht dass jetzt gleich Assembler erwähnt wird. :D
Na dann lern ich jetzt mal Assembler Programmieren.(Dieser Schritt 
musste ja irgendwann mal kommen ^^)



Schon mal irgendwer ausprobiert wie schnell man mit Assembler nen 
Counter hinkriegt?(Also verhältnis zwischen Clock und tatsächlicher 
Zählgeschwindigkeit)

mfg

von Renato (Gast)


Lesenswert?

Wer bist du und was meinst du damit?
Willst mich provozieren? Na dann viel Spaß :)

von Renato (Gast)


Lesenswert?

Also ich mein den "Gast (Gast)" :P

von holger (Gast)


Lesenswert?

>Schon mal irgendwer ausprobiert wie schnell man mit Assembler nen
>Counter hinkriegt?

Nein? Counter sollte man generell vermeiden.
Es kommt immer nur Schrott dabei raus. Volldepp!

von Peter D. (pdiener) Benutzerseite


Lesenswert?

Ein normaler 8 bit Counter braucht inclusive Auswerten der 
Abbruchbedingung 3 Takte Assembler auf einem AVR.

Es gibt in C eine äquivalente Funktion:

void _delay_loop_1    (uint8_t __count)

In 16 Bit benötigt das 5 Takte, in C entsprechend:

void _delay_loop_2    (uint16_t __count)


Grüße,

Peter

von Renato P. (renato)


Lesenswert?

>Nein? Counter sollte man generell vermeiden.
>Es kommt immer nur Schrott dabei raus. Volldepp!

Vermeiden? Was soll ich denn Vermeiden?
Ich will nur sehen wie schnell der Zählen tut und wenn möglich ne 
schnellere Methode als meine finden.
Du verstehst wohl den Sinn des Topics nicht? :)

von TestX .. (xaos)


Lesenswert?

Renato P. schrieb:
>>Nein? Counter sollte man generell vermeiden.
>>Es kommt immer nur Schrott dabei raus. Volldepp!
>
> Vermeiden? Was soll ich denn Vermeiden?
> Ich will nur sehen wie schnell der Zählen tut und wenn möglich ne
> schnellere Methode als meine finden.
> Du verstehst wohl den Sinn des Topics nicht? :)

und du nicht die funktionsweise von mcu, compiler & co...
nachdem du dich durch die tuts hier durchgearbeitet hast reden wir 
weiter ;)

von Peter D. (peda)


Lesenswert?

Renato P. schrieb:
> Ich will nur sehen wie schnell der Zählen tut und wenn möglich ne
> schnellere Methode als meine finden.

Und was soll das für einen Sinn haben?

Wenn Du schnell zählen willst, nimm nen Timer.
Dann kannst Du nebenbei noch andere Sachen machen.


53 Zyklen kann hinkommen, immerhin hast Du ja die Optimierung 
ausgeschaltet. Mit Optimierung wird diese Schleife nämlich in 0µs 
ausgeführt.

Und kein 8-Bitter macht 32Bit-Operationen in einem Takt, er muß sie aus 
mehreren 8Bit-Operationen zusammensetzen.


> Du verstehst wohl den Sinn des Topics nicht? :)

Den versteht wohl keiner.


Peter

von Renato P. (renato)


Lesenswert?

Andi D. schrieb:
> Renato P. schrieb:
>>>Nein? Counter sollte man generell vermeiden.
>>>Es kommt immer nur Schrott dabei raus. Volldepp!
>>
>> Vermeiden? Was soll ich denn Vermeiden?
>> Ich will nur sehen wie schnell der Zählen tut und wenn möglich ne
>> schnellere Methode als meine finden.
>> Du verstehst wohl den Sinn des Topics nicht? :)
>
> und du nicht die funktionsweise von mcu, compiler & co...

Jo, hab mich nicht besonders mit der Funktionsweise beschäftigt.
Ich habe mich ausserdem nicht mit der Funktionsweise eines PC's 
beschäftigt.
Für meine Anwendungen war/ist das (noch) nicht notwendig.

>nachdem du dich durch die tuts hier durchgearbeitet hast reden wir
>weiter ;)
Jo mach ma ;)

von Renato P. (renato)


Lesenswert?

Peter Dannegger schrieb:
> Renato P. schrieb:
>> Ich will nur sehen wie schnell der Zählen tut und wenn möglich ne
>> schnellere Methode als meine finden.
>
> Und was soll das für einen Sinn haben?

Tja das ist eine wirklich triviale Frage!

Ich teste und lerne!

Hast du schon mal eine Led aus/eingeschaltet? Wahrscheinlich ja! Hat das 
etwa als selbstständige Funktion einen Sinn(ausser wenns mal dunkel is 
;))?

Man entdeckt Funktionen, die man später zu einem Sinnvollen Projekt 
kombiniert.

von Peter D. (peda)


Lesenswert?

Renato P. schrieb:
> Ich teste und lerne!

Trial & Error ist keine gute Lernmethode.
Man wiederholt nur alle die Fehler, die andere längst gemacht haben.

Deutlich effektiver ist es, sich Tutorials, Examples, FAQs, Lib-Manuals 
usw. anzusehen.

Dann fällt einem z.B. beim AVR-GCC eine delay.h auf.
Die macht genau das, was Du willst und auf den Takt genau.


Scheinbar sind bei vielen C-Installation die Include-, Doc- und 
Example-Verzeichnisse gegen auslesen geschützt und  nur mit 
Administratorrechten einsehbar.


Peter

von Michael U. (amiga)


Lesenswert?

Hallo,

so zum Spaß:

8Bit:
1
  ldi r16,wert
2
3
loop:
4
  dec r16           ; 1 Takt
5
  brne loop         ; 2 Takte - 1 Takt wenn nicht gesprungen wird

16Bit:
1
  ldi ZH,high(wert)
2
  ldi ZL,low(wert)
3
4
loop:
5
  sbiw Z,1         ; 2 Takte
6
  brne loop        ; 2 Takte - 1 Takt wenn nicht gesprungen wird

32Bit:
1
  ldi YH,byte4(wert)
2
  ldi YL,byte3(wert)
3
  ldi ZH,high(wert)
4
  ldi ZL,low(wert)
5
6
loop:
7
  sbiw Z,1         ; 2 Takte 
8
  brne loop        ; 2 Takte - 1 Takt wenn nicht gesprungen wird
9
 
10
  sbiw Y,1         ; 2 Takte 
11
  brne loop        ; 2 Takte - 1 Takt wenn nicht gesprungen wird

Da kannst Du dann im .lss vergleichen, was jeweils der Compiler draus 
macht.

Gruß aus Berlin
Michael

von Peter D. (peda)


Lesenswert?

Michael U. schrieb:
> 32Bit:
> ...
> Da kannst Du dann im .lss vergleichen, was jeweils der Compiler draus
> macht.


Der GCC braucht je Runde 5 Takte:
1
void bad_style_delay( void )
2
{
3
  7a:   80 ec           ldi     r24, 0xC0       ; 192
4
  7c:   97 e2           ldi     r25, 0x27       ; 39
5
  7e:   a9 e0           ldi     r26, 0x09       ; 9
6
  80:   b0 e0           ldi     r27, 0x00       ; 0
7
  for( uint32_t i = 600000L; i; i-- )
8
  82:   01 97           sbiw    r24, 0x01       ; 1
9
  84:   a1 09           sbc     r26, r1
10
  86:   b1 09           sbc     r27, r1
11
  88:   e1 f7           brne    .-8             ; 0x82 <count+0x8>
12
    asm volatile("");
13
}
14
  8a:   08 95           ret

Und ohne das asm volatile("") 0 Takte.

Aber wie gesagt, sowas ist unter C bad style.
Nimm die delay.h.


Peter

von Gast (Gast)


Lesenswert?

>-> lol, nen ATMEGA mit 16MHZ-Quarz kann nicht schneller als mit 300KHZ
>zählen??
>Das ist wohl oder übel ein Verhältniss von 1/53.

Was hast Du denn erwartet? 1/10? 1/2?

von Herbert (Gast)


Lesenswert?

>lol, nen ATMEGA mit 16MHZ-Quarz kann nicht schneller als mit 300KHZ
>zählen??

Selber lol. Er kann natürlich mit 16MHZ zählen, wenn man ihn richtig 
programmiert. Aber dazu muss man natürlich erstmal das Datenblatt lesen 
um überhaupt zu wissen was man tun muss. Von nichts kommt halt nichts.

von Renato P. (renato)


Lesenswert?

Herbert schrieb:
>>lol, nen ATMEGA mit 16MHZ-Quarz kann nicht schneller als mit 300KHZ
>>zählen??
>
> Selber lol. Er kann natürlich mit 16MHZ zählen, wenn man ihn richtig
> programmiert. Aber dazu muss man natürlich erstmal das Datenblatt lesen
> um überhaupt zu wissen was man tun muss. Von nichts kommt halt nichts.

Jo da hab ich mich falsch ausgedrückt. Das war eher auf das zählen 
mittels C-Programm bezogen. Natürlich kann ders schneller wenn man weiß 
wie.

>-> lol, nen ATMEGA mit 16MHZ-Quarz kann nicht schneller als mit 300KHZ
>zählen??
>Das ist wohl oder übel ein Verhältniss von 1/53.

Was hast Du denn erwartet? 1/10? 1/2?

Unter C hab ich mir erhofft dass ich zumindest 1/10 hinkrieg ^^

von Karl H. (kbuchegg)


Lesenswert?

Renato P. schrieb:

> Unter C hab ich mir erhofft dass ich zumindest 1/10 hinkrieg ^^

Schalte den Optimizer ein.
Wie PeDa weiter oben schon gezeigt hat kommt man auf ca. 1/5. Also rund 
3.2Mhz

Für 32Bit-Arithmetik ist das gar kein so schlechtes Ergebnis für einen 
C-Compiler.

von sam (Gast)


Lesenswert?

>Unter C hab ich mir erhofft dass ich zumindest 1/10 hinkrieg ^^
auf welcher grundlage hast du denn gehofft?

wie ein prozessor funktioniert ist besonders bei so zeitkritischen 
sachen interessant und sollte unbedingt als grundwissen beim 
µC-programmieren vorhanden sein.

besonders wenn ich mit der ausführungsgeschwindigkeit in den bereich der 
taktfrequenz des prozessors komme (ich sag mal über 1/100 der 
taktfrequenz ) wird es wichtig für welche befehle wieviel tacktzyklen 
gebraucht werden und wie man da optimieren kann etc.

der simulator gibt einem da einen ganz guten eindruck, der zählt einem 
nämlich die tacktzyklen vor. und dort kann man auch nach belieben die 
ganze geschichte in assambler bestaunen.

von Michael U. (amiga)


Lesenswert?

Hallo,

Karl heinz Buchegger schrieb:
> Wie PeDa weiter oben schon gezeigt hat kommt man auf ca. 1/5. Also rund
> 3.2Mhz
>
> Für 32Bit-Arithmetik ist das gar kein so schlechtes Ergebnis für einen
> C-Compiler.

Kann ich nur zustimmen. Ich bin eigentlich recht positiv überrascht vom 
GGC.
Einige Sachen sollte man aber nicht machen, z.B.

 uint64_t test = 1;
 test = test << 1;

 ;-))

Gruß aus Berlin
Michael

von Malte B. (hellraiser)


Lesenswert?

Hi!

Diskussion um Compileroptimierung hin oder her,
a) ich verstehe den zweck nicht, wozu man so einen zähler als Schleife 
überhaupt sinnvoll einsetzen soll (ausser delay und dabei wäre die 
zaehlgeschwindigkeit sowieso völlig egal)
b) will mir nicht in den kopp, wieso man die Ausführungsgeschwindigkeit 
eines codes MESSEN muss, wenn alle Faktoren bekannt sind (Taktfrequenz 
und benötigte Taktzyklen für die einzelnen Befehle). Das hat man sogar 
mit Taschenrechner suchen schneller ausgerechnet als der Lötkolben zum 
heizen braucht.

Aber ansonsten recht amüsant der Thread hier :)

Gruss, Malte

von Renato P. (renato)


Lesenswert?

Malte Bayer schrieb:

> Diskussion um Compileroptimierung hin oder her,
> a) ich verstehe den zweck nicht, wozu man so einen zähler als Schleife
> überhaupt sinnvoll einsetzen soll (ausser delay und dabei wäre die
> zaehlgeschwindigkeit sowieso völlig egal)

Naja ich wollte nur allgemein mal wissen wie schnell der µC die 
C-Befehle abarbeitet. Das Beispiel mit dem Zähler is mir dazu rein 
zufällig als erstes eingefallen.
Es ist ja allgemein bekannt das Assembler schneller is als C, nur um 
wieviel hab ich noch nie irgendwo gelesen.
Der enorme Geschwindigkeitsverlust bei meinem Programm hat mich total 
verblüfft und somit hab ich mal einfach ein Topic aufgemacht.

Und was passiert als erstes? Na klar, jemand schnauzt mich an, weil er 
denkt ich will nen Timer oder ne delay proggen. ^^
sorry deswegen...

> b) will mir nicht in den kopp, wieso man die Ausführungsgeschwindigkeit
> eines codes MESSEN muss, wenn alle Faktoren bekannt sind (Taktfrequenz
> und benötigte Taktzyklen für die einzelnen Befehle). Das hat man sogar
> mit Taschenrechner suchen schneller ausgerechnet als der Lötkolben zum
> heizen braucht.

Ach, für C-Befehle gibts auch irgendwo die Zyklenzeit-Liste?(danke, ich 
schau gleich mal nach deswegen). Ich bin wohl Totalnoob...
Auf dem Datenblatt sah ich immer nur die Assembler-Zyklenzeiten und aus 
der Schule kenn ich auch nur die Assembler-Zyklenzeit-Rechnungen.

> Aber ansonsten recht amüsant der Thread hier :)
Na das is schön zu hören :P :)

von Renato P. (renato)


Lesenswert?

Karl heinz Buchegger schrieb:
> Schalte den Optimizer ein.

Tut man das mit AVR-Studio?

Sam schrieb:
> der simulator gibt einem da einen ganz guten eindruck,

Das tut man dann wohl auch im AVR-Studio?

Jo, dat sind dumme Fragen(ich verwende CodeVisionAVR, vielleicht 
deshalb).

von Karl H. (kbuchegg)


Lesenswert?

Renato P. schrieb:
> Naja ich wollte nur allgemein mal wissen wie schnell der µC die
> C-Befehle abarbeitet.

Der arbeitet die überhaupt nicht ab.

Sondern der C-Compiler übersetzt die 'C-Befehle' in Maschinensprache (im 
weitesten Sinne ist es genau das was du programmierst, wenn du Assembler 
programmierst)

> Es ist ja allgemein bekannt das Assembler schneller is als C, nur um
> wieviel hab ich noch nie irgendwo gelesen.

Nein, das ist nicht Allgemein bekannt.
Auf vielen Architekturen ist es mitlerweile so, dass der C-Compiler in 
der Lage ist, mindestens 80% aller Assembler-Programmierer bei 
durchschnittlichen Programmen zu schlagen.

> Der enorme Geschwindigkeitsverlust bei meinem Programm hat mich total
> verblüfft und somit hab ich mal einfach ein Topic aufgemacht.

Der enorme Geschwindigkeitsverlust resultiert daraus, dass du mit deinem 
Werkzeug, dem C-Compiler, noch nicht umgehen kannst. Du hast ihn ganz 
einfach 'mit angezogener Handbremse' arbeiten lassen. Da fährt dann auch 
ein Ferrari nicht schneller als 30km/h

> Ach, für C-Befehle gibts auch irgendwo die Zyklenzeit-Liste?

Nein, die gibt es nicht.
Es hängt immer davon ab, wie der Compiler bestimmte Konstrukte umsetzt. 
Und das wiederrum hängt von vielen Faktoren ab.

von Malte B. (hellraiser)


Lesenswert?

Hi Renato,

Karl trifft es auf den Punkt,
damit jetzt vielleicht alle Klarheiten beseitigt werden:

Der AVR uC arbeitet die befehle die er versteht (diese sind im 
Datenblatt ersichtlich) ab.
Für das Ausführen eines jeden Befehls benötigt er eine Gewisse Menge 
Taktzyklen (für die meisten Befehle einen, aber auch zb mal zwei takte 
für einen befehl).

Das hat erstmal garnichts mit der verwendeten Programmiersprache zu tun.

In deinem Ursprungspost vermittelst du mir den Eindruck, als ob du davon 
ausgehst, dass eine Zählschleife ohne weiteren Inhalt aus einem Befehl 
bestünde.
A bissl naiv. Für dich sieht es in C so aus, da es ja nur das for() 
konstrukt ist. Der Controller muss aber viel mehr tun:
- zähler addieren (und das nicht nur 1x, da er nur mit 8bit arbeitet)
- vergleichen ob das schleifenende erreicht ist
- schleifensprung

Meine orredner haben dir das ja bereits am entsprechenden Assemblercode 
versucht zu erklären.

Es gibt deshalb keine Tabelle mit Befehlszeiten für C-Befehle, weil die 
Compiler alle unterschiedliche Befehlskonstrukte für die Controller 
daraus produzieren. Zudem hängt das ja auch noch von dem verwendeten 
Controller ab.
So zum beispiel können manche AVR hardwareseitige Multiplikation, können 
sie dies nicht, so müssen viel mehr Taktzyklen aufgewendet werden um die 
Multiplikation auf herkömmlichen Weg zu berechnen.

Was du dir also ganz klar vor Augen halten musst ist: ZÄHLEN kann ein 
AVR eine 8bit Zahl mit fOSC/2.
Nämlich
1
 Loop:
2
  INC r16
3
  RJMP Loop
Dabei benötigt das Erhöhen und der Rücksprung jeweils einen Taktzyklus.

Das ganze natürlich wenn du den Zähler auch so programmierst.
Ich kann mir gut vorstellen, dass aus
1
for(i=0;i<=255;i++){}
etwas ähnlich Schlankes wird (ich weiss es nicht genau, weil ich avr 
"nur" in assembler programmiere). Logischerweise nicht obiges 
Assemblerbeispiel, weil dies ja eine Endlosschleife darstellt.

JS says: "Klar soweit?"

Gruss, Malte

von Renato P. (renato)


Lesenswert?

Malte Bayer schrieb:
> Ich kann mir gut vorstellen, dass aus
>
1
for(i=0;i<=255;i++){}
> etwas ähnlich Schlankes wird (ich weiss es nicht genau, weil ich avr
> "nur" in assembler programmiere).

Diese Schleife wird denk ich mal ziemlich sicher auch mehr 40 Taktzyklen 
pro Zählererhöhung brauchen(zumindest wenn ichs in meinen obigen 
Codeausschnitt einfüge).
Dat ist ja was mich wundert. Ich ging beim programmieren der Schleife 
davon aus, dass der höchstens 10 Taktzyklen für einen Zähldurchgang 
braucht, aber weit über 40 für eine einfache for-Schleife fand ich 
heftig(ich bin aber auch nen bisschen noob in Sachen Taktzyklen).

Klar verlangsamt die while Schleife das ein bisschen. Aber schau mal:
Die While Schleife fängt an, dann sollte die for-Schleife in Ruhe(sie 
tut ja nix anderes als zu zählen) bis 6Millionen zählen können und dann 
schaltet er den Port um.
Das währen 6Millionen Zählvorgänge, dann 2if's und 1while, dann wieder 
6Millionen Zählvorgänge.
Die while-Schleife und die ifs Scheinen mir da vernächlässigbar 
klein(sind ja nur 6Befehle die nach den 6Millionen Befehlen kommen).
-> Ich komm halt zum Schluss, dass diese 50 Taktzyklen die der µC bei 
mir pro Zählererhöhung gebraucht hat, fast allein an der for-Schleife 
liegen
und ps: long in int umgewandelt erhöht die Schleife nur um 
milisekunden(habs probiert).

Verstehst was ich meine? Also kurz nochmal heißt das folgendes:
1
for(i=0;i<=1;i++){} //also i um 1 erhöhen
Diese Schleife würde 52Taktzyklen verbraten.
Hingegen:
1
INC r16
nur 1 Taktzyklus

Ich mit meinem ungeschulten Auge finde das schon heftig...

von Renato P. (renato)


Lesenswert?

Malte Bayer schrieb:
> In deinem Ursprungspost vermittelst du mir den Eindruck, als ob du davon
> ausgehst, dass eine Zählschleife ohne weiteren Inhalt aus einem Befehl
> bestünde.

jo sorry, is hin und wieder gar nicht so leicht seine Problemstellung zu 
schildern.

von Renato P. (renato)


Lesenswert?

Ich schreib wieder mal Stuss. Ich vergleich ne INC-anweisung mit ner 
for-Schleife -.-...

Gut, in Assembler dauerts 3 Taktzyklen:
1
Loop:
2
  INC r16          //1
3
  RJMP Loop        //2

von Peter (Gast)


Lesenswert?

@ Renato P.
> Ich schreib wieder mal Stuss.
so falsch war es nicht, man kann ja auch die schleife optimierne.

Wenn man bis 10 Zählen möchte geht das dann so

INC r16
INC r16
INC r16
INC r16
INC r16
INC r16
INC r16
INC r16
INC r16
INC r16


schneller wird es kaum gehen, auch wenn es so kaum sinn macht.

von Simon K. (simon) Benutzerseite


Lesenswert?

Peter schrieb:
> schneller wird es kaum gehen, auch wenn es so kaum sinn macht.

Klar gehts schneller. SUBI r16, -10
:D

von Peter (Gast)


Lesenswert?

> Klar gehts schneller. SUBI r16, -10

es ging ums zählen nicht ums rechnen

von Simon K. (simon) Benutzerseite


Lesenswert?

Um den ganzen schmarren mal hier ein wenig Sinn einzuhauchen. Folgendes 
Testprogramm (Nach Fehlerkorrektur aus dem Ursprungspost übernommen).


EDIT: Argh, hab mich natürlich prompt vertan. Kleinen moment noch fürs 
neue Programm...

von Simon K. (simon) Benutzerseite


Lesenswert?

So.
1
#include <avr/io.h>
2
3
int main()
4
{
5
  long i;
6
7
  while (1)
8
  {
9
10
      for(i=0;i<=6000000;i++)
11
    {
12
      asm volatile("");
13
    } 
14
15
    if(PINC & 0x01) 
16
      PORTC = 0x00;
17
    else
18
        PORTC = 0x01;
19
  }
20
21
  return 0;
22
}

(Ich hoffe das jetzt so richtig korrigiert zu haben)
Macht:
1
  while (1)
2
  {
3
4
      for(i=0;i<=6000000;i++)
5
  94:  01 96         adiw  r24, 0x01  ; 1
6
  96:  a1 1d         adc  r26, r1
7
  98:  b1 1d         adc  r27, r1
8
  9a:  81 38         cpi  r24, 0x81  ; 129
9
  9c:  3d e8         ldi  r19, 0x8D  ; 141
10
  9e:  93 07         cpc  r25, r19
11
  a0:  3b e5         ldi  r19, 0x5B  ; 91
12
  a2:  a3 07         cpc  r26, r19
13
  a4:  30 e0         ldi  r19, 0x00  ; 0
14
  a6:  b3 07         cpc  r27, r19
15
  a8:  ac f3         brlt  .-22       ; 0x94 <main+0xc>
16
    {
17
      asm volatile("");
18
    } 
19
20
    if(PINC & 0x01) 
21
  aa:  30 9b         sbis  0x06, 0  ; 6
22
  ac:  02 c0         rjmp  .+4        ; 0xb2 <main+0x2a>
23
      PORTC = 0x00;
24
  ae:  18 b8         out  0x08, r1  ; 8
25
  b0:  01 c0         rjmp  .+2        ; 0xb4 <main+0x2c>
26
    else
27
        PORTC = 0x01;
28
  b2:  28 b9         out  0x08, r18  ; 8
29
  b4:  80 e0         ldi  r24, 0x00  ; 0
30
  b6:  90 e0         ldi  r25, 0x00  ; 0
31
  b8:  a0 e0         ldi  r26, 0x00  ; 0
32
  ba:  b0 e0         ldi  r27, 0x00  ; 0
33
  bc:  ee cf         rjmp  .-36       ; 0x9a <main+0x12>

Kommandozeile:
avr-gcc  -mmcu=atmega644 -Wall -gdwarf-2 -std=gnu99   -DF_CPU=1000000UL 
-Os -funsigned-char -funsigned-bitfields -fpack-struct -fshort-enums -MD 
-MP -MT test.o -MF dep/test.o.d  -c  ../test.c

Da benötigt die For-Schleife 13 us, bzw. 13 Takte bei 1MHz pro 
Durchlauf.

von Simon K. (simon) Benutzerseite


Lesenswert?

Mit folgenden Einstellung braucht die For-Schleife für einen Durchlauf 
37 Takte:

avr-gcc  -mmcu=atmega644 -Wall -gdwarf-2 -std=gnu99 
-DF_CPU=1000000UL -O0 -funsigned-char -funsigned-bitfields -fpack-struct 
-fshort-enums -MD -MP -MT test.o -MF dep/test.o.d  -c  ../test.c

Optimierung ausgeschaltet.

Der Code sieht mehr oder weniger grausam aus (Ich poste nur die for 
Schleife):
1
      for(i=0;i<=6000000;i++)
2
  94:  19 82         std  Y+1, r1  ; 0x01
3
  96:  1a 82         std  Y+2, r1  ; 0x02
4
  98:  1b 82         std  Y+3, r1  ; 0x03
5
  9a:  1c 82         std  Y+4, r1  ; 0x04
6
  9c:  0b c0         rjmp  .+22       ; 0xb4 <main+0x2c>
7
  9e:  89 81         ldd  r24, Y+1  ; 0x01
8
  a0:  9a 81         ldd  r25, Y+2  ; 0x02
9
  a2:  ab 81         ldd  r26, Y+3  ; 0x03
10
  a4:  bc 81         ldd  r27, Y+4  ; 0x04
11
  a6:  01 96         adiw  r24, 0x01  ; 1
12
  a8:  a1 1d         adc  r26, r1
13
  aa:  b1 1d         adc  r27, r1
14
  ac:  89 83         std  Y+1, r24  ; 0x01
15
  ae:  9a 83         std  Y+2, r25  ; 0x02
16
  b0:  ab 83         std  Y+3, r26  ; 0x03
17
  b2:  bc 83         std  Y+4, r27  ; 0x04
18
  b4:  89 81         ldd  r24, Y+1  ; 0x01
19
  b6:  9a 81         ldd  r25, Y+2  ; 0x02
20
  b8:  ab 81         ldd  r26, Y+3  ; 0x03
21
  ba:  bc 81         ldd  r27, Y+4  ; 0x04
22
  bc:  81 38         cpi  r24, 0x81  ; 129
23
  be:  2d e8         ldi  r18, 0x8D  ; 141
24
  c0:  92 07         cpc  r25, r18
25
  c2:  2b e5         ldi  r18, 0x5B  ; 91
26
  c4:  a2 07         cpc  r26, r18
27
  c6:  20 e0         ldi  r18, 0x00  ; 0
28
  c8:  b2 07         cpc  r27, r18
29
  ca:  4c f3         brlt  .-46       ; 0x9e <main+0x16>
30
    {
31
      asm volatile("");
32
    }

von Karl H. (kbuchegg)


Lesenswert?

Renato P. schrieb:
> Malte Bayer schrieb:
>> Ich kann mir gut vorstellen, dass aus
>>
1
for(i=0;i<=255;i++){}
>> etwas ähnlich Schlankes wird (ich weiss es nicht genau, weil ich avr
>> "nur" in assembler programmiere).
>
> Diese Schleife wird denk ich mal ziemlich sicher auch mehr 40 Taktzyklen
> pro Zählererhöhung brauchen(zumindest wenn ichs in meinen obigen
> Codeausschnitt einfüge).

Wie kommst du denn auf diesen Blödsinn?

von Simon K. (simon) Benutzerseite


Lesenswert?

Folgendes Programm
1
#include <avr/io.h>
2
3
int main()
4
{
5
  uint8_t i;
6
7
  for(i=0;i<10;i++)
8
  {
9
    PORTC = i;    
10
  } 
11
12
  return 0;
13
}

übersetzt sich so:
1
00000088 <main>:
2
  88:  80 e0         ldi  r24, 0x00  ; 0
3
  8a:  88 b9         out  0x08, r24  ; 8
4
  8c:  8f 5f         subi  r24, 0xFF  ; 255
5
  8e:  8a 30         cpi  r24, 0x0A  ; 10
6
  90:  e1 f7         brne  .-8        ; 0x8a <main+0x2>
7
  92:  80 e0         ldi  r24, 0x00  ; 0
8
  94:  90 e0         ldi  r25, 0x00  ; 0
9
  96:  08 95         ret

Bei Kommandozeile:
avr-gcc  -mmcu=atmega644 -Wall -std=gnu99 -DF_CPU=1000000UL -Os 
-funsigned-char -funsigned-bitfields -fpack-struct -fshort-enums  -MD 
-MP -MT test.o -MF dep/test.o.d  -c  ../test.c

Ich hab mal die Debugsachen rausgeschmissen, die haben ohnehin keinen 
Sinn mehr ergeben bei so guter Optimierung.

von Simon K. (simon) Benutzerseite


Lesenswert?

Renato P. schrieb:
> Diese Schleife wird denk ich mal ziemlich sicher auch mehr 40 Taktzyklen
> pro Zählererhöhung brauchen(zumindest wenn ichs in meinen obigen
> Codeausschnitt einfüge).

Schalt halt endlich mal den Optimierer ein du Held!

von Renato P. (renato)


Lesenswert?

Danke für die Posts Leute. Hat sehr geholfen :)

Da ich eure AVRGCC-Codes gleich ausprobieren wollte, hab ich jetzt mal 
AVR-Studio heruntergeladen.
Es stimmt, es geht nun 3 mal schneller als vorher.
:D Nur wieso das so ist, hab ich keinen Dunst.

2 eigentlich völlig identische Codes:
1
// AVRGCC mit AVRStudio        //CodeVisionAVR's Compiler
2
#include <avr/io.h>            #include <mega32.h>
3
int main(){                    void main(void){
4
  DDRC=0x01;                     long i;
5
  long i;                        DDRC=0xFF;
6
  while (1){                     while (1){
7
      for(i=0;i<=6000000;i++){}    for(i=0;i<=6000000;i++){}               
8
    if(PINC&0x01)   PORTC=0x00;    if(PINC.0==1) PORTC=0;
9
    else            PORTC=0x01;    else          PORTC=255;
10
  }                              };
11
}                              }
Der linke 4 mal schneller :D

Wenn mir den Grund hierfür auch nochmal einer erklären kann, dann bin 
ich Glücklich ;)

Arbeitet hier überhaupt einer mit CodeVisionAVR? Vielleicht sollt ich ja 
nen umstieg auf AVRStudio wagen.

von Renato P. (renato)


Lesenswert?

Simon K. schrieb:
> Renato P. schrieb:
>> Diese Schleife wird denk ich mal ziemlich sicher auch mehr 40 Taktzyklen
>> pro Zählererhöhung brauchen(zumindest wenn ichs in meinen obigen
>> Codeausschnitt einfüge).
>
> Schalt halt endlich mal den Optimierer ein du Held!

Meine IDE erkennt den Befehl asm volatile(""); nicht.

von Karl H. (kbuchegg)


Lesenswert?

Renato P. schrieb:

> Wenn mir den Grund hierfür auch nochmal einer erklären kann, dann bin
> ich Glücklich ;)

Der wahrscheinliche Grund dafür wurde dir jetzt schon ein paar mal 
gesagt:
Es hängt davon ab, welche Einstellung du für den Optimizer hast.

Je nachdem wie agressiv der Optimizer optimieren darf, kommt 
unterschiedlicher Code heraus.

von Karl H. (kbuchegg)


Lesenswert?

Renato P. schrieb:
> Simon K. schrieb:
>> Renato P. schrieb:
>>> Diese Schleife wird denk ich mal ziemlich sicher auch mehr 40 Taktzyklen
>>> pro Zählererhöhung brauchen(zumindest wenn ichs in meinen obigen
>>> Codeausschnitt einfüge).
>>
>> Schalt halt endlich mal den Optimierer ein du Held!
>
> Meine IDE erkennt den Befehl asm volatile(""); nicht.

Deiner IDE ist das "asm volatile("");" aber sowas von egal.

Dir fehlt es hinten und vorne an Grundlagen und trotzdem stellst du dich 
hier hin und ziehst über Compiler her? Dein Selbstvertrauen möchte ich 
haben! Schon mal eine Karriere in der Poltik oder als Konzernmanager ins 
Auge gefasst?

von sam (Gast)


Lesenswert?

@renato
du solltest versuchen zu begreifen was eine höhere programmiersprache 
ist.
C ist eine höhere Programmiersprache.
Assembler ist eine "niedere"

Assemblerbefehle stehen immer für einen maschinenbefehl, die der 
Prozessor versteht, man spricht schon mit dem Ausdruck, den der 
prozessor versteht ... da müssen dann nur das Wort (MOV RJMP etc) durch 
das byte, welches diesen befehl für den Prozessor darstellt ausgetauscht 
werden ... fertig.

C-Befehle und Programme versteht der Prozessor nicht. Deswegen müssen 
C-Programme erst von einem Compiler übersetzt werden, einige C-Befehle 
werden in eine ganze reihe von maschinenbefehlen übersetzt.
besonders wenn es sich um datentypen handelt, die größer sind als die 
register des Prozessors, braucht der plötzlich viel mehr Anweisungen, 
als man annehmen würde.

die kleinen AVR's haben nur 8bit große Register ... um eine 8 bit zahl 
zu inkrementieren braucht er die zahl blos aus dem ram (wozu er eine 16 
bit adresse in die adressregister schreiben muss etc)  in ein register 
legen um 1 erhöhen und zurück in den ram schreiben ...
einige compiler optimieren aber auch indem sie die 8 bit variable gleich 
im register lassen und gar nicht im ram ablegen.
bei einer 32 oder 64 bit zahl aber wahrscheinlich nicht. denn da braucht 
der prozessor ja 4 oder 8 8bit-register.
deswegen werden um eine 64 bit zahl um eins zu erhöhen erst die 
niedriegsten 8 bit aus dem ram in ein register geschrieben um eins 
erhöht und wieder zurückgeschrieben, gab es nun einen übertrag auf die 
nächsten 8 bit (255 -> 256) dann muss er die nächsten 8 bit aus dem ram 
holen usw.

Wie du siehst artet dann so ein einfaches Inkrement in eine ziemliche 
Arbeit für den Prozessor aus.

Deswegen sollte man schon aus Gründen der effizienten Programmierung auf 
solch große Datentypen verzichten. Damit sind Aufgabengebiete wie das 
bis-6-Millionen-Zählen alles andere als performant.

von Peter D. (peda)


Lesenswert?

Renato P. schrieb:
> Arbeitet hier überhaupt einer mit CodeVisionAVR? Vielleicht sollt ich ja
> nen umstieg auf AVRStudio wagen.

Zumindest erheblich weniger als mit dem AVR-GCC (WINAVR, AVRStudio).

Sachen, die nicht im C-Standard festgelegt sind, behandelt jeder 
Compiler unterschiedlich, z.B. Inline-Assembler, Interrupthandler.
Und die Compileroptionen (Optimierungs-Level) werden warscheinlich auch 
anders eingestellt.

Daher kannst Du die AVR-GCC Tips nicht wörtlich übernehmen, sondern nur 
sinngemäß.

Ich benutze auch den AVR-GCC. Da muß man sich nicht mit irgendwelchem 
Lizenzgeraffel rumärgern, wenn man an mehreren PCs arbeitet.


Peter

von Renato P. (renato)


Lesenswert?

Karl heinz Buchegger schrieb:
> Renato P. schrieb:
>> Simon K. schrieb:
>>> Renato P. schrieb:
>>>> Diese Schleife wird denk ich mal ziemlich sicher auch mehr 40 Taktzyklen
>>>> pro Zählererhöhung brauchen(zumindest wenn ichs in meinen obigen
>>>> Codeausschnitt einfüge).
>>>
>>> Schalt halt endlich mal den Optimierer ein du Held!
>>
>> Meine IDE erkennt den Befehl asm volatile(""); nicht.
>
> Deiner IDE ist das "asm volatile("");" aber sowas von egal.

Mir kommt vor ich hab vorher grad wo gelesen, dass dieses asm 
volatile(""); die optimierung sein soll.
Hab ich mich wohl verlesen oder?

von Renato P. (renato)


Angehängte Dateien:

Lesenswert?

Hab mal wegen Optimierung gesucht.
Ich habs so eingestellt wie auf der Grafik. ändert nix am Speed.

von Karl H. (kbuchegg)


Lesenswert?

Renato P. schrieb:
> Karl heinz Buchegger schrieb:
>> Renato P. schrieb:
>>> Simon K. schrieb:
>>>> Renato P. schrieb:
>>>>> Diese Schleife wird denk ich mal ziemlich sicher auch mehr 40 Taktzyklen
>>>>> pro Zählererhöhung brauchen(zumindest wenn ichs in meinen obigen
>>>>> Codeausschnitt einfüge).
>>>>
>>>> Schalt halt endlich mal den Optimierer ein du Held!
>>>
>>> Meine IDE erkennt den Befehl asm volatile(""); nicht.
>>
>> Deiner IDE ist das "asm volatile("");" aber sowas von egal.
>
> Mir kommt vor ich hab vorher grad wo gelesen, dass dieses asm
> volatile(""); die optimierung sein soll.
> Hab ich mich wohl verlesen oder?

Gaaaaaaaanz von vorne.

Wenn dein Compiler sich einen C-Quellcode vornimmt, dann kann er den 
ziemlich naiv übersetzen.

zb übersetzt er
1
    i = 5;
2
    i = 7;

zu
1
   lade ein Register mit dem Wert 5
2
   Speichere den Reigsterinhalt im Speicher ab, wo die Variable i liegt
3
   lade ein Register mit dem Wert 7
4
   Speichere den Reigsterinhalt im Speicher ab, wo die Variable i liegt

ziemlich offensichtlich, dass der ganze Teil mit laden von 5 und 
speichern unsinnig ist, da diese 5 sowieso mit einer 7 überschrieben 
werden.

Nun, das hilft dem Compiler aber nichts. Der Programmierer hat das so 
geschrieben und dieses Geschriebene ist für den Compiler erst mal 
Gesetz.

Es sei denn der Programmierer erlaubt dem Compiler zu optimieren. Die 
Datenflussanalyse findet raus, dass diese erste Zuweisung für die Katz 
ist und schmeisst sie ganz einfach raus.

Das entstehende Programm ist damit zwar auf Detailebene nicht mehr 
identisch mit dem was der Programmierer geschrieben hat, aber im 
Endeffekt ändert sich nichts. Wenn die komplette Sequenz durchgelaufen 
ist, ist das Endergebnis vom ursprünglichen Endergebnis nicht zu 
unterscheiden.

Und so wie es hier eine ziemlich offensichtiche Optimierung gibt, gibt 
es noch eine Unzahl anderer möglicher Optimierungen die letztendlich 
dazu führen, dass das Programm entweder schneller läuft oder weniger 
Platz benötigt oder beides gleichzeitig. Allen gemeinsam ist allerdings, 
dass das entstehende Programm nicht mehr ein 1:1 Abbild dessen ist, was 
der Programmierer geschrieben hat. Es verhält sich nur noch gleich, aber 
die 1:1 Entsprechung auf Quellcode-Ebene ist verschwunden.


Dazu muss dem Compiler aber erlaubt werden zu optimieren. Und man 
erlaubt ihm dieses, indem man ein Schalter umlegt. Dieser Schalter ist 
ein Commandline-switch oder es findet sich irgendwo in deiner IDE eine 
entsprechende Checkbox oder sonstige Einstellung dafür

von Läubi .. (laeubi) Benutzerseite


Lesenswert?

Peter schrieb:
> schneller wird es kaum gehen, auch wenn es so kaum sinn macht.
Tiny15 und Hardware Timer kann bis zu 16 facher Takt zählen ;)

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.