JavaFX LineChart Performance

I am trying to improve the performance of LineChart in JavaFX, but without much success. I also found that this was apparently a common problem that some programmers discovered when trying to display big data (here a lot of data is 10,000). For example, such data is quite common in science and technology, and it would be great if we could figure out how to speed up the work of LineChart in JavaFX.

Well, I found two entries here in stackoverflow with a similar question. Performance problem with JavaFX LineChart with 65000 data points and JavaFX LineChart - draw an array . Topic The performance problem with JavaFX LineChart with 65,000 data points gives a proposal (from Adam) to use the Ramer-Douglas-Puker algorithm ! to reduce the number of data points in LineChart for acceleration.

However, in scientific and technical data we usually need to see the shape of the plot, and then zoom in to see the details in certain parts of the plot. Therefore, if we use the Ramer-Douglas-Puker algorithm, we will need to redraw the LineChart each time the user zooms in / out, which I think will cost a lot of processing.

So I would like to know if anyone has any tips on speeding LineChart in JavaFX. Here is a sample code containing what I have learned so far.

import java.util.ArrayList; import java.util.List; import javafx.application.Application; import javafx.scene.Scene; import javafx.scene.chart.LineChart; import javafx.scene.chart.NumberAxis; import javafx.scene.chart.XYChart; import javafx.scene.layout.StackPane; import javafx.stage.Stage; public class TestingLineChart extends Application { @Override public void start(Stage primaryStage) { long startTime, endTime; startTime = System.nanoTime(); StackPane root = new StackPane(); NumberAxis xAxis = new NumberAxis(); NumberAxis yAxis = new NumberAxis(); LineChart<Number, Number> lineChartPlot = new LineChart<>(xAxis, yAxis); // set them false to make the plot faster lineChartPlot.setAnimated(false); lineChartPlot.setCreateSymbols(false); List<XYChart.Data<Double, Double>> data = new ArrayList<>(); Scene scene = new Scene(root, 300, 250); endTime = System.nanoTime(); System.out.println("Time (ms) for creation: " + (endTime - startTime)/1e6); startTime = System.nanoTime(); for (int n = 0; n < 1e5; n++) { data.add(new XYChart.Data(n, Math.random())); } endTime = System.nanoTime(); System.out.println("Time (ms) for adding data: " + (endTime - startTime)/1e6); startTime = System.nanoTime(); XYChart.Series dataSeries = new XYChart.Series<>(); dataSeries.setName("data"); // taking the data dataSeries.getData().addAll(data); // taking the data endTime = System.nanoTime(); System.out.println("Time (ms) for adding data to series: " + (endTime - startTime)/1e6); startTime = System.nanoTime(); lineChartPlot.getData().add(dataSeries); endTime = System.nanoTime(); System.out.println("Time (ms) for adding data to LineChart: " + (endTime - startTime)/1e6); startTime = System.nanoTime(); root.getChildren().add(lineChartPlot); endTime = System.nanoTime(); System.out.println("Time (ms) for adding LineChart StackPane: " + (endTime - startTime)/1e6); startTime = System.nanoTime(); primaryStage.setTitle("Hello World!"); primaryStage.setScene(scene); primaryStage.show(); endTime = System.nanoTime(); System.out.println("Time (ms) for showing: " + (endTime - startTime)/1e6); } /** * @param args the command line arguments */ public static void main(String[] args) { launch(args); } } 

As you can see if you are running this code, the greatest cost is in rendering, which I could not capture with these timings. Then, in my opinion, the improvement should be concentrated there, but I do not know how to do it.

Thanks.

+11
java performance javafx javafx-8
source share
4 answers

I also use JavaFX charts for a scientific application with tens of thousands of data points and was able to achieve real-time update and graphing speeds. There are two main things you need to do.

First, I think the Ramer-Douglas-Pecker algorithm is unreasonably complicated. Assuming you are working with a nice simple continuous function, it’s much easier to notice that we only have a limited screen resolution and we don’t need more than three or four data points for each pixel in the domain to convey as much information as possible. as much as possible. For example, one for each first and last data point in a pixel, and one for maximum and minimum in a pixel.

There are several options for this strategy that you can try on, but the main idea is just a good, fast, one-pass downsample, and it should be virtually lossless. (Or, more precisely, this should not add additional losses when presented on top of losses during rasterization.) This also limits the number of points to something manageable, and, in my experience, is fast enough to redraw when scaling or to update data in real time. However, this can cause problems if you use screen scaling for HiDPI or otherwise scale the components of the graph.

The second part is also important: even if you set CSS so as not to draw data point shapes, it still takes an inexplicably long time to add nodes to the scene. To solve this problem, it seems that a LineChart subclass of LineChart and overriding the dataItemAdded method dataItemAdded that it is not used if you do not want to draw shapes. You should also reuse data points that are already added to the series where possible, and not add new ones, i.e. series.getData().get(i).setXValue(...) Prefer series.getData().get(i).setXValue(...) and series.getData().get(i).setYValue(...) in series.setData(...) or series.getData().add(...) .

+10
source share

Hope this comment is not in vain or comes too late:

Some performance limitations are an integral part of JavaFX implementation: i.e. many operations are computed in the JVM, rather than being transferred to the underlying OpenGL-based HW, data points are overly large Nodes drawn in a graph scene, not a reduction of data on the fly and using Canvas ... and many others. Unfortunately, we found that many of these problems (and errors) cannot be resolved without large workarounds (e.g. final API methods) around the original JavaFX Chart API.

So we developed / redesigned (for our own application), open source and - in the hope that others would find it useful or would like to contribute - we published our JavaFX-based diagram library on GitHub:

https://github.com/GSI-CS-CO/chart-fx

The main objective: real-time performance optimization for data visualization with a refresh rate of 25 Hz for data sets with several from 10 thousand to 5 million data points common in digital signal processing applications. Performance graphs, examples, and documentation are available on GitHub:

https://github.com/GSI-CS-CO/chart-fx/raw/master/docs/pics/chartfx-example1.png https://github.com/GSI-CS-CO/chart-fx/raw /master/docs/pics/chartfx-performance1a.png https://github.com/GSI-CS-CO/chart-fx/raw/master/docs/pics/chartfx-performance1.png

Motivations for which a new JavaFX library was needed and performance comparison with other Java and C ++ / Qt libraries were presented at IPAC'19: https://ipac2019.vrws.de/papers/thprb028.pdf

NB this is my first post so there are no inline images

+1
source share

What improves the performance of charts with many data points is to remove the "shape" of the data points.

Over css, for example:

 .chart-line-symbol { -fx-shape: ""; } 

or make sure that it is not displayed at all:

 .chart-line-symbol { -fx-background-insets: 0,0; -fx-background-radius: 0px; -fx-padding: 0px; -fx-shape: ""; -fx-background-color: transparent; } 

Another approach is to remove some data points from your chart. So, for example, use only 3. datapoint or calculate the average number of data points.

0
source share

After 3 years, this is not the final solution, as there are problems with the performance of big data, but it helps a lot (of course, the schedule will be easier with this):

CSS (you can also play a little with other elements in CSS)

 .chart-vertical-grid-lines, .chart-horizontal-grid-lines { -fx-stroke: transparent; } 

Controller (this part takes more time)

 lineChart.setCreateSymbols(false); //this part is the one that consumes more time lineChart.setAnimated(false); 

Note: this problem is the same for any chart with lots of data.

0
source share

All Articles