Optimierungsfrage



  • Moin moin,

    ich entwickele zur Zeit ein kleines Plugin in C#. Hierfür muss ich Punktreihen visualisieren. Da das Graphics Objekt keine Punkte darstellt, habe ich mir mit folgender Funktion geholfen:

    private void DrawPoint(Graphics g, Point p)
    {
      Point P1 = new Point(P.x + Offset, P.y + Offset);
      Point P2 = new Point(P.x - Offset, P.y + Offset);
      Point P3 = new Point(P.x + Offset, P.y - Offset);
      Point P4 = new Point(P.x - Offset, P.y - Offset);   
    
      g.DrawLine(this.MyPen, P1, P4);
      g.DrawLine(this.MyPen, P2, P3);
    }
    

    Naja, und da ich aus der C++/C Ecke komme, bekomme ich bei solch einem Code Bauchschmerzen. Pro gezeichnetem Punkt 4 new Aufrufe. Bei einem Plot von 10000 Punkten sind das also 40000 Allokationen. Da brauch man nur mal Zoomen und meine Zeichenfunktion wird wie blöd allokieren.

    Kennt da jemand bessere Wege?

    Anmerkung:
    Auf andere Visualisierungselemente kann ich hier nicht wechseln, da das Ganze ja ein Plugin ist und die Schnittstelle mir nur das Graphics Objekt anbietet.



  • Hast du denn schon mal die tatsächliche Performance des Codes im Release-Modus analysiert, ob der Code wirklich problematisch ist?
    Falls ja, kannst du dir immer noch zwei Point-Instanzen herrichten und diese immer wiederverwenden, dann sparst du dir die vielen new-Aufrufe.



  • Da die von dir benutze DrawLine-Methode intern auch nur die mit 4 Koordinaten-Parametern nutzt (laut Reflector):

    public void DrawLine(Pen pen, Point pt1, Point pt2)
    {
        this.DrawLine(pen, pt1.X, pt1.Y, pt2.X, pt2.Y);
    }
    

    kannst du gleich diese Überladung nutzen - ohne Point-Objekte zu allokieren:

    g.DrawLine(this.MyPen, P.x - Offset, P.y - Offset, P.x + Offset, P.y + Offset);
    g.DrawLine(this.MyPen, P.x - Offset, P.y + Offset, P.x + Offset, P.y - Offset);
    

    (wobei man ja hier keine Allokation auf dem Heap hat - so wie new in C++ - sondern nur auf dem Stack, da Point ein Wertetyp (Struktur) ist).

    PS: Dieser Code funktioniert in C#:

    int x = 42;
    int y = new int();
    y = 43;
    
    Console.WriteLine(x);
    Console.WriteLine(y);
    

    s.a. Ideone - Code 😉



  • Danke für die Tips. 🙂

    Hast du denn schon mal die tatsächliche Performance des Codes im Release-Modus analysiert, ob der Code wirklich problematisch ist?

    Nein

    Aber es ist aus meiner Sicht einfach ein besserer Programmierstil wenn man unnötige Objektkopien/Allokationen vermeidet.



  • Im ersten Schritt würde ich aber mehr auf Lesbarkeit als auf unnötige Allokationen Wert legen. In .NET hast du ganz andere Flaschenhälse. Da fallen jetzt 20000 Punkte nicht ins Gewicht.

    Ob das Zeichnen einer Linie jetzt besser lesbarer ist, wenn man 5 Parameter übergibt statt 3 halte ich für fraglich.

    Alles in allem behaupte ich aber dass in der heutigen Zeit 40000 Integer kein Problem darstellen sollten und auch die Allokation nicht ins Gewicht fällt.

    EDIT:
    Sieh dir mal Graphics.FillEllipse() und Graphics.FillRectangle(), eventuell helfen dir die beiden Methoden weiter um Zeichenoperationen zu sparen.



  • Bitte ein Bit schrieb:

    Hast du denn schon mal die tatsächliche Performance des Codes im Release-Modus analysiert, ob der Code wirklich problematisch ist?

    Nein

    Aber es ist aus meiner Sicht einfach ein besserer Programmierstil wenn man unnötige Objektkopien/Allokationen vermeidet.

    Prinzipiell okay, aber solange du keinen Profiler drüberjagst, weißt du ja nie, ob die Allokationen unnötig sind. Mach das mal, geht ja einfach und dauert nicht lang
    🙂



  • Th69 schrieb:

    (wobei man ja hier keine Allokation auf dem Heap hat - so wie new in C++ - sondern nur auf dem Stack, da Point ein Wertetyp (Struktur) ist).

    Ich bin nicht sicher, ob das dem OP ganz klar geworden ist. In C# bedeutet new nicht notwendig eine Heap-Allokation. Würde man die Funktion in ungefähr äquivalentes C++ übersetzen, sähe sie so aus:

    struct Point
    {
        int x, y;
    };
    ...
    void DrawPoint(Graphics* g, Point p)
    {
        Point P1 { P.x + Offset, P.y + Offset };
        Point P2 { P.x - Offset, P.y + Offset };
        Point P3 { P.x + Offset, P.y - Offset };
        Point P4 { P.x - Offset, P.y - Offset };
    
        g->DrawLine(this->MyPen, P1, P4);
        g->DrawLine(this->MyPen, P2, P3);
    }
    

    Ich hoffe ja, von sowas bekommt niemand Bauchschmerzen.

    Edit: falsche Anrede



  • In C# bedeutet new nicht notwendig eine Heap-Allokation.

    Nein, das war mir nicht klar. Interresante Info. 🙂

    ---

    Prinzipiell okay, aber solange du keinen Profiler drüberjagst, weißt du ja nie, ob die Allokationen unnötig sind. Mach das mal, geht ja einfach und dauert nicht lang

    Werde ich machen. 🙂


Log in to reply