Forum: Mikrocontroller und Digitale Elektronik C - Laufzeit von Funktionen


von Max M. (gbl1)


Lesenswert?

Hallo,

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

Erstmal Code:
1
uint8_t Encoder_Query_v2()
2
{   
3
  encoder_neu = 0;
4
  encoder_neu |= (((ENCODER_PIN & (1<<ENCODER_PIN_UP))>>ENCODER_PIN_UP) << 1);
5
  encoder_neu |= (((ENCODER_PIN & (1<<ENCODER_PIN_DOWN))>>ENCODER_PIN_DOWN) << 0);
6
  
7
8
  if (encoder_neu != encoder_old)
9
  {
10
     if ((encoder_neu == 0b00000010) && (encoder_old == 0b00000000)) ret=1;   // RIGHT
11
     if ((encoder_neu == 0b00000011) && (encoder_old == 0b00000010)) ret=1;   // RIGHT
12
     if ((encoder_neu == 0b00000001) && (encoder_old == 0b00000011)) ret=1;   // RIGHT
13
     if ((encoder_neu == 0b00000000) && (encoder_old == 0b00000001)) ret=1;   // RIGHT
14
15
     if ((encoder_neu == 0b00000001) && (encoder_old == 0b00000000)) ret=2;   // LEFT
16
     if ((encoder_neu == 0b00000011) && (encoder_old == 0b00000001)) ret=2;   // LEFT
17
     if ((encoder_neu == 0b00000010) && (encoder_old == 0b00000011)) ret=2;   // LEFT
18
     if ((encoder_neu == 0b00000000) && (encoder_old == 0b00000010)) ret=2;   // LEFT
19
20
  }
21
  encoder_old = encoder_neu;
22
23
  return ret;
24
}
25
26
void TogglePIN(uint8_t inPin)
27
{   
28
  PORTD ^= (1<<inPin); 
29
}
30
31
32
int main(void)
33
{
34
 /*
35
   
36
 */
37
 DDRD |= (1<<0);
38
 
39
 while (1)        // 2.4MHz on 8MHz Quarz
40
 {  
41
    TogglePIN(0);
42
  
43
        //  BLOCK1
44
  uint8_t ret=0;
45
  ret=Encoder_Query_v2(); 
46
  
47
  
48
  
49
        //  BLOCK2
50
  /*
51
  uint8_t ret=0;
52
  uint8_t encoder_old=0;
53
  uint8_t encoder_neu=0;
54
55
  encoder_neu = 0;
56
  encoder_neu |= (((ENCODER_PIN & (1<<ENCODER_PIN_UP))>>ENCODER_PIN_UP) << 1);
57
  encoder_neu |= (((ENCODER_PIN & (1<<ENCODER_PIN_DOWN))>>ENCODER_PIN_DOWN) << 0);
58
59
  if (encoder_neu != encoder_old)
60
  {
61
     if ((encoder_neu == 0b00000010) && (encoder_old == 0b00000000)) ret=1;   // RIGHT 
62
     if ((encoder_neu == 0b00000011) && (encoder_old == 0b00000010)) ret=1;   // RIGHT
63
     if ((encoder_neu == 0b00000001) && (encoder_old == 0b00000011)) ret=1;   // RIGHT
64
     if ((encoder_neu == 0b00000000) && (encoder_old == 0b00000001)) ret=1;   // RIGHT
65
66
     if ((encoder_neu == 0b00000001) && (encoder_old == 0b00000000)) ret=2;   // LEFT
67
     if ((encoder_neu == 0b00000011) && (encoder_old == 0b00000001)) ret=2;   // LEFT
68
     if ((encoder_neu == 0b00000010) && (encoder_old == 0b00000011)) ret=2;   // LEFT
69
     if ((encoder_neu == 0b00000000) && (encoder_old == 0b00000010)) ret=2;   // LEFT
70
  }
71
  encoder_old = encoder_neu;  
72
  */
73
 }
74
75
 return 0;
76
}



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

von asdf (Gast)


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?

von ghjk (Gast)


Lesenswert?

asdf schrieb:
> Ja nach Taktfrequenz könnte die Zeit
> schon hinkommen.

Und je nach gewählter Optimierungstufe...

von löä# (Gast)


Lesenswert?

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

...wegen function inlining...

von Mark .. (mork)


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

von Max M. (gbl1)


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

von quadral (Gast)


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

von Ralph (Gast)


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.

von Max M. (gbl1)


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
1
void EndoderDummy();


encoder.h
1
void EndoderDummy()
2
{
3
}


main.c
1
#include <avr/pgmspace.h>
2
#include <encoder.h>
3
4
void Mainfile_Dummy()
5
{
6
}
7
8
void TogglePIN(uint8_t inPin)
9
{
10
  PORTD ^= (1<<inPin);
11
}
12
13
int main(void)
14
{
15
  DDRD |= (1<<0);
16
  while (1)
17
  {
18
    TogglePIN(0);
19
    
20
    Encoder_Dummy();
21
    //Mainfile_Dummy();
22
  }
23
}


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

von asdf (Gast)


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.

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.