wenn sind lokale oder globale Variablen besser? Optimierungen?

Hallo,

ich bin gerade auf dem Optimierungstrip. :slight_smile:

Gut. Lokale Variablen machen den Code aufgeräumter und sicher vor Veränderungen dieser, außerhalb ihrer Funktion in der sie stehen. Und im Deklarationsabschnitt steht weniger "Müll".

Und scheinbar verbraucht der Sketch weniger RAM. Nachdem was im IDE Fenster unten angezeigt wird. Ich betone scheinbar. Weil ...

void Funktionsname ()
{ 
  ...
  int VALUE = analogRead(A0);
  ...
}

die Variable VALUE wird doch sowieso ständig neu erzeugt wenn die Funktion aufgerufen wird. Also spart man doch im Grunde genommen keinen RAM. Also kann man die Variable doch gleich global deklarieren. Das müßte laut meiner Meinung sogar schneller gehen, weil die Variable schon vorhanden ist und nicht erst jedenmal neu erzeugt werden muß.

Meinungen?

hi,

jaaaa, aber…

sie wird ja auch wieder entsorgt, und auf diesem platz kann dann eine andere lokale variable der nächsten funktion stehen.

gruß stefan

Das müßte laut meiner Meinung sogar schneller gehen, weil die Variable schon vorhanden ist und nicht erst jedenmal neu erzeugt werden muß.

Da wird nix erzeugt.
Intern gibts für jede Variable einen Zeiger
Der Stackpointer wird um sizeof(var) erhöht.
Der Var Zeiger zeigt auf diesen Bereich.

Das "schneller gehen" beschränkt sich maximal auf eine Addition und 2 Zuweisungen.
Und das auch nur, wenn die Variable zu groß für die Register ist.
Denn der Compiler hätte sie lieber im Register, als auf dem Stack.

Kleinere Mengen von lokalen Variablen werden sicher in Registern platziert, so wie die Funktionsparameter.
Die nehmen also nicht mal irgendwelchen RAM ein.

Hallo,

hmmm, noch irgendwie verwirrender.

Wenn eine lokale Variable irgendwann verworfen wird, muß sie doch später erneut erzeugt werden? Oder legt der Compiler auch für jede lokale Variablen einen Zeiger im Stackpointer an? Würde aber keinen Sinn machen, dann würde es ja nicht heißen, lokale werden am Lebensende verworfen.

a) wenige lokale Variablen nehmen zeitgleich keinen Platz im RAM weg, weil sie in Registern liegen
Nur liegen Register nicht im RAM?

b) das anlegen einer lokalen Variable kostet Zeit in Form einer Addition + 2 Zuweisungen
okay, klare Aussage

c) lokale Variablen werden irgendwann wieder verworfen
eigentlich klare Aussage, dachte ich auch so, nur jetzt siehe oben verwirrt mich das.
Werden die nun gelöscht oder bleiben die auf irgendeinen Stackpointer mit Zeiger?

Wenn eine Funktion aufgerufen wird, wird ein Stackframe angelegt.

In diesem Rahmen landet:

  1. die Rücksprungadresse,
  2. die Register, welche gesichert werden müssen,
  3. Übergabevariablen welche nicht in die Register passen.
  4. lokale Variablen, welche nicht in die Register passen.

Der Stackframe wird beim verlassen der Funktion wieder vollständig abgebaut.

Nur liegen Register nicht im RAM?

Unterhalb des Rams, da wo auch die Port/ADC/Timer usw. Register stecken.
Siehe Speicherlayout im Datenblatt
http://www.atmel.com/images/atmel-8271-8-bit-avr-microcontroller-atmega48a-48pa-88a-88pa-168a-168pa-328-328p_datasheet_complete.pdf
Figure 8-3 Seite 19

Wenn eine lokale Variable irgendwann verworfen wird, muß sie doch später erneut erzeugt werden?

Da wird nichts erzeugt!
Das ist keine Geburt nach langer Schwangerschaft und keine Hausbaustelle...
Es wird einfach nur ein Örtchen auf dem Stack reserviert.(bis zum Ende der Funktion)

Der Aufwand für das Reservieren der Variablen und Parameter findet beim Übersetzen statt, nicht während der Ausführung.

hi,

ob Du es jetzt "erzeugen" oder platz reservieren nennst, ist jacke wie hose...

es muß zumindest berechnet werden, wie groß der frame ist, und da haben wir die addition, die nicht in null zeit abgeht.
außerdem muß der zeiger "erzeugt" werden, kann auch nicht umsonst sein.
der zeiger muß auch noch mit der adresse beschrieben werden.

klar braucht das zeit. ob das jetzt was ausmacht, kommt auf's programm an.
aber nur sagen: LOKALE SIND GUT, nur weil es einer (momentanen) programmierkonvention entspricht oder meinetwegen sogar einem (momentanen) paradigma, naja...

wenn man in bereiche vordringt, wo das wichtig ist, landet man sowieso bei assembler, und davon sind wir alle weit weg...

gruß stefan

Der Aufwand für das Reservieren der Variablen und Parameter findet beim Übersetzen statt, nicht während der Ausführung.

wie das ??? Du meinst jetzt wohl globale variablen. bei lokalen geht das eben nicht.

Sicher meine ich das für die lokalen.

Der Stackframe muss für jede Funktion (die einen benutzt) aufgebaut werden, nur die 'Dicke' ändert sich durch die lokalen Variablen unter Umständen (wenn nicht in Registern).

Doc_Arduino:
Werden die nun gelöscht oder bleiben die auf irgendeinen Stackpointer mit Zeiger?

Mach dich mal schlau darüber wie der Stack/Stapelspeicher funktioniert. Darum musst dich zwar in Hochsprachen nicht direkt kümmern, aber du hast trotzdem ständig damit zu tun und es ist immer noch eine Fehlerquelle.

Zitat von WP:

Bei den meisten Prozessoren beginnt der Stapel bei einer hohen Adresse und wird in Richtung der Adresse 0 gestapelt.

Das ist auch auf dem AVR der Fall:

Der Stack beginnt auf am oberen Ende. Am unteren Ende steht erst das .data Segment (globale und statische Variablen die initialisiert werden müssen), das .bbs Segment(globale und statische nicht-initialisierte Variablen) und dann der Heap (Variablen die dynamisch mit malloc()/new) erzeugt werden.)

Wenn du dann so viel Speicher verwendest dass der Heap mit Stack kollidiert knallt es.

So berechnet man übrigens auch wie viel RAM noch frei ist. Indem man die Differenz von Stack-Ende und Heap-Ende ausrechnet.

Eine andere Sache mit lokalen Variablen ist die Sichtbarkeit. Man ist normal darauf bedacht den globalen Namensraum nicht unnötig mit Variablen zuzumüllen. Globale Variablen sind überall sichtbar und können überall verändert werden!

Jede Variable hat eine Adresse, die entweder fest ist (global) und vom Linker ermittelt wird, oder bei lokalen Variablen einen Offset relativ zum Stackframe/-pointer darstellt, der vom Compiler ermittelt wird. Der Speicher für lokale Variablen wird einfach als Block reserviert, indem der Stackpointer am Eintritt in die Funktion erniedrigt wird. Eine dynamische Belegung kommt nur in Verbindung mit Pointer-Variablen vor, dann wird der Speicher auf dem Heap reserviert und die Adresse im Pointer gespeichert.

Lokale Variable können beliebig oft angelegt werden, mit jedem verschachtelten Aufruf eines Unterprogramms. Dann hat jede dieser Variablen den gleichen Offset zum Stackpointer, aber eine unterschiedliche absolute Adresse.

Zur Laufzeit sind Pointer am ungünstigsten, dazu muß (im AT... Prozessor) der Pointer ins X, Y oder Z-Register geladen werden, bevor ein Zugriff erfolgen kann. Globale Variablen können direkt geladen werden. Lokale Variablen können je nach Verwendung in Registern gehalten werden, mit besonders schnellem Zugriff, oder im Stack mit Zugriff relativ zum Stackpointer-Register. Was mich dabei etwas verwirrt: es gibt keine Adressierungsart relativ zum Stackpointer? Dann wird wohl eines der Pointer-Register (X,Y,Z) als Framepointer benutzt, d.h. als Pointer auf den Anfang des Blocks der lokalen Variablen.

hi,

Sicher meine ich das für die lokalen.

Der Stackframe muss für jede Funktion (die einen benutzt) aufgebaut werden, nur die 'Dicke' ändert sich durch die lokalen Variablen unter Umständen (wenn nicht in Registern).

wie sollte für eine funktion kein stackframe benutzt werden? zumindest die rücksprungadresse muß auf den stack.
und die erforderliche dicke muß berechnet werden. würde das bei der kompilierung berechnet, müßte dieser wert über die gesamte laufzeit mitgezogen werden, was wiederum RAM benötigen würde...

übrigens: ich bin kein gegner von lokalen variablen, benutze sie ja auch. aber globale sind nach meiner meinung (bin ja kein fachmann, aber das sind manche andere auch nicht) halt schneller.

und ob man lieber so oder so programmiert, sollte nicht dogmatisch beantwortet werden, alles hat vor- und nachteile.

gruß stefan

Eisebaer:
wie sollte für eine funktion kein stackframe benutzt werden? zumindest die rücksprungadresse muß auf den stack.

Inline Funktionen benötigen die nicht.

Ah, this is obviously some strange usage of the word 'function' that I wasn't previously aware of. (Eisebaer)

Na gut, es wird ein minimaler Overhead für die lokale Variable erzeugt, aber man
greift ja normalerweise nicht nur einmal drauf zu sondern 10, 20 oder x mal.
D.h. der Overhead ist einmal da beim Eintritt in die Funktion (Methode, Prozedur) und
dann nicht mehr, sollte also verschwindend gering sein.
Klar, wenn man einen Funktion 1000000 mal aufruft und dann an einer entscheidenden
Stelle eine lokale gegen eine globale Variable austauscht so wird das messbar sein.
Aber dann befinden wir und in Bereichen wo, wie einer meiner Vorposter schon schrieb, es
langsam Zeit wird sich über Assembler Gedanken zu machen.

Ulli

6.39 An Inline Function is As Fast As a Macro

By declaring a function inline, you can direct GCC to make calls to that function faster. One way GCC can achieve this is to integrate that function's code into the code for its callers. This makes execution faster by eliminating the function-call overhead; in addition, if any of the actual argument values are constant, their known values may permit simplifications at compile time so that not all of the inline function's code needs to be included. The effect on code size is less predictable; object code may be larger or smaller with function inlining, depending on the particular case. You can also direct GCC to try to integrate all “simple enough” functions into their callers with the option -finline-functions.

https://gcc.gnu.org/onlinedocs/gcc-4.8.5/gcc/Inline.html#Inline

Eisebaer:
Ah, this is obviously some strange usage of the word 'function' that I wasn't previously aware of. (Eisebaer)

Das ist das Schöne hier, man lernt noch jeden Tag was Neues, was man bisher nie gebraucht hat.

hi,

whandall, ich wollte Dir nicht widersprechen, Du hast schon recht mit der inline-funktion.

ich wollte eigentlich nur sagen, daß - sagen wir mal so: wieviele leute hier haben schon mal eine inline-funktion für den arduino geschrieben?

und Deine signatur, naja, die hat halt wie die faust auf's auge gepaßt. :slight_smile:

gruß stefan

@Eisebaer: Hätte mich auch sehr gewundert, wenn dir das neu gewesen wäre.

Grüße, Sven. :wink:

Klar, wenn man einen Funktion 1000000 mal aufruft und dann an einer entscheidenden
Stelle eine lokale gegen eine globale Variable austauscht so wird das messbar sein.

Schreibt nun mal jemand einen kurzen Benchmark und überprüft und quantifiziert den Unterschied, oder muss ich das machen?

Beste Grüße,

Helmuth