Stefan S.
Stefan S.

Reputation: 4103

GUI Takes Longer Than Calculation And Slows Entire Process

We have a complex calculation that takes a variable time. With some input values thousand steps can be done in one second - with other input values a step takes several seconds.

That's completely correct, so we just want to inform the user about the progress. The problem is that in the former case updating the GUI takes longer than the actual calculation, so after it is done there are still about 10 seconds of GUI update events in the queue (which in that case triples the execution time of the entire calculation).

I think that's a general problem, so I broke it down to a somewhat framework agnostic example:

public class QueueTest {

    static final int STEPS = 30;

    public static void main(String[] args) {
        final Gui gui = // ...

        final Display display = Display.getDefault();
        final Thread thread = new Thread(() -> {
            for (int i = 0; i < STEPS; i++) {
                final int step = i; // calculate something etc.
                gui.updateLater(display, step);
            }
            System.out.println("Finished calculation.");
        });
        thread.start();

        while (true) {
            if (!display.readAndDispatch()) {
                display.sleep();
            }
        }
    }

    interface Gui {

        default void updateLater(Display display, int step) {
            display.asyncExec(() -> update(step));
        }

        default void update(int step) {
            System.out.println("Update " + (step + 1) + " / " + STEPS);
            if (step == STEPS - 1) {
                System.out.println("Finished GUI.");
            }
        }
    }
}

(The additional Thread only "calculates" steps and sends it to the GUI to show the progress.)

So let's consider some implementations of Gui:

static class NoGui implements Gui {

    @Override
    public void update(int step) {
        if (step == STEPS - 1) {
            System.out.println("Finished GUI.");
        }
    }
}

This example prints only when the GUI is finished. The result is these two lines, printed at almost the same time:

Finished calculation.
Finished GUI.

That's perfectly reasonable. The GUI events are quick to be finished. Now let's make them slow:

static class SlowGui implements Gui {

    @Override
    public void update(int step) {
        try {
            Thread.sleep(100);
            Gui.super.update(step);
        } catch (final InterruptedException e) {
            e.printStackTrace();
        }
    }
}

This prints something like the following, with the finishing of calculation and the GUI three seconds apart:

Finished calculation.
Update 1 / 30
Update 2 / 30
Update 3 / 30
...
Update 30 / 30
Finished GUI.

That's what I'm seeing in our application. The calculation finishes, but the GUI is too slow and has to execute its event queue after the calculation is already done.

I want to optimize this behavior and came up with something like this:

static class IgnorantGui extends SlowGui {

    private boolean inProgress;
    private Integer nextStep;

    @Override
    public void updateLater(Display display, int step) {
        if (this.inProgress) {
            this.nextStep = Integer.valueOf(step);
        } else {
            this.inProgress = true;
            super.updateLater(display, step);
        }
    }

    @Override
    public void update(int step) {
        try {
            Integer currentStep = Integer.valueOf(step);
            do {
                super.update(currentStep.intValue());
                currentStep = this.nextStep;
                this.nextStep = null;
            } while (currentStep != null);
        } finally {
            this.inProgress = false;
        }
    }
}

The output is the following four lines:

Finished calculation.
Update 1 / 30
Update 30 / 30
Finished GUI.

This implementation just ignores the events in between and so is a lot quicker. This is a valid solution to my problem.

I think this entire use case might be common, and maybe there is a more elegant solution. Or even some standard Java API to handle it. (Maybe some SWT / Eclipse Framework API, since that's what we're using.)

So... how to handle a GUI that takes longer to update than the calculation and so slows the application down?

Upvotes: 0

Views: 56

Answers (2)

greg-449
greg-449

Reputation: 111141

One way that I use is to poll the background thread using a timer runnable in the UI thread. Use Display.timerExec for this:

display.timerExec(100, new Runnable() {
  @Override
  public void run() {

     // TODO update UI from background thread details

     // Run again
     display.timerExec(100, this);
  }
});

The background thread doesn't do any asyncExec calls it just maintains data that the UI thread can access.

Upvotes: 2

Cryptor
Cryptor

Reputation: 43

don't know if I get it right but it seems that you are continuously updating the GUI. Try to add something like a counter that determines when the gui should be updated. Or if it isnt needed to see all steps try something like

static class SlowGui implements Gui {

@Override
public void update(int step) {
    try {
        if(step%5==0){
            Gui.super.update(step);
        }
    } catch (final InterruptedException e) {
        e.printStackTrace();
    }
}

}

this should update every 5 steps.

And why is there a sleep in the update method?

Hope I could help you.

Upvotes: 0

Related Questions