JavaFX LineChart Performance

2022-09-04 01:19:15

我一直在努力提高JavaFX中LineChart的性能,但没有取得很大的成功。我还发现,这似乎是一些程序员在尝试显示大数据时发现的常见问题(这里的大代表>10,000的数据大小)。例如,这种数据在科学和工程中非常普遍,如果我们能弄清楚如何在JavaFX中加速LineChart,那就太好了。

好吧,我在stackoverflow中发现了两个类似的问题,JavaFX LineChart有65000个数据点JavaFX LineChart - draw array的性能问题。主题 JavaFX LineChart 的性能问题与 65000 个数据点最终提出了一个建议(由 Adam 提出),即使用 Ramer–Douglas–Peucker 算法!以减少进入线图的数据点数,以加快速度。

然而,在科学和工程数据中,我们通常需要看到绘图形状,然后放大才能看到绘图特定部分的细节。因此,如果我们使用Ramer-Douglas-Peucker算法,每次用户放大/缩小时,我们都需要重新绘制LineChart,我认为这将花费大量的处理成本。

因此,我想知道是否有人在JavaFX中加速LineChart方面有一些提示。下面是一个示例代码,其中包含我到目前为止所学到的内容。

    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);
}
}

正如您所看到的,如果您运行此代码,最大的成本是渲染,我无法使用这些计时捕获。然后,在我看来,改进应该集中在那里,但我不知道如何。

谢谢。


答案 1

我还将JavaFX图表用于具有数十个数据点的科学应用程序,并且已经能够实现更新和绘制图形的实时速度。您需要做两件主要的事情。

首先,我认为Ramer-Douglas-Peucker算法是不必要的复杂。假设您正在使用一个很好的简单连续函数,那么很容易观察到我们只有有限的显示分辨率,并且我们不需要域中每个像素最多三到四个数据点来传达尽可能多的信息。例如,对于像素内出现的第一个和最后一个数据点,每个一个,对于像素内的最大值和最小值,各一个。

您可以尝试使用此策略的一些变体,但基本思想只是一个不错的快速单通道下采样,它基本上应该是无损的。(或者更准确地说,除了光栅化之外,它应该不会增加额外的代表性损失。它还将点数限制为可管理的内容,并且根据我的经验,它足够快,可以在缩放或实时数据更新时重绘。但是,如果您具有 HiDPI 的显示缩放或出于任何原因以其他方式缩放图形组件,则可能会导致问题。

第二部分同样重要:即使您将css设置为不绘制数据点形状,仍然需要莫名其妙的很长时间才能将节点添加到场景中。为了解决这个问题,在您不想绘制形状的情况下,将方法子类化并重写为no-op似乎就足够了。您还应该尽可能重用已添加到序列中的数据点,而不是添加新的数据点,即首选和或。LineChartdataItemAddedseries.getData().get(i).setXValue(...)series.getData().get(i).setYValue(...)series.setData(...)series.getData().add(...)


答案 2

希望这个评论不是徒劳的,或者来得太晚:

一些性能限制是JavaFX实现固有的:即。许多操作在JVM中计算,而不是被推送到底层的基于OpenGL的硬件,在场景图中绘制的数据点过大,而不是动态数据缩减和使用...仅举几例。不幸的是,我们发现,如果没有围绕原始JavaFX API的更大的解决方法(例如API方法),许多这些问题(和错误)就无法解决。NodesCanvasfinalChart

因此,我们开发/重新设计(用于我们的内部应用程序),开源,并希望其他人发现它有用或想要做出贡献 - 在GitHub上发布了我们基于JavaFX的图表库:

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

其主要重点:以 25 Hz 的更新速率对数字信号处理应用中常见的几万到 500 万个数据点的数据集进行性能优化的实时数据可视化。性能图、示例和文档可在 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

为什么需要一个新的JavaFX库,以及其他基于Java和C++ / Qt的库的性能比较已经在IPAC'19上进行了介绍:https://ipac2019.vrws.de/papers/thprb028.pdf

注意:这是我的第一篇文章,因此没有内联图像


推荐