OOP oder Prozedural?


  • Mod

    bevor man einfach kristalkugel perforance analysen macht, sollte an erstmal die verlaesslichen mittel wie z.b. profiler nutzen, denn am ende ist es eher schlechtes programmieren als c++-overhead.

    jedenfalls meine erfahrung mit leuten die solche vermutungen dahinwerfen.



  • Hi,
    wie gesagt, ich denke auch nicht, dass es an den Klassen selbst liegt, sondern eher daran, was ich noch zusätzlich gecodet habe. Vielleicht wird es an einem Beispiel deutlicher (an dem ich auch es auch getestet habe):
    In meinem Buch wird ein einfacher Pixelplot (1000 Pixel pro Frame) ungefähr so durchgeführt:
    Globale Variablen, Defines und in der while Schleife meines Programmes wird einmal gelockt, dann direkt auf die Surfacedaten zugegriffen und die Farbe zufällig gesetzt, dann wieder unlocked.
    Mit meinem Klassendesign läuft es ungefähr so:
    Beim Start des Programms Objekte erstellt, die die DirectX Objekte kapseln, dabei verschiedene Parameter gesetzt wie z.B. Bittiefe, in der while Schleife wird dann die Lockmethode meiner Klasse aufgerufen, die Methode zum Plotten und dann die Unlockmethode.
    Im Endeffekt also das gleiche. Aber in meinen Methoden ist nun Overhead drin (Überprüfungen auf Bittiefe, auch virtuelle Methoden werden verwendet, etc.). Und genau diese Codestücke machen das Programm langsamer. Allerdings macht das ganze ohne sie keinen Sinn.
    Es ist schwer das ganze zu beschreiben.
    Aber vielleicht könnt ihr was zum Thema Profiler posten (gibts sowas kostenlos, was ist damit möglich etc.) oder ihr kennt Tutorials, in denen DirectX mit Klassen durchgearbeitet wird (es sollte aber zunächst DirectDraw verwendet werden, da sich mein Buch auch zunächst darauf bezieht).
    Also zusammengefasst: meine Klassen machen nichts anderes als der Code im Buch, nur mit mehr Überprüfungen etc., da ich das ganze unabhängig von globalen Variablen und Defines machen will...



  • Ein Hinweis, was fuer einen Compiler du einsetzt, waer hilfreich um zu wissen was fuer ein Profiler du brauchst. Was ein Profiler macht, wird hier erklaert: http://de.wikipedia.org/wiki/Profiler_(Programmierung) (Grob gesagt: ein Profiler sagt dir, WO in deinem Programm die meiste Zeit verbraucht wird).

    Wie Don06 schon gesagt hat, solltest du erstmal erklaeren, was du mit "langsamer laufen" meinst. Wenn du damit 1150 fps statt 1200 fps meinst: komplett vernachlaessigbar. Also: was meinst du mit "langsamer"?

    Uhne da meine Glaskugel in letzter Zeit relativ trueb ist, kann ich dir leider nicht sagen, warum dein Programm so viel langsamer ist, ohne den Quellcode zu sehen (aber bitte poste nur die relevanten Teile, niemand wird sich hier durch Zig Zeilen Quellcode wuehlen). Nur eine Bemerkung nebenbei: Warum lockst du die Zeichenflaeche in der While-Schleife und nicht ausserhalb?



  • Mhh ok.
    Also als Compiler nutze ich Visual C++ 2005 Express Edition.
    Langsamer laufen ist bis jetzt nur ein Gefühl. Ich muss erst noch einen "Framemesser" einbauen, dann kann ich genauer sagen, wieviel langsamer es läuft.
    Und zum Quelltext: kann ich gerne posten aber ich weiß im Moment noch nicht welche Stellen wirklich relevant sind und das ganze langsamer machen. Bzw. wenn ich was poste, könnte es schon länger werden...
    Und zum Locken: Ich locke ja nur einmal, wenn ich die Pixel plotten will. Sprich lock, dann die Pixelplots, dann wieder unlock. Das sollte schon so passen.
    Nun gut ich gucke mal was ich an relevantem Code posten könnte...



  • Cpp_Junky schrieb:

    Welches Spiel soll denn heute CPU limitiert sein? 😕 Speicher ja, aber CPU? Naja ich weiss nich...

    Nur noch die besten. Zum Beispiel wo ich mitarbeite. f'`8k

    Gruß, TGGC (making great games since 1992)



  • Hi,
    also hab mich heute mal dranbegeben und einen Framezähler in mein Programm mit Klassen und in das Originalprogramm eingebaut. Beide machen im Endeffekt das selbe, das Original ist nur viel kürzer und arbeitet ohne Klassen. Es greift im Endeffekt in der while Schleife in WinMain direkt auf die Surfacedaten zu und zwar über globale Variablen. Ich hab das ganze halt mit Klassen gemacht und viele Überprüfungen und Overhead dazugepackt, der die Verwendung der Klassen aber erheblich leichter macht.
    Naja jetzt die Ergebnisse:
    Originalprogramm: um die 6000 fps.
    Mein Programm: um die 130 fps.
    Macht es bei einem solchen Unterschied überhaupt Sinn, ein Klassenkonstrukt zu basteln um DirectX leichter und einfacher zu bedienen. Und wie macht das z.B. die Irrlichtengine?
    Vielleicht habt ihr ja ein Tutorial oder so wo DirectX mit Klassen programmiert wird.
    Ansonsten kann ich auch gerne Code posten damit ihr seht was ich an Overhead drin hab.

    EDIT:
    Ein Nachtrag: Hab jetzt mal den Code aus dem Originalprogramm genommen und in meine while Schleife gesetzt. Habe nur zwei Zugriffe in der while Schleife über die Klassen und zwar um den Surfacepointer zu bekommen und ein Zugriff auf die Klasse des Framezählers.
    Ergebnis: 3000 fps.
    Kann es wirklich sein, dass zwei Zugriffe auf Methoden die Framezahl halbieren?



  • Hm,
    ich schreib mal was ich gebastelt hab:
    -eigene Exceptionklasse
    -Mainklasse, kapselt DIRECTDRAW7 Objekt, erstellt es im Konstruktor und bietet Zugriff darauf etc.
    -Palettenklasse, kapselt DIRECTDRAWPALETTE Objekt, PALETTEENTRIES werden in vector gespeichert und dann eingesetzt, Zugriffe, Veränderungen der Palette etc. über Methoden
    -Surfaceklasse, Basisklasse für alle Surfaces, es können keine Objekte erstellt werden, da ich den Konstruktor protected gesetzt habe, kapselt DIRECTDRAWSURFACE7 Objekt sowie DDSURFACEDESC2, den Memorypitch und die Surfacedaten nach einem Lock, Lock, Unlock sowie Pixelplot über Methoden, Methoden alle als virtual deklariert
    -PrimarySurfaceklasse, abgeleitet von Surfaceklasse, übernimmt alle Methoden, aber bietet die zusätzliche Möglichkeit, eine Palette einzusetzen.
    -Colorklasse, speichert 8,16,24 oder 32 Bit Farbcode als ganzes sowie als R,G,B getrennt

    noch folgen soll eigentlich: Doublebuffering, Colorkeying, Clipping, Offscreensurfaceklasse für Sprites, Bitmaps etc. und dazu natürlich dann Spriteklasse, Bitmapklasse etc.

    So hatte ich mir den Aufbau überlegt. Für ein Spiel wollte ich dann eine Klassenstruktur oben drauf setzen. Playerklasse, Spielfeldklasse etc. je nachdem was ich für ein Spiel machen will.

    Aber wenn das ganze jetzt so viel Ressourcen verbraucht (FPS siehe voriger Post), dann frage ich mich ob das überhaupt so viel Sinn macht (bzw. ob man das überhaupt so machen sollte mit den Klassen um die DirectX Objekte). Oder ob man direkt Playerklassen und so schreibt und in denen dann die Zugriffe auf die DirectXkomponenten koordiniert.

    Hoffe ihr versteht jetzt mein Problem. Falls ihr noch Code sehen wollt, sagt Bescheid, dann poste ich was (ist aber dann etwas länger)...



  • Wann testest Du denn auf Bittiefe? In jedem Durchlauf oder nur einmal? Wenn Du natürlich in jedem Durchlauf solche Dinge abfrägst, wird das schnell langsamer. Es ist eigentlich nie eine gute Idee wenn Spielobjekte (wie z.B. der Spieler) sich selbst um Hardware-Dinge kümmern. Diese sollten sich eigentlich nur um ihre Aufgaben innerhalb der Spielwelt kümmern müssen.



  • Amateur schrieb:

    Aber wenn das ganze jetzt so viel Ressourcen verbraucht (FPS siehe voriger Post), dann frage ich mich ob das überhaupt so viel Sinn macht (bzw. ob man das überhaupt so machen sollte mit den Klassen um die DirectX Objekte). Oder ob man direkt Playerklassen und so schreibt und in denen dann die Zugriffe auf die DirectXkomponenten koordiniert.

    Hoffe ihr versteht jetzt mein Problem. Falls ihr noch Code sehen wollt, sagt Bescheid, dann poste ich was (ist aber dann etwas länger)...

    Na ja, der Grossteil der Spiele heutzutage ist in OOP geschrieben. Das solltest auch du tun, es ist ziemlich nicht der falsche Weg. Bei den krassen FPS-Unterschieden machst du garantiert irgendwo etwas falsch. Wie gesagt: Besorg dir einen Profiler und schau nach WO der Fehler liegt!
    (Ich benutze VS2005 nicht und kann dir deshalb keinen empfehlen. Such mal hier im Forum und sonst mit Google, und wenn du nichts findest, frag nochmal im Forum nach)



  • Hi,
    konnte nicht früher antworten.
    Also nach einem Profiler werd ich mich mal umschauen.
    Ansonsten hab ich mir gedacht poste ich mal meine selbstgeschriebene Lock und Unlockfunktion. Ich schätze dass dort ein Großteil an Frames verloren geht:

    void CSurface::Lock(const LPRECT lpflaeche, int flags, HANDLE hEvent)
    {
    	HRESULT result = DD_OK;
    
    	ZeroMemory(&ddsd, sizeof(ddsd));
    	ddsd.dwSize = sizeof(ddsd);
    	if ((result = lpsurface->Lock(lpflaeche,&ddsd,flags,hEvent)) == DDERR_SURFACELOST)
    	{
    		if (FAILED(lpsurface->Restore()))
    			throw CException("Fehler bei Restore nach Lock.");
    		else 
    			result = lpsurface->Lock(lpflaeche,&ddsd,flags,hEvent);
    	}
    
    	if (result!=DD_OK)
    	{
    		if (result==DDERR_SURFACELOST)
    			throw CException("Surface lost trotz Restore. Kein Lock möglich.");
    		else
    			throw CException("Unbekannter Fehler bei PS->Lock.");
    	}
    	if (lpflaeche)
    		lockedRect = *lpflaeche;
    	locked++;
    	mempitch = ddsd.lPitch;
    	surfaceData = static_cast<UCHAR*>(ddsd.lpSurface);
    }
    
    void CSurface::Unlock()
    {
    	// wenn nicht gelockt, zurück
    	if (!locked) return;
    
    	HRESULT result;
    
    	if ((result = lpsurface->Unlock(&lockedRect)) == DDERR_SURFACELOST)
    	{
    		if (FAILED(lpsurface->Restore()))
    			throw CException("Fehler bei Restore nach Unlock");
    		else 
    			result = lpsurface->Unlock(&lockedRect);
    	}
    
    	if (result!=DD_OK)
    	{
    		if (result==DDERR_SURFACELOST)
    			throw CException("Surface lost trotz Restore. Kein Unlock möglich.");
    		else
    			throw CException("Unbekannter Fehler bei PS->Unlock.");
    	}
    
    	if(!(--locked))
    	{
    		ZeroMemory(&lockedRect,sizeof(lockedRect));
    		mempitch = 0;
    		surfaceData = 0;
    	}
    }
    

    Leider sehr umfangreich. Was mir jetzt spontan einfallen würde um das ganze zu verkürzen, wäre immer ein Restore auszuführen und dann erst zu locken. Nur diesen Lock überprüfen und gegebenenfalls eine Exception auslösen. Aber vllt. könnt ihr ja kurz über den Code schauen und einige Verbesserungsvorschläge geben...
    Ich danke euch für die Mühe!



  • Amateur schrieb:

    [...]
    Ein Nachtrag: Hab jetzt mal den Code aus dem Originalprogramm genommen und in meine while Schleife gesetzt. Habe nur zwei Zugriffe in der while Schleife über die Klassen und zwar um den Surfacepointer zu bekommen und ein Zugriff auf die Klasse des Framezählers.
    Ergebnis: 3000 fps.
    Kann es wirklich sein, dass zwei Zugriffe auf Methoden die Framezahl halbieren?

    Ja, aber bei solchen hohen FPS-Raten macht eine Halbierung so gut wie keinen Unterschied.

    Eine Halbierung von 6000FPS sind gerade mal 0,166666666666666666666666666666667 millisekunden mehr.

    6000 zu 130 sind natürlich schon ein bischen mehr. Nämlich 7,52564102564102564102564102564103 millisekunden. Da sollte man sich schon eher Gedanken machen.

    Siehe dazu auch hier.



  • Hi,
    erstmal: interessante Website über FPS etc. Danke!
    Zum anderen hab ich jetzt (ohne Profiler) herausgefunden, wo ein großteil der Frames verloren geht, auch wenn ich nicht nachvollziehen kann wieso.
    Hier mal der Code des Pixelplots:

    void CSurface::PlotPixel(int x, int y, const CColor& color)
    {
    	// wenn color das gleiche format hat wie surface
    	if (bits == color.GetBits())
    	{
    		if (!locked) //<--
    			Lock();  //<--
    		surfaceData[x+y*mempitch] = color.GetColor();
    		if (locked) //<--
    			Unlock();  //<--
    	}
    }
    

    Wenn ich die markierten Zeilen auskommentiere steigen die FPS auf um die 2000. Jetzt frage ich mich, warum die Zeilen das ganze so ausbremsen. Da ich locked beim Aufruf von Lock() (schon vor dem Pixelplot) inkrementiere dürfte die if Abfrage false liefern und Lock kein zweites Mal aufgerufen werden. Das gleiche gilt für Unlock(). Also liegt der Programmierfehler wohl eher in der Lock und Unlockfunktion (Code in vorigem Post). Ich kann den Inhalt der Variable leider nicht mit dem Debugger testen, da ich sonst einen Breakpoint zwischen Lock und Unlock legen muss und das soll man laut Internet nicht machen.
    Vllt. könnt ihr mir weiterhelfen, ansonsten teste ich mal weiter und vllt. finde ich den Fehler im Code der das ganze so viel langsamer macht (zwischen 2000 FPS mit Klassen und 6000 FPS Originalcode dürfte ja dann nicht mehr so viel Unterschied liegen)...

    EDIT: Ok hab den Fehler gefunden... Eigentlich total offensichtlich... Durch den Aufruf von Unlock() nach jedem Plot erzwinge ich das erneute Locken und Unlocken bei jedem Plot. Ok ich schreib das ganze mal um und dann mal schauen wieviele Frames ich bekomme...
    EDIT2: Ok läuft bei 2000 FPS... Jetzt kommen wir der Sache schon näher...


Anmelden zum Antworten