C - Laufzeit von Funktionen

OP #2049423
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
Gast #2049436
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?
#2049464
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
OP #2049471
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
Gast #2049491
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
OP #2049821
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
Gast #2049880
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

Bitte melde dich an, um einen Beitrag zu schreiben.

oder

Mit Google-Account einloggen

Die Registrierung ist kostenlos und dauert nur eine Minute.

Jetzt registrieren