Tag, ich habe hier ein Programm, bei dem mir etwas aufgefallen ist. Da
sind 4 Funktionen drin. In jeder Funktion wird eine lokale temp Variable
angelegt. Ich habe gedacht, eine globale Variable wäre hier günstige,
aber der erzeugte Code ist deutlich größer.
Wie kommt das?
G.
Kann man so allgemein nicht sagen.
IdR sind lokale auto-Variablen günstiger, ja. Aber wenn es zu viele
werden, dann muss gcc einen Frame für die ganzen lokalen Variablen
anlegen, und das kostet Code (und Laufzeit).
Ausserdem ist es ungünstig, auto-Variablen anzulegen, die nicht in
Registern gehalten werden können. Das sind zB Variablen mit "krummer"
Größe.
Ebenfalls nicht prickelnd ist es, wenn man die Adresse einer
auto-Variablen braucht und der Compiler die Adressberechnung nicht
wegoptimieren kann.
Bei globalen Variablen kann es günstig sein, sie in temporäre lokale
Variablen zu kopieren um die Manipulationen zu machen.
Aber es ist immer abhängig vom Compiler, vom Code und der
Zielarchitektur was "gut" und was "schlecht" ist. Nicht alles wird vom
Compiler optimal umgesetzt; den optimalen Compiler gibt es eben noch
nicht.
avr-gcc, hatte ich vergessen zu erwähnen.
Hm, stimmt, so ne temporäre Variable im Register wäre nicht verkehrt.
Wählt der Compiler automatisch ein entsprechendes Register?
Und wie ist das mit Interrupts und lokalen Variablen? Geht das
problemlos?
G. wrote:
> Hm, stimmt, so ne temporäre Variable im Register wäre nicht verkehrt.> Wählt der Compiler automatisch ein entsprechendes Register?
Ja und nein. Mit dem 'register'-Schlüsselwort kannst du dem Compiler
empfehlen, die Variable in einem Register zu halten. Ob er das dann
auch macht, und wenn ja, welches Register er benutzt, bleibt ihm ganz
alleine überlassen.
Beim GCC kann man mit dem ASM-Dingen eine Variable fest an ein
selbstgewähltes Register binden, näheres dazu im Tutorial.
> Und wie ist das mit Interrupts und lokalen Variablen? Geht das> problemlos?
Ja, geht. Die liegen ja im Frame des Aufrufs, und das wird für jeden
Aufruf neu angelegt und danach wieder freigegeben.
G. wrote:
> Und wie ist das mit Interrupts und lokalen Variablen? Geht das> problemlos?
Das kommt darauf an, was du machen willst; wenn sich die Tätigkeit der
Interrupt-Funktion nur lokal auswirkt, sind lokale Variablen das beste.
Wenn Du allerdings Ergebnisse nach "draußen" exportieren willst (wie
z.B. empfangene UART-Zeichen oder Zähler-Erhöhungen oder im Interrupt
gemessene Spannungen), verwendest Du globale Variablen. Dabei dann ggf.
"volatile" nicht vergessen!
Sven Pauli schrob:
> G. wrote:>> Hm, stimmt, so ne temporäre Variable im Register wäre nicht verkehrt.>> Wählt der Compiler automatisch ein entsprechendes Register?> Ja und nein. Mit dem 'register'-Schlüsselwort kannst du dem Compiler> empfehlen, die Variable in einem Register zu halten. Ob er das dann> auch macht, und wenn ja, welches Register er benutzt, bleibt ihm ganz> alleine überlassen.register und atuo haben heutzutage keinen Effekt mehr, zumindest
nicht be gcc. Der weiß was gut ist... Lokale Variablen versucht er in
GPRs zu halten.
> Beim GCC kann man mit dem ASM-Dingen eine Variable fest an ein> selbstgewähltes Register binden, näheres dazu im Tutorial.
Wovon aber abzuraten ist, wenn man nicht genau weiß was das für
Nebeneffekte hat bzw. haben kann!
Günter R. schrob:
> Wenn Du allerdings Ergebnisse nach "draußen" exportieren willst (wie> z.B. empfangene UART-Zeichen oder Zähler-Erhöhungen oder im Interrupt> gemessene Spannungen), verwendest Du globale Variablen. Dabei dann ggf.> "volatile" nicht vergessen!
...und daß der Zugriff ggf. atomar sein muß um Race-Conditions etc. zu
vermeiden.
Der Ersteller dieses Wikis sollte mal ein Rechtschreibprogramm über
seine Texte laufen lassen ...
Kritik angekommen und akzeptiert und Text in Bearbeitung.
G. schrob:
>http://www.wiki.elektronik-projekt.de/mikrocontrol...
>> Da steht alles
...ganz falsch.
Günter R. schrieb:
>> Der Ersteller dieses Wikis sollte mal ein Rechtschreibprogramm über>> seine Texte laufen lassen ...
kater schrieb:
> Kritik angekommen und akzeptiert und Text in Bearbeitung.
Ojeojemineoje bei so vielen sachlichen und inhaltlichen Fehlern lohnt es
sich echt nicht, die Orthografie aufzupolieren!
Am besten ganz in die Tonne kloppen!
>> Da steht alles>...ganz falsch.>Ojeojemineoje bei so vielen sachlichen und inhaltlichen Fehlern
Erklärst du uns auch was da so falsch ist, von den offensichtlichen
Rechtschreibfehlern abgesehen?
Schließlich stammt der Inhalt aus einer AppNote von Atmel. Es kann
höchstens sein das es nicht genau so auf den avr-gcc zutrifft.
Wenn sich Übersetzungsfehler eingeschlichen haben könntest du sie
vielleicht einfach mal korrigieren oder zumindest darauf hinweisen statt
nur zu maulen. Ich finde es echt zum Kotzen. Da macht sich jemand die
Mühe um sowas zu erstellen und wenn es mal nicht ganz passt wird gemault
statt das man hilft es zu verbessern.
G. wrote:
> Erklärst du uns auch was da so falsch ist, von den offensichtlichen> Rechtschreibfehlern abgesehen?
"Die Pointer werden auch benutzt, um auf den Flash Speicher zuzugreifen.
Der Speicher kann auch direkt adressiert werden. Das gibt Zugriff auf
den gesamten Speicher mit einer 2-Word Instruktion."
Trifft auf avr-gcc nicht zu, dort geht Zugriff auf Flash nur über
Funktionen. Der resultierende Code ist zwar ähnlich, aber die
Programmierung viel unmständlicher.
"Nach dem Einschalten oder einem Reset muss der Stack Pointer
initialisiert werden, bevor irgendeine Funktion aufgerufen werden kann."
Nur bei älteren AVR-Modellen. Und was hat das eigentlich mit dem Thema
zu tun? Das ist Sache des Compilers bzw. dessen Laufzeitcode/Library und
nicht des Programmierers.
"Das SRAM ist für globale Variablen reserviert und kann für nichts
anderes verwendet werden."
Der Stack in dem sich u.A. die lokalen Variablen befinden liegt auch
dort. Besser: Das RAM kann nur für Daten, nicht aber für Code verwendet
werden.
"Lokale Variablen werden bevorzugt in den Registern gespeichert wenn sie
deklariert werden."
Unverständlich. Es gibt keine undeklarierten lokalen Variablen.
"Eine lokale statische Variable wird in ein Register beim Start der
Funktion geladen. Ist die Funktion zu Ende, wird die Variable zurück in
das SRAM gespeichert. Statische Variablen sind darum effizienter als
globale Variablen, wenn sie mehr als einmal in der Funktion benutzt
werden."
Es kann sein, dass der Compiler den Code so optimiert. Es kann
allerdings auch sein, dass er es nicht macht und damit genauso umgeht
wie mit globalen Variablen.
"Bis zu zwei Parameter von einfachen Variablen (char,int, long, float,
double) können zwischen den Funktionen über die Register R16-R23
getauscht werden."
Dies skizziert wohl das Registermodell eines bestimmten Compilers.
avr-gcc arbeitet anders und es werden auch mehr als 2 Parameter in
Registern übergeben.
"Wenn globale Variablen benötigt werden, sollten sie wenn immer es
passend ist in Strukturen abgelegt werden. So ist es dem Compiler
möglich sie direkt zu adressieren."
Abhängig vom verwendeten Compiler und keineswegs allgemein gültig.
Vielen Dank, wenigstens einer der mithilft. Ich werde das mal
weitergeben.
Aber wenn ich das so lese gibt es für die meisten Diskrepanzen 2 Gründe:
Das Alter der AVR035, die ist vom Januar 2004, und die Tatsache, das sie
nicht für den avr gcc geschrieben wurde. Gab es den 2004 überhaupt
schon?
Jedenfalls steht in dem Artikel, das es eine freie und nicht
vollständige Übersetzung der AVR035 ist. Wenn ich das im Original
richtig gelesen habe fehlt zum Beispiel der Teil mit dem Eeprom
Wie auch immer, vielleicht kann der Autor ja eine Anpassung an den gcc
und das Jahr 2009 vornehmen
Was dort speziell in Bezug auf avr-gcc fehlt: Es sollten bei kleinen und
mittleren Funktionen möglichst nur skalare Daten als lokale Variablen
mit der impliziten Speicherklasse "auto" verwendet werden, und diese
sollten nie indirekt genutzt werden (auch nicht als indirekter Parameter
wie &var). Ansonsten muss der Compiler recht aufwendig einen
vollständigen Stack-Frame erzeugen, und auch die Adresse einer solchen
Variblen wird deutlich umständlicher ermittelt als bei statisch
adressierten Variablen. Irgendwelche Puffer-Arrays und Stringspeicher
sind als also lokale "static"s effizienter.
>Es sollten bei kleinen und mittleren Funktionen möglichst nur skalare Daten>als lokale Variablen mit der impliziten Speicherklasse "auto" verwendet>werden...
Ohweh, da bin ich nicht Freak genug um das zu verstehen ;)
Wird das irgendwo erklärt so das auch ich das verstehen kann? Am besten
auf deutsch. Ich bin Ü40 und meine Englischkenntnisse sind etwas
bescheiden.
Es freut mich Leute kennenzulernen die mehr von Compiler verstehen als
ich. Für mein schlechtes Englisch und Deutsch muss ich mich
entschuldigen, ich hatte den Text nie korrektur gelesen, da ich ihn nie
veröffentlichen wollte. Ich habe ihn aus reinen privaten
Interessengründen in das deutsch übersetzt.
Auch habe ich nie gesagt, dass es auf den avr-gcc zutrifft, noch das es
richtig oder aktuell ist. Es ist eine Übersetzung des AVR App Note 35.
Beschwerden gehen an Atmel.
Wenn sich die Profis hier aber mal zusammen setzen würden um einen
aktuellen ähnlichen Text zu entwerfen, würde mich das sehr freuen. Mich
interesiert diese Compilerdinge sehr, sonst hätte ich mir auch nie die
Mühe gemacht etwas zu übersetzen.
G. wrote:
> Ohweh, da bin ich nicht Freak genug um das zu verstehen ;)
Lokale Daten können "static" oder "auto" sein ("register" ist etwas aus
der Mode gekommen). Steht nichts dabei, dann sind lokale Daten "auto",
das sind also die normalen.
Skalare Daten sind das Gegenteil von zusammengesetzten Datentypen wie
Arrays und struct/union. Also einfache Daten vom Typ "int", "char",
"float", ..., sowie Zeiger und auch "enum". Also das was in Register
passt.
Läuft letztlich darauf hinaus, dass bei avr-gcc nur solche Daten lokal
ohne "static" verwendet werden sollten, die der Compiler in Register
legt. Wenn er das nicht mehr kann, dann wird der Code der Funktion
deutlich aufwendiger.
> Ich bin Ü40
Ich auch. Das kann ich also als Entschuldigung nicht gelten lassen ;-).
Danke für die Erläuterung.
>Ich auch. Das kann ich also als Entschuldigung nicht gelten lassen ;-).
Nach dem Schulenglisch hatte ich nicht mehr viel damit zu tun. Im
Nachhinein ärgert es mich.
Deshalb freue ich mich immer, wenn sich Leute die Mühe machen sowas in
deutsch zu schreiben
Ok, entschuldigt wenn ich nerven sollte, aber ich möchte versuchen das
richtig zu verstehen. Ich habe gerade ein wenig gegoogelt und möchte
das gelesene nur nochmal absichern.
Wenn ich eine Variable, egal welchen Typs, außerhalb eines Blocks oder
einer Funktion ohne Zusatzangaben deklariere, wird sie im SRAM abgelegt.
Wenn sie verwendet wird muss sie jedes mal in ein Register kopiert
werden, oder zumindest ein Zeiger initialisiert werden. In dem Fall ist
es eine globale static Variable.
Wenn ich eine skalare Variable, also Char, Int usw, innerhalb einer
Funktion ohne Zusatz deklariere wird sie, sofern möglich, in einem
Register abgelegt. Deshalb ist der Code kompakter (um auf die
ursprüngliche Frage zurückzukommen).
Wenn ich ein Array/String innerhalb einer Funktion deklarieren muss ich
sie static deklarieren wodurch sie letztendlich zu einer globalen
Variable wird die zur gesamten Laufzeit im SRAM liegt
Ich hoffe, ich habe das halbwegs richtig verstanden und wiedergegeben.
G. wrote:
> Wenn ich eine Variable, egal welchen Typs, außerhalb eines Blocks oder> einer Funktion ohne Zusatzangaben deklariere, wird sie im SRAM abgelegt.> Wenn sie verwendet wird muss sie jedes mal in ein Register kopiert> werden, oder zumindest ein Zeiger initialisiert werden. In dem Fall ist> es eine globale static Variable.
Was du mit den Zeiger meinst weiss ich nicht, aber der Rest stimmt. Es
sein denn die Variablen wird so deklariert, dass sie im Flash oder
EEPROM landet.
> Wenn ich eine skalare Variable, also Char, Int usw, innerhalb einer> Funktion ohne Zusatz deklariere wird sie, sofern möglich, in einem> Register abgelegt. Deshalb ist der Code kompakter (um auf die> ursprüngliche Frage zurückzukommen).
Korrekt.
> Wenn ich ein Array/String innerhalb einer Funktion deklarieren muss ich> sie static deklarieren wodurch sie letztendlich zu einer globalen> Variable wird die zur gesamten Laufzeit im SRAM liegt
Du musst nicht, du kannst. Bei avr-gcc führt das zu kürzerem Code, hat
aber ggf. höheren RAM-Verbrauch zur Folge. Wenn das RAM knapp ist, Flash
aber nicht, dann ist "static" kein Vorteil.
Wenn man beispielsweise diverse Funktionen hat, die alle einen
temporären Stringpuffer verwenden, dann kann es in diesem Sinne auch
nützlich sein, für alle den gleichen statischen Puffer zu verwenden.
>Was du mit den Zeiger meinst weiss ich nicht...
Naja, wenn man ein Array oder so etwas hat wird ja nicht jedesmal das
komplette Array an eine Funktion übergeben sondern nur ein Zeiger oder
eine Referenz.
>Du musst nicht, du kannst. Bei avr-gcc führt das zu kürzerem Code, hat>aber ggf. höheren RAM-Verbrauch zur Folge. Wenn das RAM knapp ist, Flash>aber nicht, dann ist "static" kein Vorteil.
Wenn es Konstanten im Flash sind muss man offensichtlich static davor
setzen. Ich zitiere aus dem Tutorial dieser Seite:
>Deklarationen von Variablen im Flash-Speicher werden durch das "Attribut" >PROGMEM ergänzt. Lokale Variablen (eigentlich Konstanten) innerhalb von >Funktionen können ebenfalls im Programmspeicher abgelegt werden. Dazu ist >bei
der Definition jedoch ein static voranzustellen, da solche "Variablen" >nicht auf
dem Stack bzw. (bei Optimierung) in Registern verwaltet werden >können. Der
Compiler "wirft" eine Warnung falls static fehlt.
Hm, ich hoffe, eines Tages verstehe ich die Funktion von static,
volatile, extern usw mal richtig. Halbwissen ist manchmal
gefährlicher als gar nichts wissen
G. wrote:
> Ok, entschuldigt wenn ich nerven sollte, aber ich möchte versuchen das> richtig zu verstehen. Ich habe gerade ein wenig gegoogelt und möchte> das gelesene nur nochmal absichern.
Das Thema ist recht komplex.
Nach meiner unspezifischen Kritik an dem oben verlinkten Artikel geht's
also ins Eingemachte!
Zum Thema Optimierungen kann man keine allgemeingültigen Tipps geben:
Formal gesprochen bildet ein Compiler eine C-Quelle auf eine
Assembler-Quelle oder eine Binärdatei ab.
Den erzeugten Code kann er beliebig wählen sofern er im Rahmen seiner
Spezifikation die Semantik unverändert lässt .
Ein Compiler ist nicht dazu "verpflichtet", kurzen oder schnellen Code
zu erzeugen oder überflüssige Instruktionen wegzulassen.
Zunächst ist die Codegüte bei gleichbleibender Maschine, für die erzeugt
werden soll, abhängig vom
1
* verwendeten Compiler (also auch von seiner Version)
2
* seinen Einstellungen (Schalter, Optionen, ...)
3
* wieviel Wissen er über die Quelle hat
4
* wie die Quelle hingeschrieben ist
Dabei will ich immer davon ausgehen, daß mehrere im folgenden zu
vergleichende Quellen für den Progammierer semantisch äquivalent sind.
Der Programmierer hat idR das meiste Wissen über die Quelle, etwa
darüber, ob eine extern aufgerufene Funktion globale Variablen verändert
oder nicht, und wenn ja, welche.
Weiterhin will ich davon ausgehen, daß sowohl der Befehlssatz und die
Möglichkeiten der Maschine bekannt sind als auch die Sprache C und ihre
Konstrukte und Konzepte.
Für AVR ist dabei in erster Linie bedeutsam:
1
* Wie mächtig sind die einzelnen Register?
2
* Welche Adressierungsarten kennt AVR?
3
Wie teuer sind sie im Hinblick auf Raum und Zeit?
Da der Compiler, um dem es im Forum i.W. geht, avr-gcc ist, beziehe
ich mich im folgenden auf ihn. Hier sind wichtig:
1
* Wie verwendet der Compiler die einzelnen internen Register (GPRs)?
2
Etwa zur
3
- Argumentübergabe bei Funktionsaufrufen
4
- Zugriff auf Daten im Flash
5
- Zugriff auf den Stack
6
* Welche Optimierungsstrategiern gibt es (im groben) und welche
7
könnte er vielleicht anwenden?
8
* Wie stellt man den Compiler richtig ein? Welche Schalter, etc?
9
Allgemein: wie sollte man den Compiler aufrufen?
10
* Was kann er mit welchem Wissen anfangen? Etwa
11
- wird eine Funktion nur in einem Modul verwendet?
12
- wird eine Funktion (statisch) nur 1x aufgerufen
13
- hat ein eine Variable einen zur Compilerzeit oder zur Linkzeit
14
bekannten Wert?
15
* Wann ist es günstig, dem Compiler Informationen zu geben, und wann,
16
ihm Informationen vorzuenthalten? Wie stellt man das überhaupt an?
Was die Sprache C betrifft:
1
* Was genau sind die Unterschiede zwischen den einzelnen Speicherklassen?
2
* Was genau ist und was bewirken Qualifier wie volatile und const?
3
* Was genau sind die Unterschiede zwischen globalen und lokalen Variablen?
4
* Was genau bedeutet ein Funktionsaufruf?
5
* Was genau passiert bei Adressbildung einer Variablen?
6
* Was genau passiert bei Dereferenzierung eines Zeigers?
Gerade über die C-Punkte besteht oft große Unklarheit.
Dazu mache man sich die Unterschiede folgender Variablendefinitionen und
-deklarationen klar (hier im Gegensatz zu Code-Conventionen alle in
Großbuchstaben):
1
intA;
2
intA1=1;
3
staticintB;
4
staticintB1=1;
5
externintC;
6
7
voidfoo(intI,int*PI)
8
{
9
intJ;
10
intJ1=1;
11
staticintK;
12
staticintK1=1;
13
externintL;
14
}
und gleiches für Funktionen:
1
intE(int);
2
staticintF(int);
3
externintG(int);
4
intH(int,...);
'n Haufen Holz :-) also, worüber man sich im voraus Klarheit verschaffen
sollte...
Fortsetzung folgt
Johann
p.s.:
... falls Interesse besteht. (bevor ich mir hier den Wolf tippse... ;-))
So, ich möchte mich jetzt auch mal zu Wort melden. Ist ja schließlich
mein Wiki :)
Die Rechtschreibfehler habe ich größtenteils behoben. Mir ist selbst
nicht aufgefallen, das da so viele Tippfehler drin waren. Was die Kritik
angeht: wie schon erwähnt wurde handelt es sich um eine Übersetzung der
AVR035. Die ist von 2004 und sicher für den IAR geschrieben. Das die
Angaben mit dem avr-gcc von 2009 nicht unbedingt übereinstimmen kann man
sich eigentlich denken.
@Johann L. und A. K.
Ich wäre interessiert daran mehr über den optimalen Einsatz den avr-gcc
zu erfahren. Zum einen weil ich gerade aktiv viel damit arbeite und weil
ich Zeitgleich im Wiki an einem Tutorial schreibe. Und ich möchte
natürlich möglichst keinen Unsinn verzapfen.
Mein Teelöffel Senf :) zu folgendem Abschnitt:
"Globale Variablen die außerhalb einer Funktion deklariert werden,
werden dem SRAM zugeordnet. Das SRAM ist für globale Variablen
reserviert und kann für nichts anderes verwendet werden. Das ist eine
bedenkliche SRAM Verschwendung. Zu viele globale Variablen machen den
Code weniger lesbar und schwer zu modifizieren."
Wie A. K. schon schrieb, wird das SRAM nicht nur für globale Variablen,
sondern noch für viele andere Dinge verwendet. Offensichtlich wurde das
Original
"Global variables that are declared outside a function are assigned to
an SRAM memory location. The SRAM location is reserved for the global
variable and can not be used for other purposes, this is considered to
be waste of valuable SRAM space. Too many global variables make the
code less readable and hard to modify."
ungenau übersetzt. In dem Text ist die Rede von der der Variablen
zugeordneten "SRAM location" und nicht vom gesamten SRAM. Vielleicht
wäre folgende (freie und etwas ausführlichere) Übersetzung klarer:
"Eine globale Variable, die außerhalb einer Funktion deklariert ist,
wird an einer festen Speicherstelle im SRAM platziert. Diese
Speicherstelle ist während des gesamten Programmlaufs von der globalen
Variablen belegt und kann nicht zur Speicherung anderer Daten
verwendet werden. Deswegen ist es eine Verschwendung wertvollen
SRAM-Speicherplatzes, Variablen ohne triftigen Grund global zu machen.
Darüber hinaus machen zu viele globale Variablen den Programmcode
schwerer les- und änderbar."
Was den SRAM-Verbrauch betrifft, gilt das Geschriebene auch für alle mit
static deklarierten Variablen, da diese, wie der Name schon andeutet,
ebenfalls für den gesamten Programmlauf an einer festen Stelle im
SRAM liegen.
Danke, ich werde es entsprechend ändern. Das sind so Feinheiten, die man
leicht übersieht.
Ich habe inzwischen auch noch gelernt das das nicht nur für Funktionen
sondern auch für Blöcke gilt. Man kann in einem Programm oder innerhalb
einer Funktion mit {} einen abgegrenzten Block erstellen.
Johann L. wrote
> ... falls Interesse besteht. (bevor ich mir hier den Wolf tippse... ;-))
Interesse besteht auf jeden Fall - und wahrscheinlich auch (dringender)
Aufklärungsbedarf.
Sehr interessantes Thema!
Wie G. bereits schrieb:
> Halbwissen ist manchmal gefährlicher als gar nichts wissen
@Johann L.
Wäre an einer detaillierteren Ausführung ebenfalls äusserst
interessiert.
Was die Sprache C betrifft, so bin ich mir über die von dir genannten
Unterschiede sehr wohl bewusst, in Bezug auf den AVR habe ich aber immer
wieder ein Verständnis-Problem.