DynamicDataDemo 3/CombinedDomainXYPlot is very SLOW

A discussion forum for JFreeChart (a 2D chart library for the Java platform).
Locked
gizza
Posts: 7
Joined: Tue Oct 14, 2008 8:25 am

DynamicDataDemo 3/CombinedDomainXYPlot is very SLOW

Post by gizza » Thu Oct 23, 2008 1:15 am

Hi everyone,

I googled a little bit but I could not find an answer execpt ppl with the same problem.

For my a vital sign live chart I am using the DynamicDataDemo 3 from the Developers Guide with little bit of variation. So my chart is fed by a socket that receives data every 100ms that will be added to the chart.


My Problem:

The chart works fine and does not crash but it is very slow and resource consuming (50% CPU load and more). So I was wondering if did something wrong. It gets worse as soon as the chart reached the end is shifting the graph and the time grid.

Instead of using as it is done in the example

Code: Select all

add(new Millisecond(), newValue)
I am using

Code: Select all

addOrUpdate(new Millisecond(), newValue)
as it turned out the chart crashed with the add method.

My computer is a Windows machine with 3 GHz and 2 GB ram so I think that cannot be the bottleneck.

Following my code:

Code: Select all

package org.pmmc.gui.elements;

import java.awt.BasicStroke;
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.FlowLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;

import javax.swing.BorderFactory;
import javax.swing.JButton;
import javax.swing.JPanel;

import org.jfree.chart.ChartPanel;
import org.jfree.chart.JFreeChart;
import org.jfree.chart.axis.DateAxis;
import org.jfree.chart.axis.NumberAxis;
import org.jfree.chart.axis.ValueAxis;
import org.jfree.chart.plot.CombinedDomainXYPlot;
import org.jfree.chart.plot.XYPlot;
import org.jfree.chart.renderer.xy.StandardXYItemRenderer;
import org.jfree.data.time.Millisecond;
import org.jfree.data.time.TimeSeries;
import org.jfree.data.time.TimeSeriesCollection;
import org.jfree.ui.ApplicationFrame;
import org.jfree.ui.RefineryUtilities;
//import org.jfree.ui.Spacer;
import org.pmmc.util.SystemSettings;

/**
 * A demonstration application showing a time series chart where you can dynamically add
 * (random) data by clicking on a button.
 *
 */
public class CombinedVitalSignChart  {

    /** The number of subplots. */
    public static final int SUBPLOT_COUNT = 5;
    
    /** The datasets. */
    private TimeSeriesCollection[] datasets;
    
    private TimeSeriesCollection pulseDatasets;
    private TimeSeriesCollection respirationDatasets;
    private TimeSeriesCollection systolicBPDatasets;
    private TimeSeriesCollection diastolicBPDatasets;
    private TimeSeriesCollection temperatureDatasets;
    private TimeSeriesCollection oxSaturationDatasets;
    
    private int timeAxisLength;
    
    /** The most recent value added to series 1. */
    private double[] lastValue = new double[SUBPLOT_COUNT];
    
    /** contains the entire charts */
    private JPanel globalChartPanel;
    
    private Color labelColor = Color.WHITE;

    /**
     * Constructs a new demonstration application.
     *
     * @param title  the frame title.
     */
    public CombinedVitalSignChart() {
        final CombinedDomainXYPlot plot = new CombinedDomainXYPlot(new DateAxis());
        this.datasets = new TimeSeriesCollection[SUBPLOT_COUNT];
        
        timeAxisLength = new Integer(SystemSettings.getProperty("vitalsigns.graph.timeaxislength"))*60000;
        
        for (int i = 0; i < SUBPLOT_COUNT; i++) {

        	this.lastValue[i] = 100.0;
            final TimeSeries series = new TimeSeries("Random " + i, Millisecond.class);
            this.datasets[i] = new TimeSeriesCollection(series);
            
            if(i==2){ //adds additional graph to blood pressure plot
            	final TimeSeries series2 = new TimeSeries("Random " + i, Millisecond.class);
            	this.datasets[i].addSeries(series2);
            }
            
            final NumberAxis rangeAxis = new NumberAxis(); //Axis name
            rangeAxis.setAutoRangeIncludesZero(false);            
            rangeAxis.setTickLabelPaint(labelColor); //set range label color
            rangeAxis.setLabelPaint(labelColor); //set range unit color
            
            StandardXYItemRenderer renderer = new StandardXYItemRenderer();
            
            if(i == 0){ //pulse
            	rangeAxis.setRange(-2.0, 2.0);
            	renderer.setSeriesPaint(0, Color.RED);
            	renderer.setSeriesStroke(0, new BasicStroke(1f)); 
            }else if(i == 1){ //respiration
            	//rangeAxis.setRange(0, 30.0);
            	renderer.setSeriesPaint(0, Color.MAGENTA);
            	renderer.setSeriesStroke(0, new BasicStroke(1.5f)); 
            	rangeAxis.setAutoRangeMinimumSize(30.0);
            }else if(i == 2){//BP
            	rangeAxis.setAutoRangeMinimumSize(200.0);
            	renderer.setSeriesPaint(0, Color.GREEN);
            	renderer.setSeriesPaint(1, Color.YELLOW);
            	renderer.setSeriesStroke(0, new BasicStroke(1.5f)); 
            }else if(i == 3){//Temp
            	rangeAxis.setAutoRangeMinimumSize(50.0);
            	renderer.setSeriesPaint(0, Color.BLUE);
            	renderer.setSeriesStroke(0, new BasicStroke(1.5f)); 
            }else if(i == 4){//oxy
            	rangeAxis.setAutoRangeMinimumSize(100.0);
            	renderer.setSeriesPaint(0, Color.ORANGE);
            	renderer.setSeriesStroke(0, new BasicStroke(1.5f)); 
            }
            
            
            
            final XYPlot subplot = new XYPlot(
                    this.datasets[i], null, rangeAxis, renderer);
            //subplot.setOutlinePaint(Color.ORANGE);
            
            subplot.setBackgroundPaint(Color.BLACK); //grid color
            subplot.setDomainGridlinePaint(Color.white); 
            subplot.setRangeGridlinePaint(Color.white);
            plot.add(subplot);
            
        }
        
        
        final JFreeChart chart = new JFreeChart(null, plot); //null == no title
//        chart.getLegend().setAnchor(Legend.EAST);
        chart.setBorderPaint(Color.black);
        chart.setBorderVisible(true);
        chart.setBackgroundPaint(Color.BLACK); //global char color
        chart.removeLegend();
        
        plot.setBackgroundPaint(Color.BLACK);
        plot.setDomainGridlinePaint(Color.WHITE);
        plot.setRangeGridlinePaint(Color.WHITE);
  //      plot.setAxisOffset(new Spacer(Spacer.ABSOLUTE, 4, 4, 4, 4));
        
        //Time x-axis
        final ValueAxis axis = plot.getDomainAxis();
        axis.setAutoRange(true);
        axis.setFixedAutoRange(timeAxisLength);  // 60 seconds
        axis.setTickLabelPaint(labelColor);
        
        
        //Get his panel
        globalChartPanel = new JPanel(new BorderLayout());

        final ChartPanel chartPanel = new ChartPanel(chart);
        globalChartPanel.add(chartPanel);


    }

    
    /**
     * Returns the JPanel containing the charts.
     * @return
     */
    public JPanel getChartPanel(){
    	return globalChartPanel;
    }
    
    /**
     * addOrUpdates a new value to the chart.
     * @param newValue double chart value
     */
    public void addOrUpdatePulseRateValueToChart(double newValue){
    	this.datasets[0].getSeries(0).addOrUpdate(new Millisecond(), newValue);
    }
    
    /**
     * addOrUpdates a new value to the chart.
     * @param newValue double chart value
     */
    public void addOrUpdateRespirationValueToChart(double newValue){
    	this.datasets[1].getSeries(0).addOrUpdate(new Millisecond(), newValue);
    }
    
    /**
     * addOrUpdates a new value to the chart.
     * @param newValue double chart value
     */
    public void addOrUpdateDiastolicBloodpressureValueToChart(double newValue){
    	this.datasets[2].getSeries(0).addOrUpdate(new Millisecond(), newValue);
    }
    
    /**
     * addOrUpdates a new value to the chart.
     * @param newValue double chart value
     */
    public void addOrUpdateTemperatureValueToChart(double newValue){
    	this.datasets[3].getSeries(0).addOrUpdate(new Millisecond(), newValue);
    }
    
    /**
     * addOrUpdates a new value to the chart.
     * @param newValue double chart value
     */
    public void addOrUpdateOxygenSaturationValueToChart(double newValue){
    	this.datasets[4].getSeries(0).addOrUpdate(new Millisecond(), newValue);
    }
    
    /**
     * addOrUpdates a new value to the chart.
     * @param newValue double chart value
     */
    public void addOrUpdateSystolicBloodpressureValueToChart(double newValue){
    	this.datasets[2].getSeries(1).addOrUpdate(new Millisecond(), newValue);
    }



}

Thank you a lot for your HELP. If there is a post already that solves this problem please post I just havent found it then.

Cheers

Chris

gizza
Posts: 7
Joined: Tue Oct 14, 2008 8:25 am

setNotify(false) and vise versa

Post by gizza » Thu Oct 23, 2008 1:43 am

HI

i want to add that I am using the whole thing in full-screen.

BTW: I found this enty: http://www.jfree.org/phpBB2/viewtopic.p ... light=slow
Are you adding multiple values to the time series chart? Each call to addOrUpdate() will trigger a DatasetChangeEvent that will, in turn, trigger a chart repaint. If you are adding multiple values, switch off chart event notification first (chart.setNotify(false)) then switch it back on when you're done updating the dataset (chart.setNotify(true) - this call will trigger the chart repaint that will show all the new values in your dataset).

Aside from that, each time a chart is updated the whole chart is repainted...so it is slower than it could be if JFreeChart had been designed with dynamic charts specifically in mind. Performance will be affected by lots of factors, including the dimensions of the chart(s), which version of the JRE you use (JRE 6.0 seemed to improve Java2D performance significantly), your hardware, whether or not you have antialiasing switched on for the charts, and lots of other things.
_________________
Dave Gilbert
JFreeChart Project Leader
And i implemented the chart.setNotify(false) before adding the value and vise versa after adding them.

There was unfortunatley no signifcant increase in performance. :(

:arrow: Any ideas? Thank you!

Cheers

Chris

RichardWest
Posts: 844
Joined: Fri Oct 13, 2006 9:29 pm
Location: Sunnyvale, CA

Post by RichardWest » Thu Oct 23, 2008 2:10 am

Did you take a look at Slowdown in JFreeChart display? The SamplingXYLineRenderer that David wrote will probably improve performance. My only concern would be a degradation in quality due to the spikey nature of vital signs, but you will experience this problem regardless when the number of data points exceeds the number of pixels.
Richard West
Design Engineer II
Advanced Micro Devices
Sunnyvale, CA

david.gilbert
JFreeChart Project Leader
Posts: 11734
Joined: Fri Mar 14, 2003 10:29 am
antibot: No, of course not.
Contact:

Post by david.gilbert » Thu Oct 23, 2008 9:43 am

By default, the TimeSeries class will retain all the data you add to it...so over time, your dataset will become very large, and it will take longer to compute the axis bounds etc. If you want to automatically discard older data from the time series (if you know it isn't needed anymore) you can use one of the methods:

Code: Select all

public void setMaximumItemCount(int maximum);
public void setMaximumItemAge(long periods);
The SamplingXYLineRenderer is also likely to help - I tried to ensure that it doesn't omit any spikes in the data (give it a try, feedback is welcome).

I am also working on fixing the axis bounds calculations so that they only look at the range of visible data on the x-axis...rather than the entire dataset. When that fix goes in, you will also see an improvement.
David Gilbert
JFreeChart Project Leader

:idea: Read my blog
:idea: Support JFree via the Github sponsorship program

Locked