Performanceanomalien bei TChart

Rund um die LCL und andere Komponenten
Antworten
Lomat
Beiträge: 37
Registriert: Fr 14. Jan 2022, 13:44

Performanceanomalien bei TChart

Beitrag von Lomat »

Hallo zusammen,

ich habe ein Performance-Problem mit Liniengrafiken in TChart. Es handelt sich um etwa 130 XY-Datensätze mit jeweils einigen tausend Datenpunkten in jeweils 3 Charts. Hinzu kommen zwei weitere Charts, die jeweils nur einen Datensatz enthalten. Es sollten eigentlich nur die Y-Werte ausgetauscht werden. Weist man diese über YValue[j] mit j = Integer zu dann wird die Sache zur Gedultsprobe bzw. das Programm schläft ein. Bereinigt man die Charts über TChart.Series.Clear, erzeugt alle LineSeries neu und füllt diese wieder mit Daten geht es so schnell, dass das Programm interaktiv bleibt bzw. der Vorgang ist fast instantan abgeschlossen.

Die Frage ist: Warum ist das so? Eigentlich ist das unlogisch.

Ich zeige hier mal den Code nebts auskommentierten Fehlschlägen:

Code: Alles auswählen


Procedure TMainForm.RefreshAllIntensitysInChartsExceptMeasuredDark;

Var TempRamanLineSeries : TLineSeries;
    i, j                : Integer;
    TempRamanChartLineSeries : TLineSeries;
    TempDarkChartLineSeries  : TLineSeries;
    TempRawChartLineSeries   : TLineSeries;
    TempLineSeries           : TLineSeries;
    rgSeriesColor            : Array Of TColor;
    rgstSeriesTitle          : Array Of String;

Begin
  //Schneller Code
  Setlength(rgSeriesColor,   RamanSpectraObjectList.Count);
  Setlength(rgstSeriesTitle, RamanSpectraObjectList.Count);
  For i := 0 To RamanSpectraObjectList.Count - 1 Do
      Begin
        rgSeriesColor[i]   := (RamanChart.Series.Items[i] As TLineSeries).SeriesColor;
        rgstSeriesTitle[i] := (RamanChart.Series.Items[i] As TLineSeries).Title;
      End;
  RamanChart.Series.Clear;
  DarkChart.Series.Clear;
  RawChart.Series.Clear;
  For i := 0 To RamanSpectraObjectList.Count - 1 Do
      Begin
        TempRamanChartLineSeries := TLineSeries.Create(RamanChart);
        TempDarkChartLineSeries  := TLineSeries.Create(DarkChart);
        TempRawChartLineSeries   := TLineSeries.Create(RawChart);

        TempRamanChartLineSeries.SeriesColor := rgSeriesColor[i];
        TempDarkChartLineSeries. SeriesColor := rgSeriesColor[i];
        TempRawChartLineSeries.  SeriesColor := rgSeriesColor[i];

        TempRamanChartLineSeries.Title := rgstSeriesTitle[i];
        TempDarkChartLineSeries. Title := rgstSeriesTitle[i];
        TempRawChartLineSeries.  Title := rgstSeriesTitle[i];

        With (RamanSpectraObjectList.Items[i] As TRamanSpectrum) Do
             For j := 0 To iPropLength - 1 Do
                 Begin
                   TempRamanChartLineSeries.AddXY(dbGetWaveNumber(j), dbGetIntensity(j));
                   TempDarkChartLineSeries. AddXY(dbGetWaveNumber(j), dbGetDarkIntensity(j));
                   TempRawChartLineSeries.  AddXY(dbGetWaveNumber(j), dbGetRawIntensity(j));
                 End;
        RamanChart.AddSeries(TempRamanChartLineSeries);
        DarkChart.AddSeries(TempDarkChartLineSeries);
        RawChart.AddSeries(TempRawChartLineSeries);
      End;

  //Blockert das Programm und ist viel zu langsam
  {For i := 0 To RamanSpectraObjectList.Count - 1 Do
      Begin
        (RamanChart.Series.Items[i] As TLineSeries).Clear;
        (DarkChart.Series.Items[i]  As TLineSeries).Clear;
        (RawChart.Series.Items[i]   As TLineSeries).Clear;
      End;
  For i := 0 To RamanSpectraObjectList.Count - 1 Do
      Begin
        TempRamanChartLineSeries := (RamanChart.Series.Items[i] As TLineSeries);
        TempDarkChartLineSeries  := (DarkChart.Series.Items[i]  As TLineSeries);
        TempRawChartLineSeries   := (RawChart.Series.Items[i]   As TLineSeries);
        With (RamanSpectraObjectList.Items[i] As TRamanSpectrum) Do
             For j := 0 To iPropLength - 1 Do
                 Begin
                   TempRamanChartLineSeries.AddXY(dbGetWaveNumber(j), dbGetIntensity(j));
                   TempDarkChartLineSeries. AddXY(dbGetWaveNumber(j), dbGetDarkIntensity(j));
                   TempRawChartLineSeries.  AddXY(dbGetWaveNumber(j), dbGetRawIntensity(j));
                 End;
      End;     }

  //Ist ebenfalls übermäßig langsam
  {For i := 0 To RamanSpectraObjectList.Count - 1 Do
      With (RamanSpectraObjectList.Items[i] As TRamanSpectrum) Do
           Begin
             For j := 0 To iPropLength - 1 Do
                 Begin
                   (RamanChart.Series.Items[i] As TLineSeries).AddXY(dbGetWaveNumber(j), dbGetIntensity(j));
                   (DarkChart.Series.Items[i]  As TLineSeries).AddXY(dbGetWaveNumber(j), dbGetDarkIntensity(j));
                   (RawChart.Series.Items[i]   As TLineSeries).AddXY(dbGetWaveNumber(j), dbGetRawIntensity(j));
                 End;
             For j := 0 To iPropLength - 1 Do
                 Begin
                   (RamanChart.Series.Items[i] As TLineSeries).YValue[j] := dbGetIntensity(j);
                   (DarkChart.Series.Items[i]  As TLineSeries).YValue[j] := dbGetDarkIntensity(j);
                   (RawChart.Series.Items[i]   As TLineSeries).YValue[j] := dbGetRawIntensity(j);
                 End
           end;      }

  //Austausch der Y-Werte in den Einzelspektren-Charts
  If Not(iActualSpectrum = -1) Then
     With (RamanSpectraObjectList.Items[iActualSpectrum] As TRamanSpectrum) Do
          For j := 0 To iPropLength - 1 Do
              Begin
               (ActualRamanChart.Series.Items[0] As TLineSeries).YValue[j] := dbGetIntensity(j);
               (ActualDarkChart.Series.Items[0]  As TLineSeries).YValue[j] := dbGetDarkIntensity(j);
              End
end; 



Lomat
Beiträge: 37
Registriert: Fr 14. Jan 2022, 13:44

Re: Performanceanomalien bei TChart

Beitrag von Lomat »

Hallo Nochmal,

ich habe wohl die goldene Regel verletzt, das Aktualisieren abzuschalten, wenn der Chart mit großen Datenmengen gefüllt werden soll. So geht es viel besser:

Code: Alles auswählen

  //Austausch der Y-Werte der Sammelcharts
  For i := 0 To RamanSpectraObjectList.Count - 1 Do
      With (RamanSpectraObjectList.Items[i] As TRamanSpectrum) Do
           Begin
             For j := 0 To iPropLength - 1 Do
                 Begin
                   (RamanChart.Series.Items[i] As TLineSeries).YValue[j] := dbGetIntensity(j);
                   (DarkChart.Series.Items[i]  As TLineSeries).YValue[j] := dbGetDarkIntensity(j);
                   (RawChart.Series.Items[i]   As TLineSeries).YValue[j] := dbGetRawIntensity(j);
                 End
           end;

  RamanChart.EnableRedrawing;
  DarkChart. EnableRedrawing;
  RawChart.  EnableRedrawing;

  ActualRamanChart.DisableRedrawing;
  ActualDarkChart. DisableRedrawing;

  //Austausch der Y-Werte in den Einzelspektren-Charts
  If Not(iActualSpectrum = -1) Then
     With (RamanSpectraObjectList.Items[iActualSpectrum] As TRamanSpectrum) Do
          For j := 0 To iPropLength - 1 Do
              Begin
               (ActualRamanChart.Series.Items[0] As TLineSeries).YValue[j] := dbGetIntensity(j);
               (ActualDarkChart.Series.Items[0]  As TLineSeries).YValue[j] := dbGetDarkIntensity(j);
              End;

  ActualRamanChart.EnableRedrawing;
  ActualDarkChart. EnableRedrawing;   

wp_xyz
Beiträge: 5146
Registriert: Fr 8. Apr 2011, 09:01

Re: Performanceanomalien bei TChart

Beitrag von wp_xyz »

Genau.

TSchnuckenbock
Beiträge: 118
Registriert: Do 20. Jul 2017, 23:47
OS, Lazarus, FPC: Win7 und Win10
CPU-Target: xxBit
Wohnort: Südheide (Schnuckenland)

Re: Performanceanomalien bei TChart

Beitrag von TSchnuckenbock »

Zwar ein bischen OT, aber...

Mir kam so spontan bei dem Code-Beispiel der Gedanke, daß da eventuell eine try-finally-Konstrukt sinnvoll sein könnte ala

Code: Alles auswählen

Chart.DisableRedrawing;
try
  ....
finally
  Chart.EnableRedrawing;
end
Was denken andere darüber?

wp_xyz
Beiträge: 5146
Registriert: Fr 8. Apr 2011, 09:01

Re: Performanceanomalien bei TChart

Beitrag von wp_xyz »

Richtig.

Benutzeravatar
af0815
Lazarusforum e. V.
Beiträge: 6780
Registriert: So 7. Jan 2007, 10:20
OS, Lazarus, FPC: FPC fixes Lazarus fixes per fpcupdeluxe (win,linux,raspi)
CPU-Target: 32Bit (64Bit)
Wohnort: Burgenland
Kontaktdaten:

Re: Performanceanomalien bei TChart

Beitrag von af0815 »

Das Verhalten und die Lösung ist nicht auf TAChart beschränkt, sonder betrifft fast alle visuellen Komponenten, besonders bei TAChart und den DB- Komponenten spürt man es am schnellsten.
Blöd kann man ruhig sein, nur zu Helfen muss man sich wissen (oder nachsehen in LazInfos/LazSnippets).

Lomat
Beiträge: 37
Registriert: Fr 14. Jan 2022, 13:44

Re: Performanceanomalien bei TChart

Beitrag von Lomat »

Hallo nochmal,

ich habe da doch noch ein Problem. Das Programm beinhaltet eine Ladefunktion für ein oder mehrere Spektren (YX-Datensätze). Beim Laden das aktualisieren anzuschalten sieht erst mal schön aus, ein Spektrum nach dem anderen erscheint in den Charts. Bei vielen Spektren friert das Programm dan zunehmend ein. Ich habe vor die Visualisierung dann abzuschalten. So weit so gut, aber hier funktinieren EnabelDrawing und DisableDrawing nicht da sie nicht nur die Zeichnung, sondern auch weitere Funtionen des Charts abschalten, insbesondere die Komunikation mit einer ChartListbox. Die Folge ist ein Ausnahmefehler beim Zugriff auf die ChartListBox (Zugriffsverletzung). Was funktioniert ist, die Eigenschaft "visible" des Charts temporär auf False zu setzen. Dann zeigt er zwar das ganze Diagramm nicht mehr an, aber das Laden ist dann erträglich schnell.

Die Frage ist, ob das Vorgehen unsauber ist bzw. ich böse Überraschungen hierdurch erleben kann.

Viele Grüße

Christoph

wp_xyz
Beiträge: 5146
Registriert: Fr 8. Apr 2011, 09:01

Re: Performanceanomalien bei TChart

Beitrag von wp_xyz »

EnableDrawing/DisableDrawing ist nur dafür gedacht, während des Ladens das Neuzeichnen zu unterbinden. Idealerweise sollte der Programmblock, in dem es angewendet wird, so kurz sein, dass man gleichzeitig das DisableDrawing und das EnableDrawing auf dem Bildschirm hat. Und der Block muss mit einem Try-Finally geschützt werden, so dass die beiden immer im Takt bleiben, d.h. zu jedem DisableDrawing MUSS ein EnableDrawing gehören, sonst wird der Chart nie mehr aktualisiert.

Wie TSchnuckenbock schon bemerkt hat, fehlt bei deinem Code weiter oben das try-finally. Wenn es irgendwo während des Chart-Aufbaus zu einer Exception kommt, und die Funktion vorzeitig verlassen wird, kann der betroffene Chart nie mehr neu gezeichnet werden.
Lomat hat geschrieben: Di 16. Apr 2024, 14:16 Bei vielen Spektren friert das Programm dan zunehmend ein.
Was sind hier "viele"? Also: wieviele Spektren kannst du laden, bis der Fehler auftritt? Und wieviele Punkte enthält jedes Spektrum? Oder ist es immer nur dasselbe Spektrum, bei dem das Programm einfriert?

Aktiviere die üblichen Kandidaten: RangeChecking, OverflowChecking, I/O-Checking, HeapTrace, ... und prüfe, ob da nicht etwas vordergründig falsch ist. Ansonsten hilft nur zeitaufreibende Arbeit mit dem Debugger.
Dateianhänge
many_series.zip
(2.69 KiB) 20-mal heruntergeladen

Lomat
Beiträge: 37
Registriert: Fr 14. Jan 2022, 13:44

Re: Performanceanomalien bei TChart

Beitrag von Lomat »

EnableDrawing/DisableDrawing ist nur dafür gedacht, während des Ladens das Neuzeichnen zu unterbinden. Idealerweise sollte der Programmblock, in dem es angewendet wird, so kurz sein, dass man gleichzeitig das DisableDrawing und das EnableDrawing auf dem Bildschirm hat. Und der Block muss mit einem Try-Finally geschützt werden, so dass die beiden immer im Takt bleiben, d.h. zu jedem DisableDrawing MUSS ein EnableDrawing gehören, sonst wird der Chart nie mehr aktualisiert.
Das ist klar.

Das neue Problem bezieht sich aber nicht auf das Codebeispiel. Das funktioniert. Hierfür den doch sehr umfangreichen Code zu posten würde nur Verwirrung stiften. Mein Problem ist, dass die gleich Vorgehensweise wie Oben an einer anderen Stelle nicht funktioniert, weil zwischendurch auf eine ChartListBox (die, die TChart gehört und keine normale Listbox) auch nicht aktualisiert wird. Greift man dann auf die Listbox zu wird dann das mit Try und Finaly zum Ernstfall. Zwar sind dann das Chart bzw die Charts nicht mehr aus dem Tritt, aber der Rest meines Programms bzw. Try und Finally helfen dann nur noch die Sache ohne Datenverlust zu beenden.

Eine Exception sollte im Regelfall nicht auftreten, da ansonsten die Darstellung nicht mehr stimmt und der Nutzer nicht mehr weiß, was er tut. Ich habe dann auf die Variante mit der Sichtbarkeit gesetzt, wodurch ein Ausnahmefehler verhindert wird und wollte nur wissen, ob ich mir dadurch andere Probleme einhandel, von denen ich nichts weis.

PS: Das mit den Debuggen hat längst geklappt, sonst wüste ich nicht, dass der Fehler beim Zugriff auf die ChartListBox passiert. Genau gesagt, es wurde auf ein Element zugegriffen, was noch gar nicht existiert hat, weil die Listbox nicht mehr mit dem Chart syncronisiert wurde.

Viele Grüße Christoph

wp_xyz
Beiträge: 5146
Registriert: Fr 8. Apr 2011, 09:01

Re: Performanceanomalien bei TChart

Beitrag von wp_xyz »

Während des Ladens solltest du nicht auf die Chartlistbox zugreifen. Wozu willst du das tun?

Du könntest evtl auch die ChartListbox vom Chart trennen (ChartListbox.Chart := nil) und hinterher wieder neu verbinden (ChartListbox.Chart := Chart1), wobei ich jetzt aber nicht weiß, was mit den vorher ausgeblendeten Series passiert.

Lomat
Beiträge: 37
Registriert: Fr 14. Jan 2022, 13:44

Re: Performanceanomalien bei TChart

Beitrag von Lomat »

Während des Ladens solltest du nicht auf die Chartlistbox zugreifen. Wozu willst du das tun?
Das triffts im Prinzip. Das Coderelikt bekomme ich auf die Schnelle nicht heraus, aber genau da liegt der Fehler. DisableRedrawing sorgt ja dafür, dass das Chart in Ruhe mit Daten gefüllt werden kann. Wenn ich genauer darüber nachdenke ist das Verhalten der ChartListBox logisch. DisableRedrawing ist wohl mehr als nur das Neuzeichnen abstellen sondern so was wie BeginUpdate in anderen Komponenten,, z. B. den LineSeries. Das mit dem Visual auf False klappt hier als Workearaund. Langfristig wäre eine Reorganisation der Laden-Funktion angebracht, die vorher nicht für viele Spektren vorgesehen war.
Du könntest evtl auch die ChartListbox vom Chart trennen (ChartListbox.Chart := nil) und hinterher wieder neu verbinden (ChartListbox.Chart := Chart1), wobei ich jetzt aber nicht weiß, was mit den vorher ausgeblendeten Series passiert.
Klingt abenteuerlich, da ich keine Ahnung vom Datenaustausch zwischen den beiden Komponenten habe. Ich glaube das lasse ich lieber.

Viele Grüße Christoph

wp_xyz
Beiträge: 5146
Registriert: Fr 8. Apr 2011, 09:01

Re: Performanceanomalien bei TChart

Beitrag von wp_xyz »

Lomat hat geschrieben: Di 16. Apr 2024, 19:01
Während des Ladens solltest du nicht auf die Chartlistbox zugreifen. Wozu willst du das tun?
Das triffts im Prinzip. Das Coderelikt bekomme ich auf die Schnelle nicht heraus, aber genau da liegt der Fehler.
Wahrscheinlich liegt darin auch der Grund dafür, dass du überhaupt DisableDrawing/EnableDrawing verwenden musst. Denn wie du an dem oben heute geposteten Projektchen sehen kannst, kann TAChart auch ohne DisableDrawing/EnableDrawing Unmengen von Daten laden, ohne dass man eine Verlangsamung bemerkt.

Antworten