TMS Chart performance

Delphi: Delphi 7 pro SP1

Good day TMS Team,
My app becomes unusable (response is to slow, APP is like freezed) when I have a daily retrospective amount of data (86 400 points),
Yoy should be able to reproduce my case \Demos\Demo XY DateTime\Demo.dpr if you increase points count in cycle

procedure TForm10.FormCreate(Sender: TObject);
var
  i: integer;
  d: TDateTime;
begin
  AdvGDIPChartView1.BeginUpdate;
  with AdvGDIPChartView1.Panes[0] do
  begin
    XAxis.UnitType := utMinute;
    XAxis.Size := 130;
    Title.Text := 'May 2010';
    Title.Size := 50;
    Title.Font.Size := 16;
    Range.StartDate := EncodeDate(2010, 05, 1);
    with Series[0] do
    begin
      AutoRange := arEnabled;
      ChartType := ctXYLine;
      XAxis.MajorUnitTimeFormat := 'dd/mm/yyyy';
      XAxis.TextBottom.Angle := 50;
      XAxis.MajorUnitSpacing := 20;
      XAxis.XYValuesOffset := 60;
      XAxis.AutoUnits := false;
      for I := 0 to 86400 do
      begin
        d := EncodeDate(2010, 05, 1) + I * 5;
        AddSingleXYPoint(ConvertDateTimeToX(d),  Random(100));
      end;
    end;
    Range.RangeTo := 30;
  end;
  AdvGDIPChartView1.EndUpdate;
end;

Please add AdvGDIPChartView1.BeginUpdate; & AdvGDIPChartView1.EndUpdate around the code:

procedure TForm10.FormCreate(Sender: TObject);
var
  i: integer;
  d: TDateTime;
begin
  AdvGDIPChartView1.BeginUpdate;
  with AdvGDIPChartView1.Panes[0] do
  begin
    XAxis.UnitType := utMinute;
    XAxis.Size := 130;
    Title.Text := 'May 2010';
    Title.Size := 50;
    Title.Font.Size := 16;
    Range.StartDate := EncodeDate(2010, 05, 1);
    with Series[0] do
    begin
      AutoRange := arEnabled;
      ChartType := ctXYLine;
      XAxis.MajorUnitTimeFormat := 'dd/mm/yyyy';
      XAxis.TextBottom.Angle := 50;
      XAxis.MajorUnitSpacing := 20;
      XAxis.XYValuesOffset := 60;
      XAxis.AutoUnits := false;
      XAxis.XYValues := False;

      for I := 0 to 86400 do
      begin
        d := EncodeDate(2010, 05, 1) + I * 5;
        AddSingleXYPoint(ConvertDateTimeToX(d),  Random(100));
      end;
    end;
    Range.RangeTo := 30;
  end;
  AdvGDIPChartView1.EndUpdate;

end;

Hello @Pieter_Scheldeman , thank you for the update. But why is it working so bad with a big range let's say 10k points? Some extrenal dependency flaw, like gdiplus.dll? I mean usual Chart component can easily take this amount of points with no backdraw for performance of the whole application. Yep, it takes time to add all those 10k points, but from then on app works nice and smooth.

And one more question, obviously If I keep this component, I need to implement some pagination, how is it done? cause If I change the startdate and RangeTo the graphical view seems not be changing...

procedure TForm10.btnNextPageClick(Sender: TObject);
begin
  AdvGDIPChartView1.BeginUpdate;
    AdvGDIPChartView1.Panes[0].Range.StartDate := IncSecond(AdvGDIPChartView1.Panes[0].Range.StartDate, GetPageSize);
    AdvGDIPChartView1.Panes[0].Range.RangeTo := GetPageSize;
  AdvGDIPChartView1.EndUpdate;
end;

Or I should not add all 86400 points from the data store to the ChartView and reload the view as a whole once the pagination button is clicked? Like clear series and add points from next page?

Hi,

We'll investigate why the performance decreased with the amount of numbers.
This means indeed that you will have to limit the amount of points that are added. The calculation happens on the total number of points added, regardless of the range. This is something that can be optimized however, so we'll write this down for investigation. I suggest to add a limit amount of points, and then work with paging to view another range. 5000 - 10000 points will result in better performance.

Hello @Pieter_Scheldeman, once you're planning to check the performance, could you pls have a look at the case when mouse is moving over the chart causes CPU usage increases drastically. To check that you can use the sample from this thread.

sample.zip (9.8 KB)

  • add some panels to see the difference when mouse is over the chart
  • add some significant (lets say 86 400 points) to the chart
  • keep moving mose inside chart
    -- Expected no performance degradation
    -- Actual CPU usage grows and UI stops responding for a couple of seconds.

Hi,

The mouse move is also related to the amount of points. We have investigated this here and there is currently no short term solution to map all points in a single view, so we suggest to using paging, limiting the amount of added points until the performance is acceptable. We have added this on our todolist for investigation, to see if we can increase performance to work with a large amount of points.

@Pieter_Scheldeman, can you feel my pain from using this component? Standard TChart works with series with 86400 points just fine, and you don`t have to spend days of dev time configuring it :(

Is there any timeframe when you have the result of the investigation?
Is it possible to get a refund and forget about this component forever?

We have tested this here in comparison with TChart, and TChart also has the same performance issue, when adding 86400 points at once. Also, hovering the mouse over TChart makes the CPU go up, in the same way as it does with TAdvChartView. We have tested with TChart, by dropping a new instance on the form, adding the following code:

  s: TChartSeries;
  I: Integer;
begin
  Series1.BeginUpdate;
  for I := 0 to 86400 do
    Series1.Add(Random(1000));
  Series1.EndUpdate;

CPU usage was around 15% on our machine. with such a large amount of points, TChart also locks up with a X-Axis range that tries to show all points at once.

With the sample you have sent based on TMS VCL Chart, adding 86400 points at once, will also result in the same performance issue. Testing this here resulted in a CPU usage of 19%. Please note that the VCL chart uses more resources and more intensive GDI+ graphics compared to TChart.

The real issue here is the amount of points being visible at the same time, so to fix the issue with performance in the TMS VCL Chart sample, please change this line of code:

Range.RangeTo := pointsCount - 1;

to a fixed number of points (for example 400)

Range.Rangeto := 400

then you can enter 86400 points in the edit box, whilst keeping a more performant chart by only visualizing 400 points at once.

@Pieter_Scheldeman, I don`t know what have you been testing guys, but consider things below please:

  1. VCL chart TSeries class doesn't contain Begin/EndUpdate methods in D7. Have a look at the project I'm attaching to this post.This components works fine when I move mouse over component with 100 000 points. Of course, it takes time to add those points, but that`s something we can explain to customers.

  2. When I simply comment out 1000 lines of nooddle code in procedure TAdvChartView.MouseMove(Shift: TShiftState; X, Y: Integer); :man_facepalming: I get normal performance of the application with 100 000 points.

  3. How would you explain a user that he doesn't want to see the chart that shows behavior of a very expensive equipment within the full period, but scroll through dozens of pages, a jedi trick? We'll simply loose a contract like this.

  4. When you say <<We have tested>> do you mean that it was you personally? Could you explain me why I could find a workaround, by simply AdvGDIPChartView1.EnableInteraction := False; only 30m stepping the code in debugger, that I've never seen before? Believe I'm not a jedi at all...

  5. I'd like to start negotiation regarding this situation, please tag a superviser in your reply, so that he'll find a clear picture. Maybe we could find a solution to this situation like an extended period of license for this component and a clear time frame when it will be optimezed?

sample.zip (502.4 KB)

Hi,

I have been testing in Delphi 11, which has GDI+ enabled by default for TChart. I suppose that Delphi 7 does not have the BeginUpdate & EndUpdate as well as GDI+ disabled by default. TAdvChartView (GDI) or TAdvGDIPChartView (GDI+) is designed to load large amount of points with BeginUpdate & EndUpdate, has the ability to interact with it, but then needs limited amount of visible points. Of course, interaction can be turned off with EnableInteraction to avoid the performance issues when hovering over the chart, but when interaction is required, the amount of visible points needs to be limited (as explained in one of my previous posts). Unfortunately it's still unclear now if you need interaction or not. There are solutions for both cases, but the approach differs. Additionally, GDI+ disabled TeeChart will of course load faster and work better than a GDI+ enabled TAdvChartView (TAdvGDIPChartView). GDI+ is asking for a lot more resources in comparison with GDI.

@Pieter_Scheldeman it looks like you’re not reading at all. Delphi 7 is mentioned in the start topic. And it is absolutely clear that bad code of the component loops through all points to get value under mouse pointer - on mouse move and simply abuses applications event que.

If that’s clear that interaction disabling helps why no one proposed that instead of cumbersome pagination idea of yours?

Do I have to sit and debug a paid library myself, or you do have someone more competent than @Pieter_Scheldeman out there in a company?

  1. We had no details on what kind of interaction you needed or not needed.
  2. In your Oct 6 post, you mentioned a need for paging yourself first.
  3. When interaction is needed like getting values you hover over or show hints with values during hovering, mouse interaction is needed and this kind of interaction involves searching through points.
  4. A fair performance comparison is between components that are both GDI based or both GDI+ based. It is not fair to compare a GDI+ component with a GDI component.
  5. In our support center, we try at all times to help anyone asking questions here and the answers are always based on the amount of detail provided. The more detail, the better we can help.

If you have further technical questions on TMS charts, let us know and we continue to do our best to provide help. And let's keep the discussion respectful.

Good day @brunofierens , nice to meet you. I'm sorry, if my tone sound unpolite. The deadline on the project is closer and closer and I'm feeling a terrible pressure.

Let's start over, one great thing is the Free priority support through email and forum left for this component at the moment, true.

Okay, when user moves the mouse over the canvas of the component, application freezes and the whole application is not usable. We're not able to migrare from Delphi 7 which is on the list on the component page, so please don't suggest anything like upgrade to Delphi 11 and prospere :)

When I turn EnableInteraction off - it is something not desirable, but a temporary workaround. From my point of view onMouseMove is no place for any heavy code at no times, do you agree? When interaction is enabled function TAdvChart.GetXAxisValue(X, Y: Integer; R: TRect): TXAxisRangeValue; is called among others and performance degrades drastically, as application still performs computations and new onMouseMove is already enqued. And thats it, no GDI, no magic, just a bad design.

I see a stable solution like: any kind of the delay of EnableInteraction block of code, so that the decendant of TAdvChart don't perform computations for all Mouse Moves but the last one.

  1. Could you pls, have a look at the code, and tell me is it possible to fix this behavior in a stable release and when will that be delievered?
  2. Please show me where exactly GDI / GDI+ calls are located in the mouse move event handler call stack? As talk is cheap, show me the code, please :)
  3. Are there any other techniques, except begin/endupdate I can benifit from, when I'm working with (1 pane with 3 series 86400 point each)? Please consider these scenarios:
    3.1 Form hide/show
    3.2 Form resizing

First of all, please inform what exact kind of interaction you need from the mouse move event?
If there is no interaction needed, simply turn off EnableInteraction.
If interaction is needed, please provide details about what interaction exactly.
An interaction such as showing chart point values in a hint or via another way as the mouse hovers over a point, is an interaction where it cannot be avoided that a search must be performed through the chart data. For 'normal' numbers of data displayed, i.e. typically it doesn't make much sense to show more data horizontally than there are pixels on the screen (so, let's say <= 4000 data points), this should not affect performance.

For your other points:

  1. I first need to know what EXACT interaction IS needed before we can look if it makes sense to somehow implement it in a more performant way.
  2. The difference between GDI and GDI+ is in the rendering. TAdvGDIPChartView uses the GDI+ API for drawing. This Microsoft Windows API uses software anti-aliasing and is known to be slower than the non-aliased GDI equivalent.
  3. On form resize, a redraw will happen. This is necessary and cannot be avoided. Drawing 86400 points is a lot, so in GDI+, where this is happening anti-aliased, this has for the redraw its performance toll. For a line chart, this means drawing 86400-1 anti-aliased lines. Not sure how you expect this number to be reduced?

Hi @brunofierens, thank you for the response.

I need all kinds of interaction as requirements do change and in the next release I don’t want to be limited. Or you could give me a set of options to choose from.

Mouse moving optimisation with a delayed call looks like a pretty easy fix, could you please share your concerns why it can not be done?

@brunofierens thanks for asking:

Blockquote Not sure how you expect this number to be reduced?

Regarding GDI performance, Ive seen this in Steema teechart, those guys provide an excellent idea: user can choose between 2 behaviours:

  • draw all points: yep, takes time to draw / redraw comparable to TMS.

  • draw only points that have different pixel: and the chart works nice and smooth even with 1 million points on dataset, as it shows always a reasonable amount of points. When you zoom in you see more and more details as you change the scale of axis. Could you add this as a feature request for future releases?

Can you describe in detail what "all kinds of interaction" means for you?

We will investigate if it is feasible to implement such performance tweaks as skipping line drawing for identical pixels for cases of very large amounts of data.

@brunofierens is it possible to simply get a refund for this library and forget this nightmare?

Contact sales by email for this