[Performance] inline-Inspector vs. friend Direktzugriff
-
Ich habe eine extrem Performance-sensible Klasse.
Soll heissen, das im Idealfall mehrere Millionen Male pro Sekunde auf die class-data zugegriffen wird.Um die beste Performance zu erreichen hatte ich mir ueberlegt, die jeweiligen anderen Klassen mit dieser zu befreunden(friend), so dass sie direkt auf die class-data zugreifen koennen.
Da ich das nicht ganz so schoen finde, weil ich sehr viele Klassen dann befreunden muesste, habe ich mir gedacht einen inline-Inspector einzubauen, weil der ja eigentlich genau so schnell sein sollte.
Also:inline Class::getX() { return x; }
Ist die inline- Loesung genau so schnell, wie die friend Loesung?
Habt ihr sonst evtl. noch bessere Vorschlaege?
-
hoi Raptor
also wie der unterschied zwischen der friend und der inline is weiß ich leider nicht. braucht man ja nur mal ausprobieren.
was aber viel bringen kann:
wenn du in ner memberfunc oefters auf ne eigenschaft zugreifst, dann solltest auf die eigenschaft ne referenz oder einen pointer legen. das geht meines wissen schneller, weil, wenn du ja ueber den this-pointer drauf zu greifst. im normalfall is das wahrscheinlich egal, aber wenn, so wie bei dir, paar million mal in der sekunde darauf zugegriffen wird, dann kann das schon viel ausmachen. falls ich da jetz bloedsinn verzapfe, bitte korigieren.Meep Meep
-
Koenntest du bitte mal ein Beispiel geben?
Aus deinem Text kann ich das jetzt nicht ganz nachvollziehen.
Danke.
-
Hi
ich nehm an er meint lediglich dass deine inlinemethode den wert nicht by-value zurück geben sollte. Wird ja immerhin ne kopie gemacht dann.
-
ich könnte mir vorstellen, dass er sowas meint
class Kapsel { public: Kapsel(); int* getX(); private: int X; const int* pX; // Wo genau muss das Const hin, damit der Wert des Pointers // nicht verändert werden kann? }; Kapsel::Kapsel() : X(0) { pX = &X; } // so int* Kapsel::getX() { return pX; } // oder so int* Kapsel::getX() { return &X; }
Eigentlich könnte man das ja auch mit einer Referenz lösen.
-
@hehejo:
Also, ich wuesste nicht, was deine Variante an Performance bringen sollte...Uebrigens:
const int* pX; //Das Object, auf das gezeigt wird, kann nicht veraendert werden int* pX const; //Der Pointer kann nicht mehr veraendert werden.
-
re
ich meinte das folgend:
class klasse { private: int wert; ... public: int brauche_oft_wert_func(void); ... }; int klasse::brauche_oft_wert_func(void) { // wenn die variable wert nicht geaendert wird reicht es auch so: int t_wert = wert; wert = machwas(t_wert) - machwasanderes(t_wert); char *buffer = new char[t_wert]; for(int i = 0;i < t_wert;i++) { buffer[i] = t_wert + i; } }
in der oberen memberfunktion wird wert oft gebraucht. ob man es nun als temporaere copy von wert, ne referenz auf wert oder nen pointer benutzt, haengt vom davon ab, was man damit macht. auf jeden fall erspart man sich so den weg ueber den this-pointer. muesste man mal ausprobieren, wie viel das im endeffekt is.
Meep Meep
-
weils mich jetz doch mal genauer interessiert hab ich das mal ausgemessen. inwiefern das ein vernuenftiger test is, weiß ich nicht ;o)
hab mal folgendes getestet und mit rdtsc gemessen:
class klasse { private: int temp; int erg; public: void rechne1(void); void rechne2(void); }; void klasse::rechne1(void) { temp = 100000; erg = 0; for(int i = 0;i < temp;i++ erg += i; } void klasse::rechne2(void) { temp = 100000; int t_temp = temp; int t_erg = 0; for(int i = 0;i < t_temp;i++) t_erg += i; erg = t_erg; }
rechne1 brauchte bei mir ca. 660000 ticks und rechne2 ca. 220000 ticks. also grad mal ein drittel.
Meep Meep
-
Das ist ja alles schön und gut aber was hat das mit dem problem zu tun ?
Leider hab ich keine ahnung wo intern der unterschied zwischen dem zugriff direkt auf den member durch nen freund und nem zugriff per inline-methode. Das wär aber wichtig zu wissen, um hier aussagen treffen zu können, vll weiss ja einer von den pros mehr.
so far...
-
Meep Meep schrieb:
weils mich jetz doch mal genauer interessiert hab ich das mal ausgemessen. inwiefern das ein vernuenftiger test is, weiß ich nicht ;o)
hab mal folgendes getestet und mit rdtsc gemessen:ich hab deine test mal auch genommen.
hab die anzahl um faktor 100 vergrößert, damit ich feine sekundenanzahl bei mir kriege.#include <iostream> using namespace std; typedef unsigned long long u64; inline u64 rdtsc(){ u64 r; asm volatile( "rdtsc" :"=A" (r) ); return r; } class klasse { private: int temp; int erg; public: void rechne1(void); void rechne2(void); }; void klasse::rechne1(void) { temp = 10000000; erg = 0; for(int i = 0;i < temp;i++) erg += i; } void klasse::rechne2(void) { temp = 10000000; int t_temp = temp; int t_erg = 0; for(int i = 0;i < t_temp;i++) t_erg += i; erg = t_erg; } int main(){ { u64 start=rdtsc(); klasse k; k.rechne1(); u64 stop=rdtsc(); cout<<(stop-start)/4.e7<<endl; } { u64 start=rdtsc(); klasse k; k.rechne2(); u64 stop=rdtsc(); cout<<(stop-start)/4.e7<<endl; } }
ausgabe
2.80059
1.97064naja, rechne2 ist deutlich schneller.
aber andererseits ist deine optimierung so simpel, daß die sogar ein webdesigner machen könnte. mal schauen, ob der compiler das auch hinkriegt.
ich schalte -O3 ein.
ausgabe:
1.67002
0.584626krass!
also wir lernen: ein webdesigner ist klüger als ein compiler.
und wir lernen: der zugriff auf auto-variablen ist schneller als der zugriff auf attribute.
und jetzt, wo das tolle messprogramm steht, mal messen, ob der direkte zugriff auf attribute schneller ist als der zugriff über inspektoren.
void klasse::rechne1(void) { temp = 10000000; erg = 0; for(int i = 0;i < temp;i++) erg += i; } void klasse::rechne2(void) { temp = 10000000; erg = 0; for(int i = 0;i < getTemp();i++) erg += i; }
ausgabe:
1.75442
1.42221nee, ist nicht der fall. der inspektor ist schneller.
wenn ich in der main() die messungen vertausche:
1.90626
1.45198aha, der ist schneller, den ich zuerst gemessen habe.
das messprogramm müsste noch ein wenig verbessert werden. aber egal. inline-inspektoren oder dorektzugriff sind genausoschnell.
wenn man die werte lokale cachen kann, wirds nochmal schneller.
-
Raptor schrieb:
Uebrigens:
const int* pX; //Das Object, auf das gezeigt wird, kann nicht veraendert werden int* pX const; //Der Pointer kann nicht mehr veraendert werden.
Fast.
const int* pX; //Das Object, auf das gezeigt wird, kann nicht veraendert werden int* const pX; //Der Pointer kann nicht mehr veraendert werden.
Sieht schon besser aus.
-
Meep Meep schrieb:
weils mich jetz doch mal genauer interessiert hab ich das mal ausgemessen. inwiefern das ein vernuenftiger test is, weiß ich nicht ;o)
hab mal folgendes getestet und mit rdtsc gemessen:
class klasse { private: int temp; int erg; public: void rechne1(void); void rechne2(void); }; void klasse::rechne1(void) { temp = 100000; erg = 0; for(int i = 0;i < temp;i++ erg += i; } void klasse::rechne2(void) { temp = 100000; int t_temp = temp; int t_erg = 0; for(int i = 0;i < t_temp;i++) t_erg += i; erg = t_erg; }
rechne1 brauchte bei mir ca. 660000 ticks und rechne2 ca. 220000 ticks. also grad mal ein drittel.
Meep Meep
So wie das da steht verwundert mich das doch erheblich. Hätte nicht gedacht, dass die Compiler da noch einen Unterschied lassen.
Dann hab ich das mal mit dem icc 8.1 getestet, und wenn man da die Optimierung einschaltet, erkennt der Compiler, dass in den Funktionen nichts getan wird und optimiert diese ganz weg.
-
womit der restliche sinn, meines eh schon sinnlfreien codes vollkommen entsorgt worden waere.
Meep Meep
-
volkard schrieb:
Meep Meep schrieb:
weils mich jetz doch mal genauer interessiert hab ich das mal ausgemessen. inwiefern das ein vernuenftiger test is, weiß ich nicht ;o)
hab mal folgendes getestet und mit rdtsc gemessen:ich hab deine test mal auch genommen.
hab die anzahl um faktor 100 vergrößert, damit ich feine sekundenanzahl bei mir kriege.Ist es überhaupt sinnvoll so große Zeiträume mit rdtsc zu messen? Da läuft doch soviel anderes Zeug, dass das Ergebnis stark verfälscht sein muss, oder nicht?
-
klar wird es verfaelscht.
aber ob ich jetz mit der systemzeit messe, oder mit rdtsc is ja egal. hab ja dann auch nur gerundete werte angegeben und nicht auf den tick genau. ich habs mit halt angewoeht mit rdtsc zu messenMeep Meep
-
Meep Meep schrieb:
klar wird es verfaelscht.
aber ob ich jetz mit der systemzeit messe, oder mit rdtsc is ja egal. hab ja dann auch nur gerundete werte angegeben und nicht auf den tick genau. ich habs mit halt angewoeht mit rdtsc zu messenMeep Meep
Ich meinte auch die ein-zwei Sekunden von volkard und nicht deine Paar Takte.
-
jo hab falsch gelesen.
womit und wie wuerdest du messen ?
Meep Meep
-
Meep Meep schrieb:
ich habs mit halt angewoeht mit rdtsc zu messen
ganz falscher ansatz.
du kannst mit rdtsc durchaus solche funktionen, die die chance haben, einmal durchzulaufen, ohne weggeschaltezt zu werden, also sagen wir mal mit laufzeiten im millisekundenbereich (sind ja noch 1000000 takte) auf 5 stellen genau ausmessen mit der einfachen schleifeint left=1000; time mintime=maxvalue<time>(); while(++left){ time t=measure(funktion); if(t<mintime){ mintime=t; left=1000; } }
das mache ich immer beim wpc, wenn speed das kriterium ist. und es funktioniert wunderbar.
-
koenntest du mir da mal erklaren was du da genau machst ? vorallem, was is maxvalue und die whileschleife wuerd mich interessieren
Meep Meep
-
Meep Meep schrieb:
jo hab falsch gelesen.
womit und wie wuerdest du messen ?
Meep Meep
Ich glaube im Bereich zwischen ganz kurz und lang gibt es keine gute Methode die CPU Zeit eines Prozesses zu messen. Für sehr kurze Bereiche kann man wohl rdtsc benutzen und für lange Bereiche times() oder getrusage(). Ich hab auch gelesen, dass der Kernel die CPU Zeit eines Prozesses nicht ganz korrekt mitführt.
Wenn es denn funktionieren würde kann man auch clock_gettime(CLOCK_PROCESS_CPUTIME_ID, &ts) nehmen, aber mir ist das noch nicht untergekommen.