Forum: Compiler & IDEs Funktionsaufruf aus Interruptroutine OK?


von Thomas (Gast)


Lesenswert?

ganz wohl fühle ich mich ja nicht dabei dort Code aufzurufen, auf den 
ich keinen Einfluss habe.

1
SIGNAL (SIG_OUTPUT_COMPARE1A)
2
{
3
  OCR1A += pgm_read_word(&table[second/UPDATE_RATE]);
4
}

von Andreas K. (a-k)


Lesenswert?

Ist völlig ok.

Zudem rufst du damit nicht wirklich eine Funktion auf. Diese "Funktion" 
ist bloss inline-code.

von Falk B. (falk)


Lesenswert?

@ Thomas (Gast)

>ganz wohl fühle ich mich ja nicht dabei dort Code aufzurufen, auf den
>ich keinen Einfluss habe.


>  OCR1A += pgm_read_word(&table[second/UPDATE_RATE]);

Tststst, ein böse Division. Das sollte man meiden, erst recht in einem 
Interrupt.

MFG
Falk

von Andreas K. (a-k)


Lesenswert?

Oder Zweierpotenz als UPDATE_RATE verwenden.

von Thomas (Gast)


Lesenswert?

Hallo, und danke für die Antworten.

Was ist denn "inline-code"?

Ist das mit der Division wirklich so schlimm? Das Ergebnis ist ja auch 
wieder ein Integer, also ohne Nachkommastelle.

Gut, ich könnte mein Programm natürlich folgendermaßen abändern:

1
SIGNAL (SIG_OUTPUT_COMPARE1B)
2
{
3
  if( --prescaler == 0 ){ 
4
    prescaler = (uint8_t) DEBOUNCE;
5
    second++;      // exact one second over
6
    second_++;   // zus. Variable nötig
7
    if (second_ == UPDATE_RATE) //zus. IF-Abfrage
8
    {
9
      second_ = 0;
10
      i+=1;
11
    }
12
  }  
13
#if XTAL % DEBOUNCE
14
  if (prescaler <= XTAL % DEBOUNCE) {
15
    OCR1B += XTAL / DEBOUNCE +1;   /* um 1 Takt längere Periode um 
16
              den Rest abzutragen */
17
  } else {
18
#endif
19
    OCR1B += XTAL / DEBOUNCE;   /* kurze Periode */
20
#if XTAL % DEBOUNCE    
21
  }
22
#endif
23
}



1
SIGNAL (SIG_OUTPUT_COMPARE1A)
2
{
3
  OCR1A += pgm_read_word(&table[i]);  //division entfällt
4
}

aber ob das so viel effizienter ist...

von Falk B. (falk)


Lesenswert?

@  Thomas (Gast)

>Was ist denn "inline-code"?

Code, der direkt an Stelle des Funktionsaufrufs eingefügt wird, ohne 
Call & return. Ist schneller.

>Ist das mit der Division wirklich so schlimm?

Jain.

> Das Ergebnis ist ja auch
>wieder ein Integer, also ohne Nachkommastelle.

Ist egal. So eine Division dauert je nach Wortbreite 50..500 Takte.

>aber ob das so viel effizienter ist...

Ist es. Und wie du siehst geht es gut ohne Division.

MFG
Falk

von Rolf Magnus (Gast)


Lesenswert?

> Was ist denn "inline-code"?

Kurze Funktionen und Makros, die nicht zu einem "klassischen" 
Funktionsaufruf auf Assembler-Ebene führen, sondern deren Code direkt 
dort eingesetzt wird, wo sie in C aufgerufen wurden. Der Overhead für 
den Aufruf ist damit komplett weg.

> Ist das mit der Division wirklich so schlimm?

Das pgm_read_word, um das du dir Sorgen machst, mutiert im Prinzip zu 
gerade mal zwei Assembler-Instruktionen. Die Division dagegen wird zu 
einem richtigen Funktionsaufruf einer mehr oder weniger aufwendigen 
Funktion, da AVRs keine Division in Hardware durchführen können.

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

Rolf Magnus wrote:

> ... Die Division dagegen wird zu
> einem richtigen Funktionsaufruf einer mehr oder weniger aufwendigen
> Funktion, da AVRs keine Division in Hardware durchführen können.

...es sei denn, UPDATE_RATE ist eine zur Compilezeit bekannte und
konstante Zweierpotenz, dann sollte der Compiler das durch eine
Schiebeoperation ersetzen.  Allerdings kann der AVR immer nur ein
Bit mit einem Befehl schieben, ein Schieben um viele Bits ist also
auch Aufwand.

von Thomas (Gast)


Lesenswert?

OK, das mit dem Inline-Code fand ich interessant und hab mal ein weinig 
experimentiert:

1
#define swp(type,a,b) {type tmp; tmp=a; a=b; a=tmp;}
2
...
3
int x=2;
4
int y=1;
5
swp(int,x,y);
6
swp(int,x,y);
7
{
8
  int x=3;
9
  swp(int,x,y)
10
}
11
{
12
  int x=3;
13
  float y =2.7;
14
  swp(int,x,y)
15
}


sehe ich das richtig, dass man überall einfach Codefragmente mit 
geschweiften Klammeren umgeben kann, um temporäre Variablen direkt 
wieder frei zu geben?
Ist das auch sinnvoll?
Variablen außerhalb mit gleichem Namen sind dann innen nicht sichtbar?
Wieso gibt der beim letzterem swp noch nicht einmal eine Warnung aus?
Was passiert in diesem Fall?

Gruß,
Thomas

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

Thomas wrote:

> sehe ich das richtig, dass man überall einfach Codefragmente mit
> geschweiften Klammeren umgeben kann, um temporäre Variablen direkt
> wieder frei zu geben?

Das siehst du falsch.  Erstens legt der GCC die Stackframes immer für
die gesamte Lebensdauer der Funktion an, es wird also für
geschachtelte Blöcke weder innerhalb der Funktion neuer Platz
alloziert noch welcher freigegeben.

Das betrifft aber praktisch nur große Variablen oder aber den Fall
ausgeschalteter Optimierung.  Kleine Variablen (bzw. daraus berechnete
Teilausdrücke) werden bei eingeschalteter Optimierung in Registern
abgelegt, und der Compiler weiß genau, wie lange er ein Register für
einen bestimmten Wert benötigt und wann es wieder für eine andere
Aufgabe zur Verfügung steht.

> Ist das auch sinnvoll?

Kommt drauf an.  Es kann die Übersichtlichkeit bei sehr langen
Funktionen erhöhen.  Alternativ kannst du aber auch aus dem
geschachtelten Block gleich eine weitere Funktion (mit dem Scope
"static") machen.  Wenn sie im gesamten Programmlauf nur einmal
benötigt wird, entsteht daraus kein Funktionsaufruf, sondern es ist
immer einfacher (mindestens durch das Einsparen von CALL und RET),
dass der Compiler sie dann gleich inline in den Aufrufer hinein
compiliert.

> Variablen außerhalb mit gleichem Namen sind dann innen nicht
> sichtbar?

Ja.

> Wieso gibt der beim letzterem swp noch nicht einmal eine Warnung
> aus?

Was hättest du denn erwartet?  float und int sind gegenseitig
zuweisungkompatibel.

> Was passiert in diesem Fall?

Bei int -> float wird eine Gleitkommazahl erzeugt, die so nahe wie
möglich der int-Zahl entspricht.  (Naja, so ungefähr, den Gesetzestext
dazu müsste ich erst suchen.)  Bei float -> int wird der ganzzahlige
Anteil der Gleitkommazahl benutzt.

Code generieren deine swp()s bei eingeschalteter Optimierung gar
keinen, da sie allesamt nichts wirklich ändern.

von Thomas (Gast)


Lesenswert?

>>aber ob das so viel effizienter ist...

>Ist es. Und wie du siehst geht es gut ohne Division.

Gut, aber außer dass diese Lösung noch zusätzlich zwei dauerhafte 
Variablen (der AVR hat nur 32 Register??) und eine if-Abfrage braucht 
habe ich damit vor allem ein Problem:

Der Code wird absolut unleserlich.
Die Funktion der ersten Lösung hingegen ist selbsterklärend und 
erschließt sich auf dem ersten Blick.

Ich werde das jetzt wohl mit einer Zweierpotenz machen, wie bereits 
vorgeschlagen. Wie oft jetzt genau die damit erzeugte Frequenz angepasst 
wird ist eigentlich nicht so wichtig. Das wird was von 4 bis 32 Sekunden 
werden. Damit dürfte das so auch die schnellste Lösung sein, wenn der 
Compiler das wirklich optimiert.

Gruß,
Thomas

von Thomas (Gast)


Lesenswert?

Wow, danke für die ausführliche Antwort, Jörg!

>Was hättest du denn erwartet?  float und int sind gegenseitig
>zuweisungkompatibel.

Eine Warnung hätte ich schon erwartet, hätte man ja per Compileroption 
abschaltbar machen können.
Ich dachte eigentlich immer, dass man für sowas casten müsste...

>Bei float -> int wird der ganzzahlige Anteil der Gleitkommazahl benutzt.

Also ohne zu runden, gut zu wissen.

>Code generieren deine swp()s bei eingeschalteter Optimierung gar
>keinen, da sie allesamt nichts wirklich ändern.

Interessant, habe ich noch gar nicht drüber nachgedacht. Du meinst also, 
der Inhalt der Speicherbereiche wird gar nicht vertauscht, sondern nur 
die Zuordnung?

Gruß,
Thomas

von Falk B. (falk)


Lesenswert?

@ Thomas (Gast)

>Gut, aber außer dass diese Lösung noch zusätzlich zwei dauerhafte
>Variablen (der AVR hat nur 32 Register??)

und noch einges an SRAM . .

AVR-Tutorial: Speicher

> und eine if-Abfrage braucht
>habe ich damit vor allem ein Problem:

>Der Code wird absolut unleserlich.

Bitte?

>Die Funktion der ersten Lösung hingegen ist selbsterklärend und
>erschließt sich auf dem ersten Blick.

Du hast komische Logik.

>Ich werde das jetzt wohl mit einer Zweierpotenz machen, wie bereits
>vorgeschlagen. Wie oft jetzt genau die damit erzeugte Frequenz angepasst
>wird ist eigentlich nicht so wichtig. Das wird was von 4 bis 32 Sekunden
>werden. Damit dürfte das so auch die schnellste Lösung sein, wenn der
>Compiler das wirklich optimiert.

Hoffen wir das mal.

MFG
Falk

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

Thomas wrote:

>>Code generieren deine swp()s bei eingeschalteter Optimierung gar
>>keinen, da sie allesamt nichts wirklich ändern.

> Interessant, habe ich noch gar nicht drüber nachgedacht. Du meinst also,
> der Inhalt der Speicherbereiche wird gar nicht vertauscht, sondern nur
> die Zuordnung?

Das wäre eine Möglichkeit.  Aber so, wie du das da oben aufgeschrieben
hast, wird der Compiler sämtlichen Code der entsprechenden C-Zeilen
wegwerfen, da die Berechnung der Ergebnisse nutzlos ist, denn sie
werden am Ende gar nicht verwendet.

von Thomas (Gast)


Lesenswert?

>denn sie werden am Ende gar nicht verwendet.

ach, sooo meinst du das...
klar, das war ja auch nur ein Experiment für den inline-Code ohne jede 
Relevanz.
Ich wollte nur mal sehen, ob der Compiler das auch so schluckt.
Dann habe ich mich gefragt, was passiert wenn man das zweimal aufruft, 
also zwei mal eine tmp Instanz erzeugt wird, und so weiter....

von Thomas (Gast)


Lesenswert?

>Du hast komische Logik.

finde ich gar nicht, in der ersten Lösung sind alle nötigen 
Informationen zum Verstehen der Funktionalität in einer einzigen Zeile 
zusammengefasst, und zwar nur da wo sie auch benötigt werden.
Durch die klingenden Namen wird auch sofort klar, was da passiert.

bei der zweiten Lösung muss man sich erstmal durch den Code wühlen und 
alle Variablen zusammentragen.
Außerdem taucht da gezwungenermaßen Code an Stellen auf, wo sich dessen 
Funktion noch gar nicht einordnen lässt. Sowas liest sich immer schwer, 
weil man sich nicht mehr aufs Wesentliche konzentriert.

von Andreas K. (a-k)


Lesenswert?

Das hängt auch ein bischen von der Qualität des Compilers ab. Gut 
optimierende Exemplare suchen den Code daraufhin ab, in welchen Teilen 
welche Variablen verwendet werden und wie oft. Daraus wird dann die 
Optimierung der Registerbelegung abgeleitet. Folglich ist es bei solchen 
Compilern nicht allzu wichtig, ihnen das Leben durch eigene Klarstellung 
der Lebensdauer zu erleichtern.

von Gisbert (Gast)


Lesenswert?

Gehört der GCC dazu ?

von Andreas K. (a-k)


Lesenswert?

Ja.

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.