0

I am developing a trading app with a GUI and charting modules using JavaFX that has to be run on a server to be close to trading centers. While my app runs very smoothly on my home PC it is unresponsive on any dedicated server or VPS I have tried so far.

The trading app is too big to share the code here, so I made a small test app that allows to quantify the difference. It is showing the frame rate (thanks to What is the preferred way of getting the frame rate of a JavaFX application?) and if you click the button it simulates some GUI work.

The dedicated server specs are:

AMD Ryzen 5 3600X 6-Core Processor 3.80 GHz 16.0 GB RAM 64-bit Operating System, x64-based processor Windows Server 2016 Standard

My home PC specs are:

AMD Ryzen 7 5700U with Radeon Graphics 1.80 GHz 16.0 GB RAM 64-bit Operating System, x64-based processor Windows 10 Home

Running the test on my home PC:

Frame rate after starting: 60 per second. Frame rate when simulation GUI work: 60 per second.

Running the test on the dedicated server:

Frame rate after starting: 52 per second. Frame rate when simulation GUI work: 40 per second.

When doing the test with the app started simultaneously five times it pushes the frame rate down towards 30 frames per second on the server, while on the PC it stays at 60 per second.

Running the app with -Dprism.verbose=true shows that on the server the Microsoft Basic Render Driver is used what might cause the difference in performance:

enter image description here

What can I do to improve the performance of JavaFX on the server or if possible to make it run as smoothly as it is running on the home PC?

Edit: I figured out that running the app with -Dprism.order=sw as VM option enhances the performance on the server significantly. The test app is there now running at same FPS as on local PC.

Here is the code of the test app:

import javafx.animation.AnimationTimer;
import javafx.application.Application;
import javafx.application.Platform;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.Label;
import javafx.scene.layout.BorderPane;
import javafx.stage.Stage;

public class SimpleFrameRateMeter extends Application {

private final long[] frameTimes = new long[100];
private int frameTimeIndex = 0;
private boolean arrayFilled = false;

@Override
public void start(Stage primaryStage) {

    borderPane = new BorderPane();

    runFrameRateMeter();

    //add a button to start some GUI action
    button = new Button("Start");

    button.setOnAction(e -> {
        if (button.getText().equals("Start")) {
            button.setText("Running...");
            doSomething();
        }
    });

    borderPane.setLeft(button);

    primaryStage.setScene(new Scene(borderPane, 250, 150));
    primaryStage.show();
}

private BorderPane borderPane;
private Button button;

private Label label = new Label();

//some GUI work
private void doSomething() {

    new Thread(() -> {

        for (int i = 0; i < 5; i++) {

            final int cnt = i;

            Platform.runLater(() -> {
                //Label label = new Label(String.valueOf(cnt));
                label.setText(String.valueOf(cnt));
                borderPane.setCenter(label);
            });

            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }

        Platform.runLater(() -> {
            button.setText("Start");
        });

    }).start();
}

//Measure frame rates from https://stackoverflow.com/questions/28287398/what-is-the-preferred-way-of-getting-the-frame-rate-of-a-javafx-application

private void runFrameRateMeter() {

    Label label = new Label();
    borderPane.setTop(label);

    AnimationTimer frameRateMeter = new AnimationTimer() {

        @Override
        public void handle(long now) {
            long oldFrameTime = frameTimes[frameTimeIndex];
            frameTimes[frameTimeIndex] = now;
            frameTimeIndex = (frameTimeIndex + 1) % frameTimes.length;
            if (frameTimeIndex == 0) {
                arrayFilled = true;
            }
            if (arrayFilled) {
                long elapsedNanos = now - oldFrameTime;
                long elapsedNanosPerFrame = elapsedNanos / frameTimes.length;
                double frameRate = 1_000_000_000.0 / elapsedNanosPerFrame;

                    label.setText(String.format("Current frame rate: %.3f", frameRate));

            }
        }
    };
    frameRateMeter.start();
}

public static void main(String[] args) {
    launch(args);
}

}

JTfx
  • 43
  • 4
  • 2
    It's hard to know if this is the same issue as your real application, but the code you posted creates and discards a lot of redundant UI elements (`Label`s) that will never actually be displayed. The target frame rate for JavaFX is 60fps; if you create a new `Label` every millisecond, only about 1 in 60 will ever have a chance of being realized to the screen. UI components are quite heavyweight objects, so you're forcing the garbage collector to do a lot of work here. You're also flooding the FX Application Thread with a large number of tasks. – James_D Apr 08 '22 at 13:05
  • 2
    If you *really* need a busy background thread that is trying to ensure maximum liveness, have the background thread create or update lightweight "data objects" only. To ensure you only perform UI work that is really necessary, use an `AnimationTimer` that grabs the latest copy of the data (with appropriate synchronization), and updates the UI to represent it. Wherever possible, update existing UI controls, instead of creating new ones. – James_D Apr 08 '22 at 13:10
  • Thx James_D for your quick answer. I changed the code. It reuses now the label and loops only 1 time per second instead of 1000. But the frame rate is still as low as before on the server. – JTfx Apr 08 '22 at 14:42
  • That's weird. Your latest code is hardly doing anything intensive.. This probably makes no difference, but why are you using `Platform.runLater(...)` inside the `AnimationTimer`? – James_D Apr 08 '22 at 14:47
  • Right, Platform.runLater(...) inside the AnimationTimer is not necessary so I removed it. Is this low frame rate maybe somehow related to the Microsoft Basic Render Driver? – JTfx Apr 08 '22 at 15:00
  • I don't know; the only platforms I have are ones on which all this runs fine. Do you even need the background thread to show low frame rates? That thread is barely doing anything. – James_D Apr 08 '22 at 18:11

0 Answers0