Page 1 of 1

Slow histograms

Posted: Fri Jun 23, 2017 10:33 am
by 16578896
Hi,

Users expect series of a certain type to be displayed as histograms. Clearly plotting these is going to be slower than simple point or line series, presumably due to the pixel calculations that need to be done, and the screen rendering that ensues. In the extreme case, users are plotting perhaps 50 series, each of over 100,000 points, on the same axis. It takes many minutes to generate the graph, which is perhaps not surprising. Of course, they always zoom in a long way to actually view the data, but all zoom operations are very slow too. It appears to users that the software has hung, and they don't notice that the CPU is working manically.

THistogramSeries seems rather faster than TBarSeries, and switching off the 'hover' setting seems to help, but what else can be done to optimize performance? At full zoom extents, with maybe 100x more points to plot than available pixels, I guess a lot of unnecessary calculations are being done.

Thanks for any advice.

Toreba

Re: Slow histograms

Posted: Fri Jun 23, 2017 2:02 pm
by yeray
Hello Toreba,

You could change your THistogramSeries for TFastLineSeries, which are much faster, each one with a TSeriesRegionTool. Then when the zoom is close enough, you could change tour series to THistogramSeries and hide the TSeriesRegionTools. Ie:

Code: Select all

uses Series, StatChar, TeeSeriesRegion;

const threshold=100;

procedure TForm1.FormCreate(Sender: TObject);
var i: Integer;
    tmpSeries: TChartSeries;
begin
  Chart1.View3D:=False;
  Chart1.Legend.Visible:=False;
  Chart1.Hover.Visible:=False;

  for i:=0 to 49 do
    Chart1.AddSeries(THistogramSeries).FillSampleValues(1000);

  Chart1.AutoRepaint:=False;
  //Change all the THistogramSeries for TFastLineSeries with TSeriesRegionTools
  for i:=0 to Chart1.SeriesCount-1 do
  begin
    with Chart1.Tools.Add(TSeriesRegionTool) as TSeriesRegionTool do
    begin
      Color:=Chart1[i].Color;
      Series:=Chart1[i];
      Pen.Hide;
    end;

    tmpSeries:=Chart1[i];
    tmpSeries:=ChangeSeriesType(tmpSeries, TFastLineSeries);
    TFastLineSeries(tmpSeries).DrawAllPoints:=False;
    TFastLineSeries(tmpSeries).Stairs:=True;
    tmpSeries.Pen.Color:=clBlack;
  end;

  Chart1.Title.Text.Text:='Now we have TFastLineSeries';

  Chart1.AutoRepaint:=True;
  Chart1.Draw;
end;

procedure TForm1.Chart1Zoom(Sender: TObject);
var i: Integer;
    tmpSeries: TChartSeries;
begin
  if (Chart1[0] is TFastLineSeries) and (Chart1.Axes.Bottom.Maximum-Chart1.Axes.Bottom.Minimum < threshold) then
  begin
    Chart1.AutoRepaint:=False;

    for i:=0 to Chart1.SeriesCount-1 do
    begin
      tmpSeries:=Chart1[i];
      tmpSeries:=ChangeSeriesType(tmpSeries, THistogramSeries);
      Chart1.Tools[i].Active:=False;
    end;

    Chart1.Title.Text.Text:='Now we have THistogramSeries';

    Chart1.AutoRepaint:=True;
    Chart1.Draw;
  end;
end;

procedure TForm1.Chart1UndoZoom(Sender: TObject);
var i: Integer;
    tmpSeries: TChartSeries;
begin
  if (Chart1[0] is THistogramSeries) then
  begin
    Chart1.AutoRepaint:=False;

    for i:=0 to Chart1.SeriesCount-1 do
    begin
      tmpSeries:=Chart1[i];
      tmpSeries:=ChangeSeriesType(tmpSeries, TFastLineSeries);
      TFastLineSeries(tmpSeries).DrawAllPoints:=False;
      TFastLineSeries(tmpSeries).Stairs:=True;
      tmpSeries.Pen.Color:=clBlack;
      Chart1.Tools[i].Active:=True;
    end;

    Chart1.Title.Text.Text:='Now we have TFastLineSeries';
    Chart1.AutoRepaint:=True;
    Chart1.Draw;
  end;
end;

Re: Slow histograms

Posted: Mon Jun 26, 2017 9:08 pm
by 16578896
Yeray,

Fantastic, thanks very much for this explanation.

I notice that the ChangeSeriesType method triggers the chart's OnRemoveSeries event. So is it true to say that this call simply creates and populates a new series of the specified class, removes the old series from the chart series list and adds the new?

Regards

Toreba

Re: Slow histograms

Posted: Tue Jun 27, 2017 8:17 am
by yeray
Hello Toreba,

Yes, pretty much:

Code: Select all

Function ChangeSeriesType(var ASeries:TChartSeries; NewType:TChartSeriesClass):TChartSeries;
var NewSeries : TChartSeries;
begin
  if ASeries.ClassType<>NewType then
  begin
    NewSeries:=CreateNewSeries(ASeries.Owner,ASeries.ParentChart,NewType);

    if Assigned(NewSeries) then
    begin
      AssignSeries(ASeries,NewSeries);
      ASeries.Free;
      ASeries:=NewSeries;
    end;
  end;

  result:=ASeries;
end;