Bug 117059 - Optimize chart drawing using medium to large datasets
Summary: Optimize chart drawing using medium to large datasets
Status: RESOLVED FIXED
Alias: None
Product: z_Archived
Classification: Eclipse Foundation
Component: BIRT (show other bugs)
Version: unspecified   Edit
Hardware: PC Windows XP
: P3 critical (vote)
Target Milestone: 2.5.0 M4   Edit
Assignee: Yi Wang CLA
QA Contact:
URL:
Whiteboard: Obsolete
Keywords: performance, plan
Depends on:
Blocks:
 
Reported: 2005-11-18 11:00 EST by Evangelos CLA
Modified: 2010-03-21 22:30 EDT (History)
5 users (show)

See Also:


Attachments

Note You need to log in before you can comment on or make changes to this bug.
Description Evangelos CLA 2005-11-18 11:00:28 EST
The time required to draw line/stock charts using large datasets(1500
data points) takes about 3 seconds. This is obviously an optimization
issue on the graph rendering side. This problem makes it impossible to
use multiple charts in an RCP enviroment, because resizing the chart
causes a chain reaction for refreshing (3 seconds for each chart).
Comment 1 David Michonneau CLA 2005-11-21 09:01:20 EST
Please specify with which version of BIRT this is happening and which device 
renderer you're using. Thanks
Comment 2 Evangelos CLA 2005-11-22 01:00:51 EST
im currently using S20051110. Im using the SWT device renderer.

(In reply to comment #1)
> Please specify with which version of BIRT this is happening and which device 
> renderer you're using. Thanks
> 

Comment 3 David Michonneau CLA 2005-12-22 04:52:29 EST
Do you show the X Axis labels or not? Have you tried turning their visibility 
to off? The code that auto-drops them introduces an overload in the chart 
generation process.
Comment 4 Evangelos CLA 2005-12-22 08:53:08 EST
Yes I have tried that. The performance does improve margiannaly.
A huge delay in redrawing the graph still exists, making it unusable.
Comment 5 David Michonneau CLA 2005-12-22 09:00:29 EST
That's strange, an instrumentation shows 90% of time spent in there, making 
them invisble should have increased the performance a lot. Maybe there is some 
other problem, are you using -DR31ENHANCE=1? This can also be a performance 
issue, since it's using reflection to keep the SWT 3.0 compatibility.

Can you send me your chart design, or the API code to create the chart? maybe 
there is some specific settings you have set that introduced that problem.
Comment 6 David Michonneau CLA 2005-12-22 14:25:57 EST
Instrumentation shows that without X Axis labels, 50% of time is spent by the 
EMF model doing copies, creation, set, get, more precisely on the LocationImpl 
and ColorDefinitionImpl objects. These objects are being used for computations 
and rendering, which is wrong, they should first be converted to lightweight 
java objects due to their intensive usage. However this means a major change 
throughout the chart engine, so this is an enhancement for a future version

The remaining of the time is evenly distributed among generation, internal 
computations and rendering of the chart.

Besides that I will optimize the X Axis labels auto-drop algorithm to be about 
twice faster. To complete it we could provide more control in the future to 
manually force the dropping through the API/model, for instance by showing only 
one label out of n labels, where can be configured in the model.
Comment 7 David Michonneau CLA 2005-12-22 19:47:51 EST
I have checked in this X-Axis labels optimization. I now propose to defer this 
performance enhancement to a future release.

Possible work-around in the meantime for the reporter:
- Reduce the number of points in your dataset, this won't affect the chart 
drawing quality, you should be able to get a nice looking chart with only a few 
hundreds points (or even less) and by selecting show lines as curves. 
- Hide the ticks on the X-Axis, Hide the labels on the X-Axis
Comment 8 Zhiqiang Qian CLA 2006-03-15 01:23:44 EST
Replaced some of the EcoreUtil.copy() with custom copyInstance() method, so chart building/rendering should be faster now. But still need further thoughts to reduce the EMF performance impact.
Comment 9 Zhiqiang Qian CLA 2006-03-22 00:36:02 EST
Mark as fixed.
Comment 10 Remy Suen CLA 2008-09-23 03:59:23 EDT
Redrawing a graph with 1500 data points is still taking a fair amount of time on BIRT 2.3.0. While it doesn't appear to be taking three seconds on my computer, which is what Evangelos originally stated, it is still too slow in my opinion.

We are working on an RCP product that grabs the polled statistics from a machine and renders it for the administrator. If you consider a poll rate of one per second, a whole day's statistic will have 86,400 points.

-------------------

The statistics below were retrieved from running the LargeDataSet class below and then trying to resize it. It seems the initial drawing procedure takes more time than subsequent redraws but it is still not fast enough.

Max set at 1500:
Rendering Time:	2451
Rendering Time:	1233
Rendering Time:	1093

Max set at 5000:
Rendering Time:	4903
Rendering Time:	3638
Rendering Time:	3451
Rendering Time:	3498

Max set at 10000:
Rendering Time:	9368
Rendering Time:	6964
Rendering Time:	6839

-------------------

import java.util.List;

import org.eclipse.birt.chart.api.ChartEngine;
import org.eclipse.birt.chart.device.IDeviceRenderer;
import org.eclipse.birt.chart.exception.ChartException;
import org.eclipse.birt.chart.factory.GeneratedChartState;
import org.eclipse.birt.chart.factory.Generator;
import org.eclipse.birt.chart.model.ChartWithAxes;
import org.eclipse.birt.chart.model.attribute.Bounds;
import org.eclipse.birt.chart.model.attribute.Marker;
import org.eclipse.birt.chart.model.attribute.impl.BoundsImpl;
import org.eclipse.birt.chart.model.attribute.impl.ColorDefinitionImpl;
import org.eclipse.birt.chart.model.component.Axis;
import org.eclipse.birt.chart.model.component.Series;
import org.eclipse.birt.chart.model.component.impl.SeriesImpl;
import org.eclipse.birt.chart.model.data.DataSet;
import org.eclipse.birt.chart.model.data.SeriesDefinition;
import org.eclipse.birt.chart.model.data.impl.NumberDataSetImpl;
import org.eclipse.birt.chart.model.data.impl.SeriesDefinitionImpl;
import org.eclipse.birt.chart.model.impl.ChartWithAxesImpl;
import org.eclipse.birt.chart.model.type.LineSeries;
import org.eclipse.birt.chart.model.type.impl.LineSeriesImpl;
import org.eclipse.birt.core.framework.PlatformConfig;
import org.eclipse.swt.SWT;
import org.eclipse.swt.events.DisposeEvent;
import org.eclipse.swt.events.DisposeListener;
import org.eclipse.swt.events.PaintEvent;
import org.eclipse.swt.events.PaintListener;
import org.eclipse.swt.graphics.GC;
import org.eclipse.swt.graphics.Image;
import org.eclipse.swt.graphics.Rectangle;
import org.eclipse.swt.layout.FillLayout;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Shell;

class LargeDataSet extends Composite implements PaintListener {

	private IDeviceRenderer deviceRenderer;

	private ChartWithAxes chart;

	private GeneratedChartState chartState;

	private SeriesDefinition sdX;
	private SeriesDefinition sdY;

	private Image imgChart;
	private GC gcImage;

	LargeDataSet(Composite parent, int style) {
		super(parent, style);

		try {
			PlatformConfig config = new PlatformConfig();
			config.setProperty("STANDALONE", "true"); //$NON-NLS-1$ //$NON-NLS-2$
			deviceRenderer = ChartEngine.instance(config).getRenderer("dv.SWT");//$NON-NLS-1$
		} catch (ChartException pex) {
			pex.printStackTrace();
			System.exit(0);
		}

		chart = ChartWithAxesImpl.create();

		// X-Axis
		Axis xAxisPrimary = chart.getPrimaryBaseAxes()[0];

		// X-Series
		sdX = SeriesDefinitionImpl.create();
		xAxisPrimary.getSeriesDefinitions().add(sdX);

		// Y-Axis
		Axis yAxisPrimary = chart.getPrimaryOrthogonalAxis(xAxisPrimary);

		// Y-Series
		sdY = SeriesDefinitionImpl.create();
		yAxisPrimary.getSeriesDefinitions().add(sdY);
		sdY.getSeriesPalette().shift(-1);

		addPaintListener(this);
		addDisposeListener(new DisposeListener() {
			public void widgetDisposed(DisposeEvent e) {
				if (imgChart != null) {
					imgChart.dispose();
				}

				if (gcImage != null) {
					gcImage.dispose();
				}
			}
		});
	}

	public static void main(String[] args) {
		Display display = Display.getDefault();
		final Shell shell = new Shell(display);
		shell.setLayout(new FillLayout());
		shell.setSize(640, 480);

		LargeDataSet adbv = new LargeDataSet(shell, SWT.NO_BACKGROUND);

		int MAX = 1500;
		double[] times = new double[MAX];
		double[] values = new double[MAX];
		for (int i = 0; i < MAX; i++) {
			times[i] = System.currentTimeMillis() + i;
			values[i] = i;
		}

		adbv.initializeGraph(times, values);

		shell.open();
		while (!shell.isDisposed()) {
			if (!display.readAndDispatch()) {
				display.sleep();
			}
		}
		display.dispose();
	}

	private void initializeGraph(double[] timeArray, double[] statisticValues) {
		DataSet xDataSet = NumberDataSetImpl.create(timeArray);
		Series xSeries = SeriesImpl.create();
		xSeries.setDataSet(xDataSet);
		sdX.getSeries().add(xSeries);

		DataSet yDataSet = NumberDataSetImpl.create(statisticValues);
		LineSeries ySeries = (LineSeries) LineSeriesImpl.create();
		ySeries.setDataSet(yDataSet);
		ySeries.getLineAttributes().setColor(ColorDefinitionImpl.BLACK());
		List markers = ySeries.getMarkers();
		for (Object object : markers) {
			Marker marker = (Marker) object;
			marker.setVisible(false);
		}
		sdY.getSeries().add(ySeries);
	}

	public void paintControl(PaintEvent e) {
		long l = System.currentTimeMillis();
		if (gcImage != null) {
			gcImage.dispose();
		}

		if (imgChart != null) {
			imgChart.dispose();
		}

		Rectangle d = getClientArea();
		imgChart = new Image(getDisplay(), d);
		gcImage = new GC(imgChart);
		deviceRenderer.setProperty(IDeviceRenderer.GRAPHICS_CONTEXT, gcImage);

		Bounds bo = BoundsImpl.create(0, 0, d.width, d.height);
		bo.scale(72d / deviceRenderer.getDisplayServer().getDpiResolution());

		Generator gr = Generator.instance();

		try {
			chartState = gr.build(deviceRenderer.getDisplayServer(), chart, bo, null, null, null);
			gr.render(deviceRenderer, chartState);
			e.gc.drawImage(imgChart, d.x, d.y);
			l = System.currentTimeMillis() - l;
			System.out.println("Rendering Time:\t" + l);
		} catch (ChartException ce) {
			ce.printStackTrace();
		}
	}

}
Comment 11 Wenfeng Li CLA 2008-10-22 12:35:48 EDT
Zhiqiang,  Shall we consider this enh idea from David in comment #6?

"Instrumentation shows that without X Axis labels, 50% of time is spent by the 
EMF model doing copies, creation, set, get, more precisely on the LocationImpl 
and ColorDefinitionImpl objects. These objects are being used for computations 
and rendering, which is wrong, they should first be converted to lightweight 
java objects due to their intensive usage. However this means a major change 
throughout the chart engine, so this is an enhancement for a future version"
Comment 12 Zhiqiang Qian CLA 2008-10-22 23:54:26 EDT
Yes, we can consider using more plain java objects instead of EMF objects for the computation. We already did some similar things for the Axis model.
Comment 13 Yi Wang CLA 2008-11-03 05:52:04 EST
Many optimization are made to accelerate Chart graphics, especially for chart with large dataset, noteworthy are the followings:
- accelerated the formatting of axis labels
- accelerated the computation of labels bounding
- accelerated the copying of some EMF Object, i.e. Fill, Marker
- accelerated the set operation of some EMF Object, i.e. Location, Bounds
- accelerated the rendering of axis ticks for large dataset
- reduced memory usage of AutoScale for large dataset

The performance is improved dramatically, the same test with LargeDataSet and 5000 data points are made for both the previous version and the new version, see below:

Max set at 5000:

previous
--------------------
Rendering Time:	8547
Rendering Time:	7219
Rendering Time:	7016
Rendering Time:	7047

now
--------------------
Rendering Time:	2218
Rendering Time:	890
Rendering Time:	656
Rendering Time:	422


Comment 14 Xiang Li CLA 2009-03-10 05:46:55 EDT
Verified in BIRT2.5.0 (daily build 090105)