Optimierung über Zeiger
-
-Bring es viel wenn man an einer schwierigen Stelle im Code alle Variablen durch Zeiger erstetzt.
-Und wiviel kosten casten? Ich habe das Problem das ich die Berechnungen in flot brauche doch das Ergebiss später auf ein array zugreifen muss. Also benötog ich wieder int ( array[int][int] ) Und das einige hundert mal pro Frame.
-
Variablen durch Zeiger ersetzen - Wie meinen?
Wenn du auf nem int mithilfe eines Zeigers zugreifst, dürfte das nicht schneller sein. Eher noch langsamer. Zeiger machen Sinn, wenn du ein großes Objekt einer Funktion übergibst. Das dauert Zeit, da geht es dann schneller, nur nen Zeiger auf das Objekt zu übergeben.
In den meisten Fällen ist es aber schöner und sicherer, per Referenz übergeben, technisch ist es das Gleiche.Könnt sich auch bei arrays lohnen:
array[a][b][c][d].member1 = 1; array[a][b][c][d].member2 = 2; array[a][b][c][d].member3 = 3;
stattdessen ist es vielleicht besser, ne Referenz auf arry[a][b][c][d] zu erstellen.
Und casten dauert lange. Vermeide es so gut es geht.
-
Casten ist wirklich extrem schlecht. Siehe: http://www.fh-merseburg.de/~roesch/trash/InttVSFloat.exe
( Source: http://www.fh-merseburg.de/~roesch/trash/InttVSFloat.cpp )
Bye, TGGC
-
@Optimizier:
Ein Freund von mir hat die seltsame Entdeckung gemacht, dass Referenzen generell langsamer sind als Zeiger. Scheinbar ist es eben technisch gesehen nicht das gleiche, also der Code wird irgendwie anders übersetzt.
-
Diese Entdeckung solltest du mal überprüfen. Weil ich habe die Entdeckung gemacht, dass sie gleich schnell sind
-
Nachtrag:
#include <iostream> #include <windows.h> using namespace std; struct MyStruct { int x; int y; int z; double u; double v; double w; }; void FunctionPointer(MyStruct *param) { param->u = 10.0; param->y = 2; param->z = 10; } void FunctionRef(MyStruct ¶m) { param.u = 10.0; param.y = 2; param.z = 10; } int main() { MyStruct a; const DWORD start1 = GetTickCount(); for (int i = 0; i < 50000000; ++i) FunctionPointer(&a); const DWORD time1 = GetTickCount() - start1; cout << "Pointer: " << time1 << "ms\n"; const DWORD start2 = GetTickCount(); for (int i = 0; i < 50000000; ++i) FunctionRef(a); const DWORD time2 = GetTickCount() - start2; cout << "Referenzen: " << time2 << "ms\n"; char b; scanf(&b); }
Bei mir sind beide Werte immer ziemlich genau auf 3500.
P4 2,5GHz, Visual Studio 7.0
-
Hier ist das Programm von meinem Kumpel:
#include <cstdio> #include <windows.h> #pragma comment(lib, "winmm.lib") #ifdef _DEBUG #define MILL 50 #else #define MILL 500 #endif class A { public: int Value; } DefaultA; class B { public: A& a; B() : a(DefaultA) {} }; class C { public: A* a; C() : a(NULL) {} }; int main() { LONGLONG Frequ; LONGLONG Start; LONGLONG End; LONGLONG Time0, Time1, Time2, Time3; unsigned long i,j; A a1, a2, a3; a1.Value = 1230321; a2.Value = 2340432; a3.Value = 3450543; B b; C c; C d; QueryPerformanceFrequency((LARGE_INTEGER*)(&Frequ)); //-------------------------------------------------- printf("\n %i Million Memory Accesses\n-----------------------------\n", MILL * 3); //-------------------------------------------------- int w = 0; QueryPerformanceCounter((LARGE_INTEGER*)(&Start)); for (j = 0; j < MILL; j++) for (i = 0; i < 1000000; i++) { d.a = &a1; w += (*d.a).Value + w; d.a = &a2; w += (*d.a).Value + w; d.a = &a3; w += (*d.a).Value + w; } QueryPerformanceCounter((LARGE_INTEGER*)(&End)); Time0 = End - Start; //-------------------------------------------------- int x = 0; QueryPerformanceCounter((LARGE_INTEGER*)(&Start)); for (j = 0; j < MILL; j++) for (i = 0; i < 1000000; i++) { c.a = &a1; x += c.a->Value + x; c.a = &a2; x += c.a->Value + x; c.a = &a3; x += c.a->Value + x; } QueryPerformanceCounter((LARGE_INTEGER*)(&End)); Time1 = End - Start; //-------------------------------------------------- int y = 0; QueryPerformanceCounter((LARGE_INTEGER*)(&Start)); for (j = 0; j < MILL; j++) for (i = 0; i < 1000000; i++) { b.a = a1; y += b.a.Value + y; b.a = a2; y += b.a.Value + y; b.a = a3; y += b.a.Value + y; } QueryPerformanceCounter((LARGE_INTEGER*)(&End)); Time2 = End - Start; //-------------------------------------------------- int z = 0; QueryPerformanceCounter((LARGE_INTEGER*)(&Start)); for (j = 0; j < MILL; j++) for (i = 0; i < 1000000; i++) { z += a1.Value + z; z += a2.Value + z; z += a3.Value + z; } QueryPerformanceCounter((LARGE_INTEGER*)(&End)); Time3 = End - Start; //-------------------------------------------------- printf(" Pointer *.: %f\n Pointer ->: %f\n Reference: %f\n Direct: %f\n\n", (double)(Time0) / (Frequ), (double)(Time1) / (Frequ), (double)(Time2) / (Frequ), (double)(Time3) / (Frequ)); return 0; }
-
@Optimizer
Ich habe das Programm einfach kopiert aber bei mir kommt beides immer 0 ...@Thomas Riker
Überall 0.000001Athlon XP 2600+ Visual Studio .net 2003
Ist das normal ?
-
Vielleicht stimmt da was mit dem Timer nicht?!
-
Du darfst keine release-Build machen, sonst werden die Schleifen komplett wegoptimiert.
Ich probier dein Programm Morgen mal aus.
-
@Optimizer: Benchmark macht keinen Sinn, wenn man ihn nicht in Release kompilieren darf.
@TomasRiker: Das Beispielprogramm ist IMHO nicht aussagekräftig, da es zu stark optimiert werden kann. Der Compiler könnte die Variablen b,c und d komplett eliminieren, muss es aber nicht. Ausserdem wird ein wichtiger Vorteil von Referenzen nicht beachtet, man muss nie testen ob sie != 0 sind. Das kann entscheidenter sein, als der eigentliche Zugriff.
-
TGGC schrieb:
Ausserdem wird ein wichtiger Vorteil von Referenzen nicht beachtet, man muss nie testen ob sie != 0 sind. Das kann entscheidenter sein, als der eigentliche Zugriff.
Ob eine Variable null sein kann, bzw. ob man sie testen muss, hängt aber von der Programmlogik ab. Man könnte jede Referenz durch einen Zeiger ersetzen und müsste dann das Erwartete in einem Kommentar schreiben. Das ist natürlich Blödsinn, also nimmt man einfach dort einen Zeiger wo er auch null sein kann und ansonsten nimmt man Referenzen.
-
Habe jetzt beide nochmal getestet und mit Debug läuft es. Aber bei mir sind Referenzen schneller. 0.69 Referenzen und 0.89 Pointer.
-
Das Problem mit dem Release-Build ist, dass man mit den Variablen irgendwas sinnvolles machen muss, z.B. ausgeben.
Der Compiler optimiert wirklich gnadenlos ALLES sonst weg.
Diese 'sinnvollen' Sachen aber könnten das Ergebnis verfälschen. Andererseits werden bei Debug-Builds z.B. Funktionen nicht geinlined.@Tomas:
Generell MÜSSTEN Referenzen und Zeiger gleich schnell sein. Ich weiß jetzt nicht genau, was der Standard dazu sagt, aber eine Referenz ist AFAIK nichts weiter als ein automatisch dereferenzierter Zeiger, der seinen Wert nicht ändern kann.
Was sind denn bei Euch für Werte rausgekommen?
-
#include <cstdio> #include <windows.h> #pragma comment(lib, "winmm.lib") #ifdef _DEBUG #define MILL 50 #else #define MILL 1500 #endif class A { public: int Value; } DefaultA; class B { public: A& a; B() : a(DefaultA) {} }; class C { public: A* a; C() : a(NULL) {} }; int main() { LONGLONG Frequ; LONGLONG Start; LONGLONG End; LONGLONG Time0, Time1, Time2, Time3; unsigned long i,j; A a1, a2, a3; a1.Value = 1230321; a2.Value = 2340432; a3.Value = 3450543; B b; C c; C d; QueryPerformanceFrequency((LARGE_INTEGER*)(&Frequ)); //-------------------------------------------------- printf("\n %i Million Memory Accesses\n-----------------------------\n", MILL * 3); //-------------------------------------------------- int w = 0; QueryPerformanceCounter((LARGE_INTEGER*)(&Start)); for (j = 0; j < MILL; j++) for (i = 0; i < 1000000; i++) { d.a = &a1; w += (*d.a).Value + w; d.a = &a2; w += (*d.a).Value + w; d.a = &a3; w += (*d.a).Value + w; } QueryPerformanceCounter((LARGE_INTEGER*)(&End)); Time0 = End - Start; QueryPerformanceCounter((LARGE_INTEGER*)(&Start)); for (j = 0; j < MILL; j++) for (i = 0; i < 1000000; i++) { d.a = &a1; w += (*d.a).Value + w; d.a = &a2; w += (*d.a).Value + w; d.a = &a3; w += (*d.a).Value + w; } QueryPerformanceCounter((LARGE_INTEGER*)(&End)); Time0 = End - Start; //-------------------------------------------------- int x = 0; QueryPerformanceCounter((LARGE_INTEGER*)(&Start)); for (j = 0; j < MILL; j++) for (i = 0; i < 1000000; i++) { c.a = &a1; x += c.a->Value + x; c.a = &a2; x += c.a->Value + x; c.a = &a3; x += c.a->Value + x; } QueryPerformanceCounter((LARGE_INTEGER*)(&End)); Time1 = End - Start; //-------------------------------------------------- int y = 0; QueryPerformanceCounter((LARGE_INTEGER*)(&Start)); for (j = 0; j < MILL; j++) for (i = 0; i < 1000000; i++) { b.a = a1; y += b.a.Value + y; b.a = a2; y += b.a.Value + y; b.a = a3; y += b.a.Value + y; } QueryPerformanceCounter((LARGE_INTEGER*)(&End)); Time2 = End - Start; //-------------------------------------------------- int z = 0; QueryPerformanceCounter((LARGE_INTEGER*)(&Start)); for (j = 0; j < MILL; j++) for (i = 0; i < 1000000; i++) { z += a1.Value + z; z += a2.Value + z; z += a3.Value + z; } QueryPerformanceCounter((LARGE_INTEGER*)(&End)); Time3 = End - Start; //-------------------------------------------------- printf(" Pointer *.: %f\n Pointer ->: %f\n Reference: %f\n Direct: %f\n\n", (double)(Time0) / (Frequ), (double)(Time1) / (Frequ), (double)(Time2) / (Frequ), (double)(Time3) / (Frequ)); printf("%d %d %d %d",x,y,z,w); return 0; }
bei mir sind alle werte gleich im releasemode, mußte den code aber ein wenig modifizieren damit er für alle schleifen gleiche bedingungen bietet und auch im release läuft.
rapso->greets();