GDI-Funktionen zu langsam



  • Ich hab mal ein bisschen mit den GDI-Grafikfunktionen in C# rumgespielt, denn die sind ja schon weitaus angenehmer zu benutzen als die in C++. Allerdings musste ich mit Erschrecken feststellen, dass sie bei größeren Operationen doch SEHR langsam sind. Ich habe dazu mal zwei Programme in C# und C++ angefertigt, die dasselbe machen. Und zwar werden jeweils zwei gleich große Bitmaps erstellt und die eine Bitmap wird mehrere Male auf die andere Bitmap geblittet:

    using System;
    using System.Drawing;
    using System.Windows.Forms;
    
    class Test
    {
    	static void Main()
    	{
    		const int size = 500;
    
    		Bitmap background = new Bitmap(size, size);
    		Bitmap foreground = new Bitmap(size, size);
    
    		Graphics backgroundGraphics = Graphics.FromImage(background);
    
    		DateTime start = DateTime.Now;
    
    		for (int i = 1; i <= 200; i++)
    			backgroundGraphics.DrawImage(foreground, 0, 0);
    
    		MessageBox.Show((DateTime.Now - start).Ticks.ToString());
    	}
    }
    
    #include <ctime>
    #include <sstream>
    #include <windows.h>
    
    using namespace std;
    
    int WinMain(HINSTANCE, HINSTANCE, LPSTR, int)
    {
    		const int size = 500;
    
    		HDC displayDC = GetDC(NULL);
    
    		HBITMAP background = CreateCompatibleBitmap(displayDC, size, size);
    		HBITMAP foreground = CreateCompatibleBitmap(displayDC, size, size);
    
    		HDC backgroundDC = CreateCompatibleDC(displayDC);
    		HDC foregroundDC = CreateCompatibleDC(displayDC);
    
    		SelectObject(backgroundDC, background);
    		SelectObject(foregroundDC, foreground);
    
    		clock_t start = clock();
    
    		for (int i = 1; i <= 200; i++)
    			BitBlt(backgroundDC, 0, 0, size, size, foregroundDC, 0, 0, SRCCOPY);
    
    		stringstream stream;
    		stream << clock() - start;
    
    		MessageBoxA(NULL, stream.str().c_str(), "", MB_OK);
    }
    

    Das C#-Programm braucht rund 73000000 Ticks, also circa sieben Sekunden. Das C++-Programm schafft es in rund 900 Ticks, also in weniger als einer Sekunde. (Das gleiche gilt auch, wenn ich bei BitBlt statt backgroundDC displayDC schreibe und das Blitten somit sichtbar auf dem Bildschirm vornehme.)

    Kann man es irgendwie hinbekommen, dass das C#-Gedöns zumindest annähernd so schnell ist wie das in C++? Ich meine, Zwischencode schön und gut, aber daran wird's ja wohl nicht liegen. Andere Funktionen in C# sind ja auch nicht 81000 mal langsamer als ihre C++-Pendants.



  • Ich kenne mich damit nicht wirklich aus, da mich grafische Sachen wenig beschäftigen, aber schon auf Anhieb frage ich mich, ob Du wirklich DASSELBE machst?

    Einmal blittest Du mit SRCCOPY, d.h. Du kopierst einfach Daten 1:1 von einer Quelle in einen Bildspeicher, ohne Konversion. Im anderen Fall zeichnest Du ein Image, das enthält doch noch die Themen Farb- und Auflösungsanpassung, Alphas, etc, die macht er da auch noch.



  • Tja, ich weiß nicht, ob es intern nicht doch was anderes ist. Aber die einzige Möglichkeit, die ich kenne, mit der GDI+ in C# ein Bild auf ein anderes zu zeichnen, ist Graphics.DrawImage. Wenn es eine schnellere Variante gibt (die vielleicht nur 1:1 kopiert oder was auch immer) wäre ich dankbar, sie zu kennen.



  • schau dir mal die Lockbits an, dort kannst du nach einem Marshal.Copy auf byte ebene schreiben, ist um einiges schneller als mit den o.g. funktionen...



  • Auf Byte-Ebene schreiben? Ist das wirklich das richtige für mein Vorhaben? Zumal ich ja in der Praxis auch noch dieses MakeTransparent benutzte, der Vordergrund also nicht rechteckig kopiert werden soll.
    Es muss doch irgendeine Möglichkeit geben, dass diese Bildkopierfunktion schneller läuft. Und zwar ohne da manuell an den binären Rohdaten der Bitmaps rumzuspielen. In C++ ging es doch auch mit einem simplen Funktionsaufruf. Da musste man auch nicht erst ein Byte-Array der Bitmap erstellen (was ja prinzipiell durchaus möglich war), damit das Kopieren flott ging.



  • Kann man es irgendwie hinbekommen, dass das C#-Gedöns zumindest annähernd so schnell ist wie das in C++? Ich meine, Zwischencode schön und gut, aber daran wird's ja wohl nicht liegen. Andere Funktionen in C# sind ja auch nicht 81000 mal langsamer als ihre C++-Pendants.

    Es liegt auch nicht daran.
    Es liegt vielmehr daran, dass das .NET Framework GDI+ verwendet, und du in deinem WinAPI Code GDI-ohne-Plus verwendest. Was zwei komplett unterschiedliche APIs sind.
    Und GDI+ ist nunmal schrecklich lahm.

    Wenn du es schneller haben willst, könntest du über PInvoke direkt die GDI Funktionen aus C# aufrufen. Oder irgendeine andere Grafik-API. Idealerweise eine für die es gute fertige Wrapper für .NET gibt.



  • hustbaer schrieb:

    Und GDI+ ist nunmal schrecklich lahm.

    Warum ist das eigentlich so? Ich meine, sie haben ja gezeigt, dass es schneller geht. Warum ist nun die neue API viel langsamer als die alte?

    hustbaer schrieb:

    Wenn du es schneller haben willst, könntest du über PInvoke direkt die GDI Funktionen aus C# aufrufen.

    Würde das dann auch noch funktionieren, wenn ich das Programm auf Linux unter Mono ausführe?

    hustbaer schrieb:

    Oder irgendeine andere Grafik-API. Idealerweise eine für die es gute fertige Wrapper für .NET gibt.

    Ich wollte jetzt mal erst nur mit dem Standardzeug, das beim normalen .NET Framework 2.0 gleich dabei ist, arbeiten und nicht irgendwelche externen Sachen einbauen. Gibt's da echt nichts Schnelles, was schon dabei ist? Ziemlich lausig von Microsoft. Ansonsten ist das ganze .NET-Zeug nämlich recht gelungen.



  • Carloss schrieb:

    hustbaer schrieb:

    Und GDI+ ist nunmal schrecklich lahm.

    Warum ist das eigentlich so? Ich meine, sie haben ja gezeigt, dass es schneller geht. Warum ist nun die neue API viel langsamer als die alte?

    GDI+ ist ja nicht als Ersatz für GDI gedacht. GDI+ ist einfach anders. Und Geschwindigkeit ist nicht immer alles. Für gewisse Dinge ist GDI+ durchaus gut geeignet.

    Soweit ich das einschätzen kann, verwendet GDI+ für alles die CPU, auch wenn die Grafikkarte es schneller könnte. Vermutlich deswegen, weil es die API einfacher macht. Weniger "unnötige" Umwege/Unterscheidungen (DIB vs. DDB, Memory Bitmap/Compatible Bitmap/Blah Bitmap ...). Die Implementierung wird natürlich dadurch auch einfacher, und nochdazu kann man garantieren, dass immer 100% dasselbe rauskommt, egal mit welcher Grafikkarte/Treiber/... man es laufen lässt. Gerade bei Dingen wie Antialiasing oder Alphablending könnten unterschiedliche Grafikkarten durchaus leicht unterschiedliche Ergebnisse liefern.

    APIs die schnell sind, dafür aber kompliziert und nicht 100% exakt gibt es schliesslich schon genügend (GDI, OpenGL, DirectX) -- wieso dann also noch eine dazumachen?

    hustbaer schrieb:

    Wenn du es schneller haben willst, könntest du über PInvoke direkt die GDI Funktionen aus C# aufrufen.

    Würde das dann auch noch funktionieren, wenn ich das Programm auf Linux unter Mono ausführe?

    Ziemlich sicher nicht. Nur wenn du wirklich Wert auf Plattformunabhängigkeit legst, dann nimm bitte Java.

    hustbaer schrieb:

    Oder irgendeine andere Grafik-API. Idealerweise eine für die es gute fertige Wrapper für .NET gibt.

    Ich wollte jetzt mal erst nur mit dem Standardzeug, das beim normalen .NET Framework 2.0 gleich dabei ist, arbeiten und nicht irgendwelche externen Sachen einbauen. Gibt's da echt nichts Schnelles, was schon dabei ist? Ziemlich lausig von Microsoft. Ansonsten ist das ganze .NET-Zeug nämlich recht gelungen.

    Er. Grosse Teile des .NET Frameworks haben nen Overhead dass einem schwindelig wird. Vor allem die ganzen Forms Sachen. Merkt man bloss nicht so schlimm, weil heutige PCs so verflucht schnell sind.



  • hustbaer schrieb:

    GDI+ ist ja nicht als Ersatz für GDI gedacht.

    Nicht für C++, aber in C# benutzt man wohl an der Stelle GDI+, wo man in C++ GDI verwendet hätte. Aber da es zu langsam ist, hat man eben keine wirkliche Alternative, es sei denn, man importiert die WinAPI-Funktionen der nativen GDI.

    hustbaer schrieb:

    APIs die schnell sind, dafür aber kompliziert und nicht 100% exakt gibt es schliesslich schon genügend (GDI, OpenGL, DirectX) -- wieso dann also noch eine dazumachen?

    Weil das alles nichts ist, was zum .NET Framework gehört, obwohl dieses Framework ansonsten eigentlich ziemlich umfassend ist.


Anmelden zum Antworten