www.mikrocontroller.net

Forum: Mikrocontroller und Digitale Elektronik C - Laufzeit von Funktionen


Autor: Max Meier (gbl1)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Hallo,

habe hier ein Phänomen welches ich so noch nicht kenne.

Erstmal Code:

uint8_t Encoder_Query_v2()
{   
  encoder_neu = 0;
  encoder_neu |= (((ENCODER_PIN & (1<<ENCODER_PIN_UP))>>ENCODER_PIN_UP) << 1);
  encoder_neu |= (((ENCODER_PIN & (1<<ENCODER_PIN_DOWN))>>ENCODER_PIN_DOWN) << 0);
  

  if (encoder_neu != encoder_old)
  {
     if ((encoder_neu == 0b00000010) && (encoder_old == 0b00000000)) ret=1;   // RIGHT
     if ((encoder_neu == 0b00000011) && (encoder_old == 0b00000010)) ret=1;   // RIGHT
     if ((encoder_neu == 0b00000001) && (encoder_old == 0b00000011)) ret=1;   // RIGHT
     if ((encoder_neu == 0b00000000) && (encoder_old == 0b00000001)) ret=1;   // RIGHT

     if ((encoder_neu == 0b00000001) && (encoder_old == 0b00000000)) ret=2;   // LEFT
     if ((encoder_neu == 0b00000011) && (encoder_old == 0b00000001)) ret=2;   // LEFT
     if ((encoder_neu == 0b00000010) && (encoder_old == 0b00000011)) ret=2;   // LEFT
     if ((encoder_neu == 0b00000000) && (encoder_old == 0b00000010)) ret=2;   // LEFT

  }
  encoder_old = encoder_neu;

  return ret;
}

void TogglePIN(uint8_t inPin)
{   
  PORTD ^= (1<<inPin); 
}


int main(void)
{
 /*
   
 */
 DDRD |= (1<<0);
 
 while (1)        // 2.4MHz on 8MHz Quarz
 {  
    TogglePIN(0);
  
        //  BLOCK1
  uint8_t ret=0;
  ret=Encoder_Query_v2(); 
  
  
  
        //  BLOCK2
  /*
  uint8_t ret=0;
  uint8_t encoder_old=0;
  uint8_t encoder_neu=0;

  encoder_neu = 0;
  encoder_neu |= (((ENCODER_PIN & (1<<ENCODER_PIN_UP))>>ENCODER_PIN_UP) << 1);
  encoder_neu |= (((ENCODER_PIN & (1<<ENCODER_PIN_DOWN))>>ENCODER_PIN_DOWN) << 0);

  if (encoder_neu != encoder_old)
  {
     if ((encoder_neu == 0b00000010) && (encoder_old == 0b00000000)) ret=1;   // RIGHT 
     if ((encoder_neu == 0b00000011) && (encoder_old == 0b00000010)) ret=1;   // RIGHT
     if ((encoder_neu == 0b00000001) && (encoder_old == 0b00000011)) ret=1;   // RIGHT
     if ((encoder_neu == 0b00000000) && (encoder_old == 0b00000001)) ret=1;   // RIGHT

     if ((encoder_neu == 0b00000001) && (encoder_old == 0b00000000)) ret=2;   // LEFT
     if ((encoder_neu == 0b00000011) && (encoder_old == 0b00000001)) ret=2;   // LEFT
     if ((encoder_neu == 0b00000010) && (encoder_old == 0b00000011)) ret=2;   // LEFT
     if ((encoder_neu == 0b00000000) && (encoder_old == 0b00000010)) ret=2;   // LEFT
  }
  encoder_old = encoder_neu;  
  */
 }

 return 0;
}



Die Funktion TogglePIN invertiert ständig einen PIN am PORTD, an diesem 
wiederum ein Frequenzzähler hängt.

Bei auskommentiertem BLOCK2, zeigt der Zähler 158kHz.
Wird hingegen BLOCK1 kommentiert und BLOCK2 verwendet, dann komme ich 
auf 1.5MHz! Also der Faktor 10.

Verwendet wird AVRStudio 4.18.


Was mache ich falsch?


LG
Günter

Autor: asdf (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Günter W. schrieb:
> Verwendet wird AVRStudio 4.18.

Dann starte doch mal den Simulator und messe damit die Zeit vom Beginn 
des Funktionsaufrufs bis zum Ausführen des ersten Befehls in der 
Funktion "Encoder_Query_v2", die Laufzeit in der Funktion und die Zeit 
vom Ende der Funktion bis zum nächsten Befehl im aufrufenden Programm.

Die aufgerufene Funktion muss einige Register auf den Stack sichern und 
anschließend wiederherstellen. Ja nach Taktfrequenz könnte die Zeit 
schon hinkommen.

Welcher Controller ist es denn und wie ist denn die Taktfreuqenz 
gewählt?

Autor: ghjk (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
asdf schrieb:
> Ja nach Taktfrequenz könnte die Zeit
> schon hinkommen.

Und je nach gewählter Optimierungstufe...

Autor: löä# (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
ghjk schrieb:
> asdf schrieb:
>> Ja nach Taktfrequenz könnte die Zeit
>> schon hinkommen.
>
> Und je nach gewählter Optimierungstufe...

...wegen function inlining...

Autor: Mark .. (mork)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Ich glaub das liegt daran, dass bei BLOCK1 Encoder_Query_v2() aufgerufen 
und ret auch tatsächlich anhand der Encoder-Werte berechnet wird, 
während bei BLOCK2 der Compiler erkennt, dass ret nie verwendet wird, 
und deshalb die ganzen if-Abfragen einfach komplett weglässt.

MfG Mark

Autor: Max Meier (gbl1)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
inline kannte ich nicht.
Damit ist es ein bisschen besser geworden.


@Mark:
Habe ein printf("%d",ret) an das Ende gesetzt. Die Laufzeitunterschiede 
bleiben erhalten.


@Allgemeinheit

CPU: ATMEGA16, externer Quarz mit 8MHz

Compilerschalter:
-mmcu=atmega16 -Wall -gdwarf-2 -std=gnu99 -ffunction-sections 
-fdata-sections -fno-tree-loop-optimize -fno-move-loop-invariants 
-DF_CPU=8000000U
L -O3 -funsigned-char -funsigned-bitfields -fpack-struct -fshort-enums 
-MD -MP -


LG Günter

Autor: quadral (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Mark .. schrieb:
> Ich glaub das liegt daran, dass bei BLOCK1 Encoder_Query_v2() aufgerufen
> und ret auch tatsächlich anhand der Encoder-Werte berechnet wird,
> während bei BLOCK2 der Compiler erkennt, dass ret nie verwendet wird,
> und deshalb die ganzen if-Abfragen einfach komplett weglässt.
>
> MfG Mark

Müsste sich anhand der Optimierungsstufe ebenfalls feststellen lassen. 
Einfach mal Abschalten bzw. auf -O0 stellen... Dann führt der Compiler 
den Code ja genau so aus, wie er da steht.

mfg
quadral

Autor: Ralph (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Sieh dir mal den erzeugten Assemblercode der beiden Versionen an.
Dort kannst du sehen was der Compiler, Linker, Optimizer erzeugt haben.
Dann wird dir auch der Unterschied klar werden.

Autor: Max Meier (gbl1)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
So, habe ein bisschen was rausgefunden.
Die eklatanten Laufzeitunterschiede hängen davon ab ob die Funktion im 
Hauptfile deklariert, oder in der .h Datei und zugehörigen .c Datei 
deklariert ist.

encoder.h
void EndoderDummy();


encoder.h
void EndoderDummy()
{
}


main.c
#include <avr/pgmspace.h>
#include <encoder.h>

void Mainfile_Dummy()
{
}

void TogglePIN(uint8_t inPin)
{
  PORTD ^= (1<<inPin);
}

int main(void)
{
  DDRD |= (1<<0);
  while (1)
  {
    TogglePIN(0);
    
    Encoder_Dummy();
    //Mainfile_Dummy();
  }
}


Encoder_Dummy  -> Takt ~800kHz
Mainfile_Dummy  -> Takt ~2MHz

Compilerschalter
-std=gnu99
-fno-tree-loop-optimize
-fdata-sections
-fno-move-loop-invariants
-ffunction-sections
-fdata-sections
-fno-tree-loop-optimize
-fno-move-loop-invariants
-finline-functions
-fjump-tables



LG
Günter

Autor: asdf (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Wenn ich genauer nachrechne, kann 1,5-2 MHz bei tatsächlicher Ausführung 
des Codes auf einem AVR selbst bei 20 MHz kaum hinkommen. Am erzeugten 
Assemblercode sollte das eindeutig zu erkennen sein.

Um die Optimierung ohne Änderung der Compileroptionen zu vermdeiden 
müsste das Ergebnis entweder TATSÄCHLICH verwendet werden (das printf 
nach dem while(1) wird nie erreicht, das "sieht" auch der Compiler).

Einfach wäre es testweise die Deklaration in main in "volatile uint8_t 
ret=0;" zu ändern. Das teilt dem Compiler dass bei dieser Variable keine 
Optimierungen durchführen darf und auch das Ergebnis jeder Zuweisung in 
den Speicher schreiben muss.

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.