Forum: Mikrocontroller und Digitale Elektronik Prozessor Taktzyklen von Funktionen


von Basti B. (basti195) Benutzerseite


Lesenswert?

Hallo,
zunächst ich habe mir das Programmieren vor ca 3 Jahren selber 
beigebracht.
Nach dem ich immer mehr dazu lerne und meine Projekte immer größer und 
komplexer werden versuche ich von globalen variablen weg zu kommen.
Jedoch hatte ich bis her die Befürchtung,das Prozessor Laufzeit verloren 
geht, wenn ich eine Funktion mit Übergabe-Parameter erstelle.

Nun meine Frage, wir später auf dem Prozessor dann für die Funktion eine 
zusätzliche variable erzeugt oder wird der Funktion nur die Adresse der 
Variablen übergeben, sodass die Funktion weiß mit welchen Variablen sie 
Arbeiten soll

Über eine Antwort würde ich mich freuen.

Grüße
Basti

von Karl M. (Gast)


Lesenswert?

Guten Morgen,

deine Frage ist leider noch zu allg. gefasst, da es hunderte 
Programmiersprachen und Kompilerbauer und Komputer gibt.

Bitte spezifiziere deine Frage und stelle auch noch Beispielcode ein,
dann kann man darauf eingehen.

von Lothar M. (Firma: Titel) (lkmiller) (Moderator) Benutzerseite


Lesenswert?

Sebastian B. schrieb:
> Jedoch hatte ich bis her die Befürchtung,das Prozessor Laufzeit verloren
> geht, wenn ich eine Funktion mit Übergabe-Parameter erstelle.
Das stimmt auch. Die Stackverwaltung braucht Rechenzeit.

Die letztendlich alleinig interessanten Fragen sind andere: 1. passt das 
Programm wenn es fertig ist noch in den uC? Und: 2. ist es schnell 
genug?

Wenn diese beiden Fragen mit JA beantwortet werden können, dann ist jede 
Sorge um die Programmeffizienz uninteressant.

> Nun meine Frage, wir später auf dem Prozessor dann für die Funktion eine
> zusätzliche variable erzeugt oder wird der Funktion nur die Adresse der
> Variablen übergeben, sodass die Funktion weiß mit welchen Variablen sie
> Arbeiten soll
Zeig mal ein Beispiel, das dazu passt.

: Bearbeitet durch Moderator
von (prx) A. K. (prx)


Lesenswert?

Sebastian B. schrieb:
> Jedoch hatte ich bis her die Befürchtung,das Prozessor Laufzeit verloren
> geht, wenn ich eine Funktion mit Übergabe-Parameter erstelle.

RISC-artige Architekturen wie AVR, ARM oder PIC32 tun sich mit 
Parameterübergabe leichter als mit globalen Variablen.

> Nun meine Frage, wir später auf dem Prozessor dann für die Funktion eine
> zusätzliche variable erzeugt oder wird der Funktion nur die Adresse der
> Variablen übergeben, sodass die Funktion weiß mit welchen Variablen sie
> Arbeiten soll

In FORTRAN wird die Adresse übergeben, in C der Wert wenn es kein Array 
ist.

von Basti B. (basti195) Benutzerseite


Lesenswert?

Ich programmiere in C
Bsp.
in beiden Funktionen soll die Variable bearbeitet werden.
ich habe eine Globale Variable (var_glob)
und eine lokale (var_loc)
1
void funktion1(void) 
2
{ 
3
var_glob ++; 
4
} 
5
6
int funktion2 (int XY)
7
{
8
XY ++; 
9
return XY; 
10
}
11
12
Main 
13
{
14
funktion1();
15
var_loc = Funktion2(var_loc);
16
}
meine Frage ist ob beim Funktionsaufruf von Funktion 2 im Prozessor eine 
2. Variable erzeugt wird, die zunächst mit der lokalen Variablen 
gleichgesetzt und so eine bestimmt anzahl von Taktzyklen und zusätzlich 
RAM braucht oder dies nur dazu ist, dass es übersichtlicher wird.

Ich hoffe das meine Frage etwas anschaulicher geworden ist

: Bearbeitet durch Moderator
von (prx) A. K. (prx)


Lesenswert?

Lothar M. schrieb:
> Das stimmt auch. Die Stackverwaltung braucht Rechenzeit.

Vorausgesetzt es findet überhaupt eine Stackverwaltung statt. Bei 
Parameterübergabe oder Variablen in Registern ist das nicht 
notwendigerweise der Fall. Oft nur in Form von Register-Save/Restore, 
wenn in der Funktion wiederum Funktionen aufgerufen werden.

von Basti B. (basti195) Benutzerseite


Lesenswert?

Wie kommt es das Globale Variablen schlechter verarbeitet werden können 
als lokale?

von (prx) A. K. (prx)


Lesenswert?

Sebastian B. schrieb:
> Wie kommt es das Globale Variablen schlechter verarbeitet werden können
> als lokale?

Parameter und lokale Variablen liegen oft in Registern. Zugriffszeit 0.

Selbst wenn sie auf dem Stack liegen können sie relativ zum Stack- oder 
Framepointer meist direkt adressiert werden, da die Distanz dazu gering 
ist und direkt im Lade/Speicherbefehl codiert werden kann.

Globale und statische Variablen hingegen haben eine recht breite 
Adresse, die viel Platz benötigt und insbesondere bei 32-Bit RISCs nicht 
in einem Befehl direkt verarbeitet werden kann. Bei den Cortex M3 
(Thumb2) sind dann auf Zeit optimiert 3 Befehle nötig. 2 laden die 
Adresse in ein Register und einer spricht darüber den Speicher an. Auf 
Platz optimiert ist es zwar etwas kürzer, sorgt aber ggf. für etliche 
Waitstates aufgrund Datenzugriffs auf das Flash-ROM. In beiden Fällen 
fällt ein zusätzlich verwendetes Register an.

Anders sieht es beispielsweise bei den 8-Bit PICs aus. Die verfügen 
entweder überhaupt nicht über die Fähigkeit, einen Stack anzusprechen, 
oder Zugriff und Verwaltung davon sind relativ aufwändig. 
Optimierungsstrategien für solche nicht auf C optimierte Architekturen 
sind daher deutlich anders als für Standardarchitekturen.

: Bearbeitet durch User
von Ulrich F. (Gast)


Lesenswert?

Sebastian B. schrieb:
> Globale Variablen .... als lokale

Ob man etwas global, oder lokal macht, sollte man davon abhängig machen, 
welche Sichtbarkeit man der Variablen geben muss.
Immer wieder eine gute Strategie: Kapseln!

Um die Taktzyklen, oder den Speicherbedarf, kann man sich einen Kopf 
machen, wenn es eng wird.

von P. M. (o-o)


Lesenswert?

Grundsätzlich gilt: Auf Sprachebene sollte man sich keine Gedanken über 
Optimierung machen, sondern auf gute Strukturierung achten.

Aus zwei Gründen:
- Erstens ist der allermeiste Code in keiner Weise performance-kritisch. 
Ob er doppelt oder zehnmal so lange braucht wie die optimale Lösung, hat 
auf die gefühlte Effizienz des Programms keinerlei Einfluss. Setze also 
die Zeit zum Optimieren genau dort ein, wo du tatsächlich 
Performance-Probleme identifiziert hast.
- Der Compiler optimiert extrem stark und intelligent. Er weiss viel 
besser als du, wie man bestimmte Sprachkonstrukte in effizienten 
Maschinencode umwandelt. Erkläre dem Compiler auf möglichst 
"verständliche" Art, was du haben möchtest und er wird das richtige 
daraus machen.

von Basti B. (basti195) Benutzerseite


Lesenswert?

Vielen dank für die vielen, schnellen und ausführlichen Beiträge :)
Das die Lokalen Variablen in Registern gespeichert werden ist praktisch.
vielen dank

grüße
basti

von (prx) A. K. (prx)


Lesenswert?

Sebastian B. schrieb:
> Das die Lokalen Variablen in Registern gespeichert werden

Nur wenn es welche gibt. Bei 8-Bit PICs und Freescales gibts keine und 
bei den 16-Bit Renesas (R8C/M16C/M32C) zu wenige. Um ein paar Beispiele 
zu nennen.

: Bearbeitet durch User
von Axel S. (a-za-z0-9)


Lesenswert?

Sebastian B. schrieb:
1
> void funktion1(void)
2
> {
3
> var_glob ++;
4
> }
5
> 
6
> int funktion2 (int XY)
7
> {
8
> XY ++;
9
> return XY;
10
> }

> meine Frage ist ob beim Funktionsaufruf von Funktion 2 im Prozessor eine
> 2. Variable erzeugt wird, die zunächst mit der lokalen Variablen
> gleichgesetzt und so eine bestimmt anzahl von Taktzyklen und zusätzlich
> RAM braucht oder dies nur dazu ist, dass es übersichtlicher wird.

Du machst dir Gedanken um nichts. Ein vernünftiger Compiler (das sind 
heutzutage fast alle) wird dir für beide Funktionen praktisch 
identischen Code generieren. Die Variable wird sich vor dem 
Funktionsaufruf in einem Register befinden. Für den Aufruf von funktion2 
wird die Variable in einem Register übergeben und in einem Register 
(meist dem gleichen) zurückgegeben. Der Compiler wird, wenn er clever 
ist, jegliche Register-zu-Register Transfers einsparen, indem er die 
Variable von Anfang an in das richtige Register lädt. Und eine so kurze 
popelige Funktion wie oben wird der Compiler fast sicher sowieso 
inlinen, also gar keinen Funktionsaufruf machen sondern den Rumpf der 
Funktion dort wo sie aufgerufen wird, einfügen.

Du machst dir aber sowieso die falschen Gedanken. Ein Ziel bei der 
Entwicklung höherer Programmiersprachen ist immer auch eine bessere 
Lesbarkeit des Programmcodes. Und Lesbarkeit meint nicht nur lesbar, 
sondern auch: von vorn herein leichter schreibbar, leicher debugbar, 
leicher zu warten und zu pflegen. Und die Vermeidung globaler Variablen 
dient ebenfalls diesem Zweck.

Wenn du in einer Hochsprache programmierst, dann sollte die Effizienz 
des vom COmpiler erzeugten Codes so ziemlich das Letzte sein, an das du 
beim Schreiben des Programmes denkst. Achte lieber darauf daß es 1. 
korrekt und 2. lesbar ist.

Optimierung auf Geschwindigkeit findet meist auf höheren Ebenen statt, 
z.B. indem man einen anderen Algorithmus implementiert. Mikrooptimierung 
wie du sie hier betreiben willst, ist in 99% aller Fälle vollkommen 
unnötig. Anders ausgedrückt: Quicksort ist nicht deswegen schneller als 
Bubblesort, weil es globale statt lokaler Variablen verwendet, sondern 
weil es einen [für größere Datenmengen] besseren Algortihmus verwendet.

von Moby A. (moby-project) Benutzerseite


Lesenswert?

Lothar M. schrieb:
> Die letztendlich alleinig interessanten Fragen sind andere: 1. passt das
> Programm wenn es fertig ist noch in den uC? Und: 2. ist es schnell
> genug?

Beides stellst Du zuallererst mit (gutem) Assembler sicher.

von Lothar M. (Firma: Titel) (lkmiller) (Moderator) Benutzerseite


Lesenswert?

Moby A. schrieb:
> Lothar M. schrieb:
>> Die letztendlich alleinig interessanten Fragen sind andere: 1. passt das
>> Programm wenn es fertig ist noch in den uC? Und: 2. ist es schnell
>> genug?
> Beides stellst Du zuallererst mit (gutem) Assembler sicher.
Das ist hier in keinster Weise die Frage!

Ich werde in Zukunft jeden Versuch, einen Thread zu entführen einfach 
per Löschung unterbinden. Leute, tut mir leid für eure Zeit zum 
Schreiben der Beiträge, die auf diesen Trollaufruf folgten. Lasst es 
einfach bleiben und meldet solche Entführungsversuche per Knopfdruck.

von P. M. (o-o)


Lesenswert?

Axel S. schrieb:
> Optimierung auf Geschwindigkeit findet meist auf höheren Ebenen statt,
> z.B. indem man einen anderen Algorithmus implementiert. Mikrooptimierung
> wie du sie hier betreiben willst, ist in 99% aller Fälle vollkommen
> unnötig. Anders ausgedrückt: Quicksort ist nicht deswegen schneller als
> Bubblesort, weil es globale statt lokaler Variablen verwendet, sondern
> weil es einen [für größere Datenmengen] besseren Algortihmus verwendet.

Mikrooptimierung ist schon wichtig, aber man macht es nicht von Hand, 
sondern der Compiler macht es. Wenn man beispielsweise eine 
mathematische Formel in einer Schleife auswertet, so gewinnt man 
Zehnerpotenzen an Performance dadurch, dass die einzelnen Funktionen 
ge-inlined und nicht separat aufgerufen werden.

von Karl H. (kbuchegg)


Lesenswert?

P. M. schrieb:
> Axel S. schrieb:
>> Optimierung auf Geschwindigkeit findet meist auf höheren Ebenen statt,
>> z.B. indem man einen anderen Algorithmus implementiert. Mikrooptimierung
>> wie du sie hier betreiben willst, ist in 99% aller Fälle vollkommen
>> unnötig. Anders ausgedrückt: Quicksort ist nicht deswegen schneller als
>> Bubblesort, weil es globale statt lokaler Variablen verwendet, sondern
>> weil es einen [für größere Datenmengen] besseren Algortihmus verwendet.
>
> Mikrooptimierung ist schon wichtig, aber man macht es nicht von Hand,
> sondern der Compiler macht es.

Und man betreibt natürlich auch nicht sinnlos kompliziertes 
Programmieren, sondern benutzt normales, übliches Vorgehen. In diesem 
Sinne muss man ein generelles "mach dir keine Sorgen, der Compiler wirds 
schon richten" auch zurechtrücken.
Eng damit im Zusammenhang steht natürlich auch, dass man seine 
Programmiersprache einigermassen beherrscht. Wer von seiner Sprache 
gerade mal 10% des Sprachumfangs kennt, und das auch nur mehr schlecht 
als recht, wird nicht viel zu Stande bringen - mit oder ohne 
Mikrooptimierung.

von Axel S. (a-za-z0-9)


Lesenswert?

P. M. schrieb:
> Axel S. schrieb:
>> Optimierung auf Geschwindigkeit findet meist auf höheren Ebenen statt,
>> z.B. indem man einen anderen Algorithmus implementiert. Mikrooptimierung
>> wie du sie hier betreiben willst, ist in 99% aller Fälle vollkommen
>> unnötig

> Mikrooptimierung ist schon wichtig, aber man macht es nicht von Hand,
> sondern der Compiler macht es.

Ich dachte es wäre im Kontext klar gewesen, daß ich manuelle Mikro- 
optimierung gemeint habe. Und nicht das was der Compiler tut.

von P. M. (o-o)


Lesenswert?

Axel S. schrieb:
> Ich dachte es wäre im Kontext klar gewesen, daß ich manuelle Mikro-
> optimierung gemeint habe. Und nicht das was der Compiler tut.

Ich habe es vermutet, ja. Wollte aber sicher gehen und noch ein paar 
Worte anfügen, da der TO sich damit scheinbar noch wenig auskennt.

Karl H. schrieb:
> Eng damit im Zusammenhang steht natürlich auch, dass man seine
> Programmiersprache einigermassen beherrscht. Wer von seiner Sprache
> gerade mal 10% des Sprachumfangs kennt, und das auch nur mehr schlecht
> als recht

Richtig. Besonders sollte man auch wissen, was in einer Sprache 
teuer/problematisch ist. In C/C++ beispielsweise dynamische 
Speicherallokation oder versehentliches pass-by-reference von grossen 
Objekten. Genau aus diesem Grund halte ich es für sinnvoll, eine Sprache 
tiefer kennen zu lernen als bloss die Sprachkonstrukte und 
Bibliotheksaufrufe kennen zu lernen. Man muss wissen, was unter der 
Haube passiert.

von (prx) A. K. (prx)


Lesenswert?

P. M. schrieb:
> oder versehentliches pass-by-reference von grossen Objekten.

War hoffentlich andersrum gemeint. Ich finde die Idee, grosse Objekte 
per Referenz zu übergeben, durchaus vernünftig.

: Bearbeitet durch User
von P. M. (o-o)


Lesenswert?

A. K. schrieb:
> P. M. schrieb:
>> oder versehentliches pass-by-reference von grossen Objekten.
>
> War hoffentlich andersrum gemeint. Ich finde die Idee, grosse Objekte
> per Referenz zu übergeben, durchaus vernünftig.

Aber klar :-) Versehentliches pass-by-value sollte es heissen. Autsch...

von W.S. (Gast)


Lesenswert?

A. K. schrieb:
> Parameter und lokale Variablen liegen oft in Registern. Zugriffszeit 0.

Hehe.. du hast heut wohl nen eher schlechten Tag. Von wegen Zugriffszeit 
0 - das Argument muß ja erstmal dort hinein kommen.

Ich halte das Herumgefummel mit Funktionsargumenten dort, wo es nicht 
sein muß, für einen ziemlich überflüssigen Kropf. Es bringt nix und 
kostet bloß bei jedem Aufruf der betreffenden Funktion das Laden des 
Argumentes.

Viel wichtiger als all das Grübeln über globale Variablen oder nicht ist 
es, eine Firmware sinnvoll zu modularisieren, so daß man nicht das ganze 
Gekröse an Variablen und anderem Krempel überall um die Ohren hat. Was 
dann an globalen Variablen wirklich noch übrig bleibt, ist fast immer 
nur ganz wenig - und das Wenige hat dann seine Berechtigung.

W.S.

von c-hater (Gast)


Lesenswert?

A. K. schrieb:

> Parameter und lokale Variablen liegen oft in Registern. Zugriffszeit 0.

Nanana.

Ganz so einfach ist das natürlich nicht. Das hängt leider von sehr 
vielen Variablen ab. Klar: Sprache, Compiler, Optimierungslevel. Darüber 
hinaus aber auch noch massiv vom Zielsystem. Wenn das einfach mal nur 
wenige Register besitzt, ist nicht damit zu rechnen, daß viele für die 
Parameterübergabe genutzt werden (können). Und leider setzt bei den oft 
relativ starren Algorithmen der Codegeneratoren die Stacknutzung oft 
schon ein, lange bevor die Register "alle" sind.

Abgesehen davon: Natürlich ist auch ein Registerzugriff nicht zum 
Nulltarif zu bekommen. Nicht mal in Assembler...

von (prx) A. K. (prx)


Lesenswert?

W.S. schrieb:
> Hehe.. du hast heut wohl nen eher schlechten Tag. Von wegen Zugriffszeit
> 0

Welche Zahl wär dir lieber, wenn man mal davon ausgeht, dass RAM bei µCs 
einen Takt Zugriffszeit hat? Ein Register hat mindestens bei den RICSs 
weniger Zugriffszeit als das RAM und negativ wirds nicht. Bei CISCs mit 
darauf optimierter Pipelining kann man zwar was einsparen, aber so arg 
viele µCs dieser Bauart fallen mir grad nicht ein.

> das Argument muß ja erstmal dort hinein kommen.

Korrekt. Das ist aber hauptsächlich dann von Vorteil, wenn die Funktion 
die Variable überhaupt nicht benötigt.

Ich hatte oben schon aufgeführt, dass ich mich in dieser Betrachtung 
eher auf registerorientierte Architekturen beziehe. Bei 8-Bit 
PIC/8051/68xx kann die Sache anders ausgehen.

> Ich halte das Herumgefummel mit Funktionsargumenten dort, wo es nicht
> sein muß, für einen ziemlich überflüssigen Kropf. Es bringt nix und
> kostet bloß bei jedem Aufruf der betreffenden Funktion das Laden des
> Argumentes.

Klar, 0 Parameter sind schneller als 1 Parameter. Aber das ist hier 
nicht die Alternative. Sondern Parameter gegenüber globaler Variable. 
Und da hat der (skalare) Parameter noch einen weiteren Vorteil, wenn 
davon nirgends die Adresse verlangt wird: Weder kann Aliasing auftreten, 
noch wird er in einem Unterprogramm verändert.

> Viel wichtiger als all das Grübeln über globale Variablen oder nicht ist
> es, eine Firmware sinnvoll zu modularisieren, so daß man nicht das ganze
> Gekröse an Variablen und anderem Krempel überall um die Ohren hat.

Modularisierung vermeidet keine Variablen, sondern platziert sie 
übersichtlicher im Quellcode und adressiert sie evtl anders. Das kann in 
Verbindung mit struct/class zu relativer Adressierung von Variablen 
führen, die sonst absolut adressiert würden. Was meist ein klarer 
Vorteil ist, es sei denn der Prozessor tut sich damit schwer. Nur hatte 
ich diese bei µC-Programmierung in C++ häufiger als in C verbreitete 
Technik hier erst einmal aussen vor gelassen.

von (prx) A. K. (prx)


Lesenswert?

c-hater schrieb:
> Wenn das einfach mal nur wenige Register besitzt,

Ja, aber das hatte ich auch schon ausdrücklich erwähnt.

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.