Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Java + Swing: writing code to coalesce change events

I have this data flow, roughly:

DataGenerator -> DataFormatter -> UI

DataGenerator is something that generates data rapidly; DataFormatter is something that formats it for display purposes; and the UI is just a bunch of Swing elements.

I'd like to make my DataGenerator something like this:

class DataGenerator
{
   final private PropertyChangeSupport pcs;
   ...
   public void addPropertyChangeListener(PropertyChangeListener pcl) {
     this.pcs.addPropertyChangeListener(pcl); 
   }
   public void removePropertyChangeListener(PropertyChangeListener pcl) {
     this.pcs.removePropertyChangeListener(pcl);
   }
}

and just call this.pcs.firePropertyChange(...) whenever my data generator has new data; then I can just do dataGenerator.addPropertyListener(listener) where listener is responsible for pushing the change forward to the DataFormatter and then to the UI.

The problem with this approach, is that there are thousands of dataGenerator changes per second (between 10,000 and 60,000 per second depending on my situation), and the computational cost of formatting it for the UI is high enough that it puts an unnecessary load on my CPU; really all I care about visually is at most 10-20 changes per second.

Is there any way to use a similar approach, but coalesce the change events before they get to the DataFormatter? If I receive multiple update events on a single topic, I just care about displaying the latest one, and can skip all the previous ones.

like image 966
Jason S Avatar asked Feb 08 '12 21:02

Jason S


2 Answers

Two ideas:

  • Aggregate PropertyChangeEvents. Extend PropertyChangeSupport, overwrite public void firePropertyChange(PropertyChangeEvent evt), fire only if last event was fired more than 50ms (or whatever time seems appropriate) ago. (In fact you should overwrite every fire* method or at least the one you use in your scenario to prevent the creation of the PropertyChangeEvent.)
  • Drop the whole event based approached. 60.000 events per second is a quite high number. In this situation I would poll. It is a conceptual change to MVP where the presenter knows if it is in active state and should poll. With this approach you don't generate thousands of useless events; you can even present the highest possible frames per second, no matter how much data there is. Or you can set the presenter to a fixed rate, let it sleep between refreshes for a certain time, or let it adapt to other circumstances (like CPU load).

I would tend to the second approach.

like image 126
Hauke Ingmar Schmidt Avatar answered Sep 21 '22 12:09

Hauke Ingmar Schmidt


It sounds like your DataGenerator doing a lot of non-GUI work on the EDT-thread. I would recommend that your DataGenerator extend SwingWorker and by that doing the work in a background thread, implemented in doInBackground. The SwingWorker could then publish intermediate results to the GUI, while you have a process method that receives the last few published chunks on the EDT and update your GUI.

The SwingWorkers process method does coalesce the published chunks, so it will not run once for every published intermediate result.

If you only care of the last result on the EDT you can use this code, that only cares about the last chunk in the list:

 @Override
 protected void process(List<Integer> chunks) {

     // get the *last* chunk, skip the others
     doSomethingWith( chunks.get(chunks.size() - 1) );
 }

Read more on SwingWorker: Tasks that Have Interim Results.

like image 31
Jonas Avatar answered Sep 22 '22 12:09

Jonas