Performance: Lieber globale Variablen statt Zugriffsfunktionen?
-
mgaeckler schrieb:
interrupt| schrieb:
mgaeckler schrieb:
knivil schrieb:
Soviel Overhead erzeugt ein call nun auch nicht. Auch ein Faktor 5-10 ist utopisch und geht stark an der Realitaet vorbei.
Das sehe ich nicht so. Ein Funktionsaufruf ist im Vergleich zum einfachen Speicherzugriff extrem teuer.
Es müssen alle Registervariablen auf dem Stack gesichert, dann muß der aktuelle PC auf dem Stack gesichert und dann zum neuen Ziel verzweigt werden.
Beim Rücksprung muß der PC und dann die Register wiederhergestellt werden. Ein Performanceverlust um den Faktor 5-10 halte ich da für sehr untertrieben.du redest hier doch eher von einem interrupt oder
OK ich geb's zu: hab Mist erzählt. Wenn jede Funktion, die Registervariablen verwendet, diese selber sichert, muß das natürlich der Aufrufer nicht mehr machen. Ist einfach schon zu lange her.
mfg Martin
Wenn man in Assembler programmiert hat man hier noch Optionen. Aber bei C macht es der Compiler meist so, dass der ganze Kontext gesichert wird. Das hat nichts mit Interrupts zu tun. Dieses sichern kostet immer Laufzeit.
Zudem ist ein call oder jump oft nicht gut für die Pipeline.Ich sehe das nicht als Mikrooptimierung. Mikro ist es vll. für manche Leute deshalb weil wir aus vll. 5-10 Maschinenbefehlen (Get/Set-Calls) eben 1-2 Maschinenbefehle (Zugriff auf globale Variable) machen.
Wenn das aber an vielen Stellen in der Software und vor allem in Teilen die sehr oft pro Sekunde aufgerufen werden können die Auswirkungen dieser Optimierung sehr groß sein.
-
Frank Erdorf schrieb:
Statt Funktionen verwende ich, wenn es WIRKLICH was bringt defines (MAKROS).
Die haben zwar viele Nachteile, vermeiden dann aber den Funktion Overhead.Gruß Frank
Dafür braucht du aber auch "echt" globale Variablen.
Falls nicht würde mich interessieren wie dein Get-Makro mit Modul-globalen (static) Variablen funktioniert.
-
knivil schrieb:
Ich seh den Vorteil bei 1. in der Performance. Es muss kein call stattfinden mit dem ganzen Overhead, möglicherweise ist der Zugriff nur 1-2 Assemblerbefehle groß.
Ich schätze je nach Architektur ist man hier um Faktor 5-10 schneller. Was meint ihr?Nicht schaetzen sondern messen. Soviel Overhead erzeugt ein call nun auch nicht. Auch ein Faktor 5-10 ist utopisch und geht stark an der Realitaet vorbei. Wichtig ist die Performance in Bezug auf das gesamte Programm, Mirkooptimierungen fallen dabei kaum ins Gewicht.
Die Schätzung basiert auf analysierten Objekt-Dumps.
Natürlich bringen solche Optimierungen nur was wenn die Gesamtheit aller möglichen Stellen wo man vll. Get/Set-Funktionen aufruft Signifikant für die Gesamtlaufzeit sind.
Ob dieser Anteil signifikant ist oder nicht lässt sich nicht pauschal sagen.
Bei einer GUI-Applikation auf dem PC vll. nicht, bei einer Steuerung auf einem Mikrocontroller evtl. schon.
-
stefan-tiger schrieb:
Die Schätzung basiert auf analysierten Objekt-Dumps.
Blah blah.
-
pointercrash() schrieb:
Prinzipiell und lehrbuchgemäß ist eine größtmögliche Kapselung erwünscht, also die static- Variante mit Get/Set- Funktionen. ....
Tatsächlich gibt es wenig Gründe, globale Variablen einzuführen, einer davon ist die Performance, ...
Wie auch immer, Du solltest höchst triftige Gründe haben, globals einzuführen und verschärft auf die Sichtbarkeit aufpassen.Viele Lehrbücher wollen allgemeine Lösungen für allgemeine Dinge beibringen. Ich denke die meisten sind für PC-Software und geben eher den Ratschlag wartbare Software statt optimierter Software zu erstellen.
Aber was ist denn C? Ist es Ein- oder Ausgebe-Sprache, oder beides?
Wenn ich einen Text schreibe dann erstelle ich eine Quelle z.B. in LaTeX und eine Ausgabe als PDF.
Ich sehe C eher als Ausgabe-Sprache. Abstraktere Dinge wie Prüfung des Scopes, Klassen und Instanzen usw. sollte man nicht versuchen in C zu verwalten sondern es auf einer höheren Abstraktionsebene "modelieren".
Der C-Code und letztendlich das was nachher auf der CPU läuft sollte so optimal wie möglich sein. Wenn mich die Sprachmittel von C vor die Wahl stellen (a) gute Verwaltbarkeit / nicht optimale Performance oder (b) schlechte Verwaltbarkeit / optimale Persormance, dann sollte man sich für (b) entscheiden und die Verwaltung mit auf C "aufgesetzten" Tools (Code-Generatoren, Skripte, etc.) durchführen die das hoffentlich optimal machen.Natürlich existieren oft nicht solche Tools oder man verwendet Sie nicht und verwaltet seinen Code "zu Fuß" in der Sprache C. Dann hat man es natürlich auch besser wenn man sich ans Lehrbuch hält...
-
stefan-tiger schrieb:
pointercrash() schrieb:
Prinzipiell und lehrbuchgemäß ist eine größtmögliche Kapselung erwünscht, also die static- Variante mit Get/Set- Funktionen. ....
Tatsächlich gibt es wenig Gründe, globale Variablen einzuführen, einer davon ist die Performance, ...
Wie auch immer, Du solltest höchst triftige Gründe haben, globals einzuführen und verschärft auf die Sichtbarkeit aufpassen.Viele Lehrbücher wollen allgemeine Lösungen für allgemeine Dinge beibringen. Ich denke die meisten sind für PC-Software und geben eher den Ratschlag wartbare Software statt optimierter Software zu erstellen.
Aber was ist denn C? Ist es Ein- oder Ausgebe-Sprache, oder beides?
Wenn ich einen Text schreibe dann erstelle ich eine Quelle z.B. in LaTeX und eine Ausgabe als PDF.
Ich sehe C eher als Ausgabe-Sprache. Abstraktere Dinge wie Prüfung des Scopes, Klassen und Instanzen usw. sollte man nicht versuchen in C zu verwalten sondern es auf einer höheren Abstraktionsebene "modelieren".
Der C-Code und letztendlich das was nachher auf der CPU läuft sollte so optimal wie möglich sein. Wenn mich die Sprachmittel von C vor die Wahl stellen (a) gute Verwaltbarkeit / nicht optimale Performance oder (b) schlechte Verwaltbarkeit / optimale Persormance, dann sollte man sich für (b) entscheiden und die Verwaltung mit auf C "aufgesetzten" Tools (Code-Generatoren, Skripte, etc.) durchführen die das hoffentlich optimal machen.Natürlich existieren oft nicht solche Tools oder man verwendet Sie nicht und verwaltet seinen Code "zu Fuß" in der Sprache C. Dann hat man es natürlich auch besser wenn man sich ans Lehrbuch hält...
hm? versteh ich nicht, wieso willst du das auf einer höheren abstraktionsebene modellieren und warum sollte das schneller sein? überhaupt weshalb denkst du dass schöne software langsam ist
-
stefan-tiger schrieb:
nein die Definitionen stehen nicht in einem Header. Warum habe ich im selben Beitrag im nachfolgenden Satz erklärt.
Tschuldigung, hab ich übersehen. Das ist dann der Grund warum die Funktionsaufrufe nicht wegoptimiert werden: Die Definitionen stehen in ner anderen Übersetzungseinheit, sind also für den Compiler nicht sichtbar.
-
Hallo stefan-tiger
Thema Makros statt Funktionen
typdef struct { int a; int b; ... } TMyStruct; // mit function overhead MyStructSetA(TMyStruct* myStruct, int a) { myStruct->a = a; } // ohne function overhead #define MyStructSetA(myStruct,A) (myStruct)->a=(A);
Wo das Struct nun liegt, stack, heap, statisch ist erstmal egal.
So etwas habe ich schon ein paar mal gemacht.Normaler weise ist es, wie schon erwähnt,
sinnvoll den Code über Funktionen oder besser noch Scharen von Funktionen zu strukturieren.
Es gibt Stellen wo einem der Funktion Overhead zu teuer ist.
Man kann diese Stellen dann mit #defines quasi 'inlinien'
ohne Struktur zu verlieren.
Ist aber grausig zu debuggen, in jedem Fall mit Vorsicht zu genießen
und sicherlich nur selten nötig.Gruß Frank
-
/* ModulA.h */ #ifndef MODULA_H #define MODULA_H typdef struct { int a; int b; ... } TMyStruct; // ohne function overhead #define MyStructSetA_Makro(A) MyStruct.a=(A); /* Prototyp */ MyStructSetA_Funktion(int a); #endif
/* ModulA.c */ #include "ModulA.h" /* private Daten des Moduls A */ static TMyStruct MyStruct; // mit function overhead MyStructSetA_Funktion(int a) { MyStruct.a = a; }
/* ModulB.c */ #include "ModulA.h" void tolleFunktion(void) { /* Modul B will was in Modul A setzten */ MyStructSetA_Makro(42); /* geht nicht :-( */ MyStructSetA_Funktion(42); /* das geht ! */ } ...
Hallo, ich habe dein Code angepasst/ergänzt. Vll. wird jetzt klar was ich mein.
-
Michael E. schrieb:
stefan-tiger schrieb:
nein die Definitionen stehen nicht in einem Header. Warum habe ich im selben Beitrag im nachfolgenden Satz erklärt.
Tschuldigung, hab ich übersehen. Das ist dann der Grund warum die Funktionsaufrufe nicht wegoptimiert werden: Die Definitionen stehen in ner anderen Übersetzungseinheit, sind also für den Compiler nicht sichtbar.
Ja aber das geht ja nicht. Schau dir mal den Code im letzten Post von mir an. Statt dem Makro könnte dort auch "deine" inline Funktion stehen. Würde aber aus den selben Gründen wie beim Makro nicht gehen.
-
Hallo stefan-tiger,
/* private Daten des Moduls A */ static TMyStruct MyStruct;
Na das static ist der Grund warum es nicht geht ...
/* Daten des Moduls A */ TMyStruct MyStruct;
und
extern TMyStruct MyStruct;
in der h Datei, schon gehts ...
Klar, so kann der 'User' dann doch direkt auf das struct zugreifen,
was soo nicht gedacht war ... Muss in die Doku -> ModulA.h.Noch etwas:
globale oder statische Variabeln sollten wann immer möglich vermieden werden.Besser soetwas ...
/* ModulA.h */
#ifndef MODULA_H
#define MODULA_Htypdef struct
{
int a;
int b;
...
} TMyStruct;// ohne function overhead
#define MyStructSetA_Makro(myStruct,A) (myStruct)->a=(A);/* Prototyp /
MyStructSetA_Funktion(TMyStruct myStruct, int a);#endif
Das bietet zusätzlich die Möglichkeit mehrere, unterschiedliche Instanzen vom ModulA haben zu können und auch oder nur eine statische Instanz zu haben.
Wie auch immer, inline bietet c nicht und c++ behält sich das Recht vor doch nicht zu inlinen (nutzt also auch nichts).
Was bleibt sind defines, das funktioniert, aber auch mit Einschränkungen ...Gruß Frank
-
Frank Erdorf schrieb:
....
Na das static ist der Grund warum es nicht geht ...
/* Daten des Moduls A */ TMyStruct MyStruct;
und
extern TMyStruct MyStruct;
in der h Datei, schon gehts ...
....
Genau das sage ich ja die ganze Zeit. Natürlich liegt es am static.
Das mit dem Pointer auf Struct ist allgemein eine gute Idee, aber für die betrachtung des Scrops erstmal unwichtig.Das Problem ist aber, wenn MyStruct "echt" global ist, wie von dir vorgeschlagen, dann hat man keine Kontrolle mehr über seine eignen Daten.
Die Set-Funktion könnte ja umfangreicher sein und irgendwelche Prüfungen durchführen. Das kann dann ein anderes Modul einfach umgehen.
Aber wie ich schon eingangs erwähnt habe: Aus verschiedenen Günden (Performance, mehrere Instanzen) würden viele "echt" globale Variablen einsetzen. Man verzichtet bewusst auf den Schutz so einer static Variablen.
-
Frank Erdorf schrieb:
....
Noch etwas:
globale oder statische Variabeln sollten wann immer möglich vermieden werden.
...Aber irgendjemand muss die Instanz der Struktur mal irgendwann anlegen.
Wenn man diese lokal in einer anderen Funktion anlegt, dann darf diese niemals beendet werden, sonst ende auch die Lebensdauer der Stuktur.
Für Strukturen (oder Variablen allgemein) die über die gesamte Ausführungsdauer, also immer, existieren sollen gibt es dann nur zwei möglichkeiten (wenn man keine dyn. Speicherverwaltung wie malloc nutzt):
1. Struktur wird in der main() angelegt
2. Struktur wird (static oder extern) global angelegtDa 1. oft unpraktikabel ist (wer will schon immer die main() anpassen?) bleibt nur 2. übrig.
Geht jetzt aber am eigentlichen Thema vorbei: Die Performance von Zugriffen auf globale Variablen vs. Zugriffsfunktionen.
-
Hallo stefan-tiger,
"Die Set-Funktion könnte ja umfangreicher sein und irgendwelche Prüfungen durchführen. Das kann dann ein anderes Modul einfach umgehen."
Genau aus diesem Grund sollte man Funktionen anbieten,
auch wenn man z.Z. keine Prüfungen macht, kann ja noch kommen ...Wenn ein Modul alle erforderlichen Funktionen anbietet,
diese gut dokumentiert sind, ein Hinweis vorhanden ist nur über die Funktionen zu arbeiten und keine Fehler drin sind, ist es egal ob die Daten noch auf anderem Weg zugänglich sind. Wenn dann zusätzlich, Performance relevante, oder einfache Funktionen über defines realisiert sind,
ist auch der letze Grund genommen direkt auf das Struct zuzugreifen und niemand wird es dann doch noch tuen ...Wenn du 'verstecken' möchtest und kompiler untersützung beim
'verstecken' haben möchtest, geht es mit defines nicht.Gruß Frank
-
Man kann mit #defines aber durchaus ein bisschen rumtricksen. z.B. so ('ne spontane Idee):
int protectedVar = 42; int getVar() { return protectedVar; } void setVar(int i) { protectedVar = i; if(protectedVar < -42) protectedVar = -42; } #define protectedVar SYNTAX_ERROR
Ein (ungewollter) direkter Zugriff auf protectedVar ist so nicht mehr möglich. Außer man umgeht diesen Makro-Schutz absichtlich.
-
Rab-Bit schrieb:
Man kann mit #defines aber durchaus ein bisschen rumtricksen. z.B. so ('ne spontane Idee):
int protectedVar = 42; #define protectedVar SYNTAX_ERROR int getVar() { #undef protectedVar return protectedVar; #define protectedVar SYNTAX_ERROR } void setVar(int i) { #undef protectedVar protectedVar = i; if(protectedVar < -42) protectedVar = -42; #define protectedVar SYNTAX_ERROR }
Ein (ungewollter) direkter Zugriff auf protectedVar ist so nicht mehr möglich. Außer man umgeht diesen Makro-Schutz absichtlich.
Supi. Die Idee führe ich noch ein wenig weiter:
In einer *.c
int varWithExtremeSecretNameUUID839334f08ebd11dfa4ee0800200c9a66 = 42;
Im Header
int getVar() { extern int varWithExtremeSecretNameUUID839334f08ebd11dfa4ee0800200c9a66; return varWithExtremeSecretNameUUID839334f08ebd11dfa4ee0800200c9a66; } void setVar(int i) { extern int varWithExtremeSecretNameUUID839334f08ebd11dfa4ee0800200c9a66; if(i<-42) i=-42; varWithExtremeSecretNameUUID839334f08ebd11dfa4ee0800200c9a66 = i; }
Durch die lokale extern-Deklaration wird noch nichtmal irgend ein Intellisense es kennen.