Parameter per Pointer übergeben?



  • Wo ist denn jetzt der genaue Unterschied, ob ich Parameter (insbesondere große Objekte) per Pointer oder als Referenz übergebe? Was ist denn schneller??

    Ich ruf in meinem Game eine Funktion 150 Mio mal pro Sekunde auf, sie soll einen bool-Wert zurückliefern.
    Ist es besser, anstatt return (bool) zu schreiben, die Funktion zu void zu machen und eine globale Variable einzustellen? Oder nen Pointer auf ne Variable zu übergeben, wo dann der Rückgabewert gespeichert wird?



  • Pointer und Referenzen sollten gleich schnell sein.

    Du solltest Referenzen gegenüber Pointern bevorzugen, da du

    1. kein Problem mit NULL Pointern oder ähnlichem hast.
    2. Pointer für mich eigentlich bedeuten, dass ein Array erwartet wird oä. Bei Referenzen ist das eindeutig



  • Hm, viele verwenden einen Pointer auch um deutlich zu machen das die Daten geändert werden.

    changeA(T & a)
    {
      a = value;
    }
    
    changeB(T & b)
    {
     b = value;
    }
    
    T obj;
    
    changeA(obj);   // Value oder Referenz ? Aus den Augen aus dem Sinn ;o)
    changeB(&obj);  // Hier macht man sich mehr gedanken drum ob was geändert wird
    

    Is jetzt aber ne Subjektive erfahrung.



  • Wenn ich ne Funktion inline hab, werden dann die Parameter immer noch ganz normal übergeben (d.h. bei call byRef Referenzen erstellt)? In dem Fall ist es doch sicher besser ne globale Variable zu verwenden, oder?

    Sind eigentlich public static Klassenmember genauso schnell wie globale Variablen (was nach meinem Verständniss auch sind)?

    Fragen über Fragen 😃



  • Optimizer schrieb:

    Ist es besser, anstatt return (bool) zu schreiben, die Funktion zu void zu machen und eine globale Variable einzustellen? Oder nen Pointer auf ne Variable zu übergeben, wo dann der Rückgabewert gespeichert wird?

    Globale Variable scheidet aus, wenn du C++ programmierst (frag jetzt bitte nicht warum!).

    Pointer auf eine Rückgabe-Variable ist auch nicht gut. Denn Parameter werden mit push auf den Stack geschoben, und das dauert lange. Der Rückgabewert (bool) dagegen wird in einem Register übergeben, was erheblich schneller ist.

    p.s.:
    Ich weiß, es gibt bei VC++ __fastcall; da werden einzelne Parameter in Register gesteckt. Ich glaube aber nicht, dass das bei so einem Parameter gut funktioniert. Wenn die Funktion sehr kurz ist, kommt es im besten Fall aufs selbe wie return (bool) raus.

    Optimizer schrieb:

    Wenn ich ne Funktion inline hab, werden dann die Parameter immer noch ganz normal übergeben (d.h. bei call byRef Referenzen erstellt)?

    In solch einem Fall helfen grundlegende Assembler-Kenntnisse. 🙂
    Referenzen sind implementationsabhängig.
    Bei x86-CPUs liegen auf dem Stack sowieso schon "Referenzen", nämlich Zeiger. Um auf diese Zeiger zuzugreifen, muss das Programm den Stackzeiger dereferenzieren. Es sind also so oder so Zeiger (Referenzen).

    Optimizer schrieb:

    In dem Fall ist es doch sicher besser ne globale Variable zu verwenden, oder?

    s.o.

    Optimizer schrieb:

    Sind eigentlich public static Klassenmember genauso schnell wie globale Variablen (was nach meinem Verständniss auch sind)?

    Implementationsabhängig, ziemlich sicher aber ja.



  • 150 Mio. pro Sekunde? da würd ich es aber inline machen.

    Ansonst: boolean durch einen Pointer ersetzen, geht in die Hose, da ein Pointer größer als ein boolean ist. Macht also keinen Sinn.

    Reference/Pointer anstatt Objekte zu übergeben, macht man ja hauptsächlich nur, weil ein Objekt meistens sehr groß ist (es wird ansonst das GANZE Objekt übergeben... sehr böse).

    Hem, wenn ich deutlich machen will, das ein Parameter nicht geändert wird, mache ich ihn const. Wenn er nicht const übergeben wird, kann der User sehen das er geändert wird:

    void funct(const ClassA &a, ClassB &b); // a wird in der Funktion nicht geändert, b dagegen wohl schon
    

    Mit Pointer deutlich zu machen, das es eine Änderung geben wird? Sehr merkwürdig...



  • p.s.:
    Für einen Rückgabewert solltest du aber keine static Elementvariable benutzen! Dann kannst du nämlich auch gleich globale (inklusive namespace) nehmen, das ist genauso schlimm.
    Versuch ohne globale Variablen auszukommen.

    btw:
    Bist du dir sicher, dass diese Funktion ein Flaschenhals ist? Hast du einen Profiler benutzt?



  • Artchi schrieb:

    Ansonst: boolean durch einen Pointer ersetzen, geht in die Hose, da ein Pointer größer als ein boolean ist. Macht also keinen Sinn.

    So argumentiert macht es Sinn Pointer zu benutzen. Heutige CPUs arbeiten mit 32Bit-Werten (Pointer) gleich schnell oder sogar schneller als mit 8Bit-Werten (bool).
    Das pushen aus den Stack verursacht die hohen Kosten bei Parametern.



  • Nein ich habe keinen Profiler benutzt, aber ich weiß es trotzdem.



  • Optimizer schrieb:

    Nein ich habe keinen Profiler benutzt, aber ich weiß es trotzdem.

    <Loriot>
    Ach!
    </Loriot>

    Ohne Profiler kannst du sowieso nichts sagen. Es sei denn, du hast nur ein Minimalprogramm. Alle Objekte mit nicht-trivialem Copy-Ctor sollten wenn möglich mit const &Obj in der Paramaterliste arbeiten. Simple Zeiger sind ein Relikt aus C und sollten eher selten benutzt werden. Aber z.B Array sind immer Zeiger, da muss man dann auch nicht noch mal refernzieren.

    Gruß Tobias



  • Was macht die Funktion denn? Vielleicht liegt der Flaschenhals gar nicht in der Parameterübergabe, sondern in der Funktion selbst.

    Ein API-Aufruf an der falschen Stelle kann enorm bremsen.

    Oder lag es doch an den Parametern?



  • Es liegt schon irgendwie an der Funktion UND an den Parametern. Früher hab ich mehr Parameter übergeben, das war noch langsamer. Im Funktionscode selber weiß ich aber echt gar nichts mehr zum optimieren, ich habe durch das inline nochmal einiges rausgeholt, aber davon hat man nichts im Debug-Build.

    EDIT: Ist denn bei VS.Net ein Profiler dabei?



  • EDIT: Ist denn bei VS.Net ein Profiler dabei?

    RTFM!

    Ich ruf in meinem Game eine Funktion 150 Mio mal pro Sekunde auf, sie soll einen bool-Wert zurückliefern.

    Hoffentlich nicht 150 Mio mal den selben 🙂

    Wenn ich ne Funktion inline hab, werden dann die Parameter immer noch ganz normal übergeben

    Wenn sie tatsächlich geinlined wird, nein.

    In dem Fall ist es doch sicher besser ne globale Variable zu verwenden, oder?

    Nö.

    Sind eigentlich public static Klassenmember genauso schnell wie globale Variablen

    Genauso schnell müde? Genauso schnell besoffen? Genauso schnell beleidigt? Was ist das eigentlich für eine Frage?
    Willst du wissen, ob der Zugriff auf eine gloable Variable genauso schnell ist wie der auf eine statische Klassenvariable?
    Sicher. Bedenke: Klassen gibt es nur auf Quellcodeebene.

    Mit Pointer deutlich zu machen, das es eine Änderung geben wird? Sehr merkwürdig...

    Alte C-Schule. Angst vor Information hiding 🙂

    aber davon hat man nichts im Debug-Build.

    Im Debug-Build versucht man in der Regel auch nicht auf dieser low-level-Ebene zu optimieren. Erstmal sollte das Programm fehlerfrei laufen.



  • Willst du wissen, ob der Zugriff auf eine gloable Variable genauso schnell ist wie der auf eine statische Klassenvariable?

    Was denn bitte sonst?!

    Hoffentlich nicht 150 Mio mal den selben 🙂

    Nein ich bin noch nicht ganz bescheuert 🙂

    Ohne Profiler kannst du sowieso nichts sagen.

    Klar kann ich was ohne Profiler sagen. Ich baue an mehreren Punkten in meinem Programm Zeitmessungen ein und deshalb weiß ich, dass die Funktion der Flaschenhals ist. Und ich weiß sogar ganz genau, welche Stelle in der Funktion der Flaschenhals ist. Das kleine Problem ist, dass es inzwischen kaum noch möglich sein sollte diese Zeilen zu optimieren (ich hab sogar schon mal den Quellcode hier gepostet). Deshalb wollte ich wissen, ob man vielleicht noch am Funktionsaufruf selber etwas optimieren kann.



  • [quote=cd9000]
    p.s.:
    Ich weiß, es gibt bei VC++ __fastcall; da werden einzelne Parameter in Register gesteckt. Ich glaube aber nicht, dass das bei so einem Parameter gut funktioniert. Wenn die Funktion sehr kurz ist, kommt es im besten Fall aufs selbe wie return (bool) raus.[/quote]

    es gibt auch so ein Keyword im C++ Standard AFAIK register heisst das.

    Aber das sollte man nicht benutzen, da der Compiler eh besser wissen sollte, was er wohin steckt IMHO



  • register ist für Variablen, __fastcall für Parameter und Rückgabewerte. Kommt aber so oder so nicht gegen inline an.



  • Also, falls es noch jemanden interressiert, ich habe jetzt die Probe auf's Exempel gemacht:

    const DWORD start1 = GetTickCount();
    	for (int i = 0; i < 2000000; i++)
    		CanGo2(GoPos);
    	const DWORD time1 = GetTickCount() - start1;
    
    	const DWORD start2 = GetTickCount();
    	for (int i = 0; i < 2000000; i++)
    		CanGo(&GoPos);
    	const DWORD time2 = GetTickCount() - start2;
    

    Ergebnis: Wie von kingruedi vorhergesagt, sind Referenzen scheinbar genauso schnell wie Pointer. Der Rumpf der beiden Funktionen ist gleich, außer dass bei der Variante mit Pointer natürlich immer GoPos->x statt GoPos.x steht.

    Bleibt noch anzumerken, dass ich den Test mit dem Debug-Build gemacht habe, die Funktionen sind also nicht inline.



  • Moment mal, wie kann das sein, ich hab jetzt zum Spaß mal das Objekt by value übergeben und das braucht nur 3/4 der Zeit ??

    const DWORD start1 = GetTickCount();
    	for (int i = 0; i < 2000000; i++)
    		CanGo(GoPos);
    	const DWORD time1 = GetTickCount() - start1;
    
    	const DWORD start2 = GetTickCount();
    	for (int i = 0; i < 2000000; i++)
    		CanGo2(GoPos);
    	const DWORD time2 = GetTickCount() - start2;
    
    __forceinline bool CanGo(const PointFloat &posToGo);
    	__forceinline bool CanGo2(const PointFloat posToGo);
    

    Ich übergebe immerhin ein 40 Byte großes Objekt!
    Während time1 meist einen Wert um 12000 hat, liegt time2 bei 8900.

    Die einzige Erklärung die ich dafür hab, ist, dass der Compiler doch den Wert als Referenz übergibt (kann er ja machen wegen dem const) und noch irgendwas anderes optimiert. Die Funktionsrümpfe sind absolut identisch.



  • Zeig doch mal den relevanten Assemblercode der beiden Funktionen.



  • Werd ich mal machen. Aber ich muss die Funktion erst noch in kleinere Funktionen unterteilen (dürft ja bei inline keine Nachteile haben). Sonst kriegst du 20 Seiten Assembler-Code 🙂

    EDIT: Irgendwie war das jetzt auch eine besondere Situation. Inzwischen hab ich mit by value nur noch 1 Sekunde Vorteil auf 11 Sekunden.

    EDIT 2: Ok, Fehlalarm. Im Release-Build hat alles seine Richtigkeit. Dort ist die Funktion mit Referenz ein bisschen schneller.


Anmelden zum Antworten