VCL vs. WINAPI-GDI Performance



  • Hallo alle zusammen

    ich habe mich entschlossen, für mein Frontend zur Visualisierung anstatt der VCL die WINAPI-GDI zu nutzen, da ich dadurch die Transparenzfunktionalität und hoffentlich auch etwas Performance gewinne.
    D.h. alle grafischen Aktionen sollen in einem Buffer ( einem HDC ) stattfinden und dann nur noch bei der Darstellungsoperation mittels Blit auf eine VCL-Komponente ( Paintbox ) übertragen werden.

    Dazu habe ich zunächst Performancemessungen gemacht, um zu schauen ob ich die GDI performant hinbekomme.

    Meine GDI-Zeichenfunktion sieht folgendermaßen aus:

    long TestGDI()
    {
        HDC hdcWin = GetWindowDC( GetDesktopWindow() );
        HDC hMemDC = CreateCompatibleDC( hdcWin );
        HBITMAP hBM = CreateCompatibleBitmap( hdcWin, 200, 200 );
        SelectObject( hMemDC, hBM );
    
        long l1 = GetTickCount();
        for ( unsigned i = 0; i < 100000; ++i )
        {
            HBRUSH br = CreateSolidBrush( RGB( 255, 255, 255 ) );
    
            RECT r;
            r.left = 0;
            r.top = 0;
            r.right = 200;
            r.bottom = 200;
            ::FillRect( hMemDC, &r, br );
    
            DeleteObject( br );
        }
        long l2 = GetTickCount();
    
        DeleteDC( hMemDC );
        DeleteObject( hBM );
        ReleaseDC(GetDesktopWindow(), hdcWin );
        return l2 - l1;
    }
    

    Da es mein erster Kontakt zur GDI ist, bitte ich darum, nicht mit Kritik zu sparen 😉

    Im Vergleich dazu der VCL-Zeichenprozess

    long TestVCL()
    {
        long l1 = GetTickCount();
        for ( unsigned i = 0; i < 100000; ++i )
        {
            Form1->PaintBox1->Canvas->Brush->Color = RGB( 255, 255, 255 );
            Form1->PaintBox1->Canvas->FillRect( Rect( 0, 0, 200, 200 ) );
        }
        long l2 = GetTickCount();
        return l2 - l1;
    }
    

    Der Aufruf:

    {
        long l = TestGDI();
        Memo1->Lines->Add( "WINAPI GDI: " + IntToStr( l ) );
    }
    
    {
        long l = TestVCL();
        Memo1->Lines->Add( "VCL: " + IntToStr( l ) );
    }
    

    Messergebnisse im ReleaseMode ( CBuilder 6.0 EE, Win XP ):

    WINAPI GDI: 2953
    VCL: 2781

    Ich habe die Messung mehrfach durchgeführt und durchaus unterschiedliche Messergebnisse erhalten, aber in jedem Fall war die VCL-Variante schneller als die GDI-Variante.
    Mir ist auch klar, dass ich beim GDI nur in einen Speicherbereich zeichne und dass beide Varianten nicht 100% vergleichbar sind, aber dennoch hätte ich hier eher Vorteile beim GDI gesehen...

    Anmerkung: Die Brushes setze ich deswegen so oft, weil das auch in meinem realen Anwendungsfall so wäre.

    Wo liegt mein Denkfehler? Was ist am GDI-Code falsch, oder unschön gemacht?

    Hat jemand eine Idee?



  • ich hab nochmal das eine oder andere Codebeispiel gefunden und nirgendwo werden die Zeichenprozesse großartig anders umgesetzt...

    Brush / Pen setzen -> zeichnen -> fertig ...



  • Ich vermute, dass die VCL das Bruch Objekt cached. Du erstellst jedesmal die gleiche Brush.



  • Ich hab jetzt mal den Brush immer mit zufälligen Farbwerten initialisiert.
    Jetzt sieht es besser aus. GDI liegt jetzt im durchschnitt knapp 5% vor der VCL. Ich denke das ist deutlich realistischer.

    Dann wirds wohl tatsächlich an dem Brush gelegen. Ich hatte wirklich nicht damit gerechnet, dass der gechached wird.

    Danke dir 🙂



  • Dein Vergleich ist übrigens nicht ganz fair. In der GDI Variante erstellst du ein offscreen Bitmap, zeichnest da rein und dann auf den Bildschirm. Das kannst du mit VCL genauso machen. Wird evtl. eh schon gemacht wegen double buffering, aber du könntest es direkt machen.
    Du kannst VCL und GDI auch ganz gut mischen. Spricht nichts dagegen, da wo es einfacher ist, VCL zu verwenden, und nicht gewrappte Sachen direkt mit GDI zu machen.



  • Du solltest das CreateSolidBrush außerhalb der Schleife machen! Im VCL merkt er sich halt, dass sich die Farbe nicht geändert hat und erzeugt den Brush nicht nochmals.... das ist das Haupt-Performance-Problem hier...



  • Jochen das hatte ich bewusst so drin, weil das in meinem Anwendungsfall das auch der Fall ist. Das die VCL das cached, hab ich in dem Moment nicht vermutet.

    Habe die VCl dann auch mal in ein BitMap zeichnen lassen, um das noch etwas fairer zu gestalten. Ergebnisse sind aber ähnlich. Leichte Vorteile für GDI, wie man es auch erwarten würde.

    Meinen GDI-Code hat niemand bemängelt, also gehe ich auch erstmal davon aus, dass das so einigermaßen in Ordnung ist.


Anmelden zum Antworten