Ich will die Laufzeit und den Speicherverbrauch der Funktion einer Klasse optimieren.
Im folgenden Beispiel gibt es die Funktion "exec" mit einem Eingang und einem Ausgang. Wird die Ausführungszeit kleiner wenn man für den Eingang den direkten Wert statt des Pointers nimmt?
Der Compiler ist gut. Er scheint die Klasse vollständig wegzuoptimieren und hält die Variablen in den Registern. Also kann man vermutlich nichts optimieren, wenn man den Input statt als Zeiger als Pointer übergibt.
Der Compiler ist gut. Er scheint die Klasse vollständig wegzuoptimieren
"Klassen" kann man sowieso nicht wegoptimieren, weil "Klassen" nicht im Assemblercode landen; Klassen sind ein abstraktes Konzept, im Assemblercode landen nur Funktionen. In deinem Fall wurde die Funktion inlined und somit der Pointer wegoptimiert; das passiert nicht immer, z.B. wenn die Funktion größer ist und mehrfach aufgerufen wird oder rekursiv ist.
Allerdings: (Kleine) Input-Daten per Pointer übergeben ist relativ sinnlos, für den Leser verwirrend und sollte schon eher vermieden werden. Mindestens mal sollte ein "const" dran. Kleine Output-Daten sollte man auch eher per Rückgabewert zurückgeben, nicht per Pointer.
Dann kann man sich überlegen, ob man einen Ausgang als Rückgabewert nutzen möchte, weil es der "primäre" Ausgang ist o.ä.
Außerdem kannst du Referenzen nutzen:
1
voidexec(constint&input,int&outputA,int&outputB)
2
{
3
outputA=input+1;
4
outputB=input+2;
5
}
6
7
X1.exec(Var1,Var2,Var3);
8
X2.exec(Var2,Var3,Var4);
9
X3.exec(Var3,Var4,Var1);
Die sind sehr ähnlich zu Pointern, signalisieren (dem Leser) aber eindeutig dass es nur ein einzelner Wert (und kein Array) ist, dass sich die Adresse nicht ändert, und dass die Adresse gültig ist (und nicht NULL/nullptr sein kann wie bei Pointern).
Die Referenzen sehen auf jeden Fall nicht schlecht aus. Kann es sein, dass es die nicht schon immer in C++ gibt? Die Frage wäre, ob sie auf jedem System kompiliert werden.
Wenn mehrere Elemente zusammen gehören, faßt man sie gerne als Struct zusammen. Das verbessert einmal die Lesbarkeit und man muß nur einen Pointer auf die Struct übergeben, anstelle haufenweise einzelne Pointer oder einzelne Variablen.
Der Link ist einfach ein Hinweis, dass die Ausgangsfrage ziemlich getrollt ist.
Deswegen war der auch nur als guter Rat gemeint. Wird doch wohl erlaubt sein?
Klasse ist einfach nicht der passende Begriff, um ihn auf Lowlevel-Ebene zu untersuchen.
Zur Auseinandersetzung mit Klassen ist bei Java ein, wie ich finde, besserer Backround da.
"Klassen" kann man sowieso nicht wegoptimieren, weil "Klassen" nicht im
Assemblercode landen
Man kann Klassen allerdings sehr unterschiedlich implementieren. Sie brauchen dann mehr oder weniger Speicher und sind mehr oder weniger effizient. Bei zeitkritischen Systemen gibt es da große Unterschiede.
Es ging wohl um den TO, dessen Beispiele weder lesbarer noch performanter sind als ein naiver, direkter Ansatz. (Dass wir seine eigentliche Aufgabe nicht kennen, geschenkt)
Der Compiler ist gut. Er scheint die Klasse vollständig wegzuoptimieren
"Klassen" kann man sowieso nicht wegoptimieren, weil "Klassen" nicht im
Assemblercode landen;
Anführungszeichen braucht man nicht.
Klassen sind ein abstraktes Konzept, im Assemblercode landen nur
Funktionen.
Eigentlich gibt es auch "Funktionen" in dem Sinne nicht. Sowas wie Funktionsparameter oder einen Returnwert kennt Assembler nicht, geschweige denn Exceptions und diverse andere Konzepte, die C++ bei Funktionen kennt. Es gibt nur den Sprung zu einem Ziel und den Rücksprung, der automatisch zur ursprünglichen Stelle springt.
In deinem Fall wurde die Funktion inlined und somit der Pointer
wegoptimiert; das passiert nicht immer, z.B. wenn die Funktion größer ist
und mehrfach aufgerufen wird oder rekursiv ist.
Oder wenn der Compiler den Inhalt der Funktion an der Stelle nicht kennt.
Allerdings: (Kleine) Input-Daten per Pointer übergeben ist relativ
sinnlos, für den Leser verwirrend und sollte schon eher vermieden
werden. Mindestens mal sollte ein "const" dran.
Eigentlich sehe ich bei solchen Daten nur gerade dann einen Sinn in einem Zeiger, wenn sie nicht const sind, so dass die Funktion da was reinschreiben kann.
Kleine Output-Daten sollte man auch eher per Rückgabewert zurückgeben,
nicht per Pointer.
Die Pointer-Variante wird gerne verwendet, wenn mehrere Sachen auf einmal zurückgegeben werden müssen. Allerdings geht das in modernem C++ auch leicht über den Returnwert, ohne dass man dazu extra gleich einen eigenen Typ definieren muss:
Es gibt nur den Sprung zu einem Ziel und den
Rücksprung, der automatisch zur ursprünglichen Stelle springt.
Naja, nicht ganz. Man hat auch Übergabeparameter, Registersicherungen,-Wiederhervorholungen, oder auch Rückgabeparameter drin, wenn man möchte.
Gruppengeschichten kann man über Moduleinrichtung organisieren, Auto-Geschichten u.a. über die beim Intel (Index-Register). Beim mc muss man sich sowas eher selber einrichten. Je nachdem, was die Hardware so im Angebot hat.
Außerdem da auch Möglichkeiten, zu entscheiden, ob man die Rechnerei mehr über die Register laufen lassen möchte, oder mehr lieber mit viel Speicherzugriffen, die aber üblicherweise Zeit kosten.
Müsste man sich aber im Einzelfall genauer ansehen. Mit Generalisierungen kann man hier kaum einen Blumentopf gewinnen.
Eigentlich sehe ich bei solchen Daten nur gerade dann einen Sinn in
einem Zeiger, wenn sie nicht const sind, so dass die Funktion da was
reinschreiben kann.
1
voidexec(constint&input,int&outputA,int&outputB)
Ich hoffe, das "const" bezieht sich auf den Zeiger und nicht auf den Inhalt, auf den er zeigt.
Ich hoffe, das "const" bezieht sich auf den Zeiger und nicht auf den
Inhalt, auf den er zeigt.
Das sind Referenzen und nicht Zeiger, und die Referenz selber ist immer "konstant", also man kann die Adresse nicht ändern (anders als beim Pointer). Somit ist "const int&" immer eine (automatisch konstante) Referenz auf einen konstanten Integer.
"const int*" - Nicht-konstanter Zeiger auf konstantes int
"int* const" - Konstanter Zeiger auf nicht-konstantes int
"const int* const" - Konstanter Zeiger auf konstantes int
"int&" - (Automatisch konstante) Referenz auf nicht-konstantes int
"const int&" - (Automatisch konstante) Referenz auf konstantes int
Die Referenzen sehen auf jeden Fall nicht schlecht aus. Kann es sein,
dass es die nicht schon immer in C++ gibt? Die Frage wäre, ob sie auf
jedem System kompiliert werden.
Wie lange programmierst du schon in C++?
Egal, wie lange, Referenzen gibt es länger…
Reflexartig Referenzen zu verwenden is m.E. keine gute Idee.
Besser:
1
voidexec(intinput,int&outputA,int&outputB)
2
{
3
outputA=input+1;
4
outputB=input+2;
5
}
Erklärung:
"input" belegt nur 2 Bytes und wird von exec nicht geändert, man kann es also ebenso gut per Value übergeben.
Wenn exec geinlined wird, vertut man sich nix und der erzeugte Code ist vermutlich der gleiche (modulo Compiler-Quirx), der C++ Code liest sich aber leichter.
Wenn exec jedoch nicht geinlined wird, ist const int &input teuer, weil (effektiv) die Adresse einer Variablen übergeben wird, und um die Adresse eines auto-Objekts nehmen zu können, muss es im Speicher liegen; hier also im Frame der Funktion. Und das ist wirklich teuer: Zum einen muss ein Frame angelegt werden, das Objekt darin gespeicher werden (auch Konstanten!), zum anderen muss die Adresse berechnet werden. Und der Callee muss derefernzieren, was ebenfalls teuer ist: Adress-Register und RAM-Zugriff werden erforderlich.
Selbst wenn input ein 32-Bit Wert ist, dürfte es effizienter sein, per Value zu übergeben statt per Referenz.
Für output-Operanden gilt evtl. ähnliches: ein int besser als Rückgabewert statt als Referenz. Oder gar ein struct mit 2 ints (kann avr-gcc in Registern halten).
Es gibt nur den Sprung zu einem Ziel und den
Rücksprung, der automatisch zur ursprünglichen Stelle springt.
Naja, nicht ganz. Man hat auch Übergabeparameter,
Registersicherungen,-Wiederhervorholungen, oder auch Rückgabeparameter
drin, wenn man möchte.
Bei welcher Architektur? In der Regel gibt es sowas auf Assenbler-Ebene nicht. Man muss das selber z.B. auf Basis von Registern oder dem Stack nachbilden.
Gruppengeschichten kann man über Moduleinrichtung organisieren,
Auto-Geschichten u.a. über die beim Intel (Index-Register).
Ich weiß nicht genau, was du damit meinst.
Außerdem da auch Möglichkeiten, zu entscheiden, ob man die Rechnerei
mehr über die Register laufen lassen möchte, oder mehr lieber mit viel
Speicherzugriffen, die aber üblicherweise Zeit kosten.
In der Regel will man sie so viel wie möglich über Register machen, eben weil die Speicherzugriffe zusätzliche Zeit kosten und sonst eigentlich keinen Vorteil haben.