001    package jigcell.compare.tests;
002    
003    import java.awt.EventQueue;
004    import java.util.List;
005    import jigcell.compare.IDataGenerator;
006    import jigcell.compare.IEditableDataElement;
007    import jigcell.compare.IReadableDataSource;
008    import jigcell.compare.ITypeChecker;
009    import jigcell.compare.data.EditableDataGenerator;
010    import jigcell.compare.data.PackedDoubleDataElement;
011    import jigcell.compare.data.PackedTreeDataElement;
012    import jigcell.compare.impl.Compare;
013    import jigcell.compare.impl.ProxyBuilder;
014    import jigcell.compare.ui.CompareFrontEnd;
015    import jigcell.compare.ui.IDataEditorTab;
016    
017    /**
018     * Supports running performance tests in the Comparator.  A benchmark is a state machine which supports timing how long a state takes to run. To
019     * run a benchmark, start a thread using the benchmark runnable.  This puts the benchmark into state 0 which is run off of the event thread,
020     * and calls the runOffEventThread method.  All other states are run on the event thread in the runOnEventThread method.  Change to a new state
021     * by calling the runState method.  To change to a new state and time how long it takes to run, call the timeRunState method.
022     *
023     * <p>
024     * This code is licensed under the DARPA BioCOMP Open Source License.  See LICENSE for more details.
025     * </p>
026     *
027     * @author Nicholas Allen
028     */
029    
030    public abstract class Benchmark {
031    
032       /**
033        * Comparator to abuse
034        */
035    
036       protected Compare compare;
037    
038       /**
039        * Runnable instance to start benchmarking
040        */
041    
042       protected final Runnable benchmarkRunnable = ProxyBuilder.proxyRunnable (this, "startBenchmark");
043    
044       /**
045        * Current state of the tester
046        */
047    
048       private int state;
049    
050       /**
051        * Current run execution time in seconds.
052        */
053    
054       protected static double currentTime () {
055          return System.currentTimeMillis () / 1000.0;
056       }
057    
058       /**
059        * Attempts to bring the VM to a controlled state.
060        */
061    
062       protected static void reset () {
063          System.gc ();
064          try {
065             Thread.sleep (50);
066          } catch (Exception e) {
067             Compare.throwUncheckedException (e);
068          }
069          System.gc ();
070       }
071    
072       /**
073        * Creates a new performance tester.
074        *
075        * @param compare Comparator to abuse
076        */
077    
078       protected Benchmark (Compare compare) {
079          this.compare = compare;
080       }
081    
082       /**
083        * Creates a data generator that represents a benchmark result.
084        *
085        * @param name Generator name
086        * @param description Generator description
087        * @param type Generator type
088        * @param results Timing results
089        */
090    
091       protected IDataGenerator createBenchmarkReport (String name, String description, String type, List results) {
092          IEditableDataElement element = new PackedTreeDataElement ();
093          EditableDataGenerator generator = new EditableDataGenerator (element);
094          generator.setName (name);
095          generator.setComment (description);
096          generator.setAttribute (ITypeChecker.ATTRIBUTE_TYPE, type);
097          for (int i = 0, l = results.size (); i < l; i++) {
098             double result [] = (double []) results.get (i);
099             IEditableDataElement _element = new PackedDoubleDataElement ();
100             element.setValue (i + 1, _element);
101             for (int _i = 0, _l = result.length; _i < _l; _i++)
102                _element.setValue (_i + 1, result [_i]);
103          }
104          return generator;
105       }
106    
107       /**
108        * Loads data in a view.
109        *
110        * @param view View
111        * @param source Data source
112        */
113    
114       protected void loadInView (IDataEditorTab view, IReadableDataSource source) {
115          if (compare instanceof CompareFrontEnd)
116             ((CompareFrontEnd) compare).selectTab (view);
117          view.loadDirect (source);
118       }
119    
120       /**
121        * Performs the tester computations that do not interact with the interface.  This method will be called at the start of the test.
122        */
123    
124       protected abstract void runOffEventThread () throws Exception;
125    
126       /**
127        * Performs an operation that interacts with the interface.  After the operation completes, the thread needs to notify the benchmark.
128        *
129        * @param state Indicates which interface operation is next
130        */
131    
132       protected abstract void runOnEventThread (int state) throws Exception;
133    
134       /**
135        * Starts an operation that will interact with the interface.  This method will invoke runOnEventThread with the same argument.
136        *
137        * @param state Some state flag
138        */
139    
140       protected final void runState (int state) throws Exception {
141          assert state > 0;
142          this.state = state;
143          EventQueue.invokeAndWait (benchmarkRunnable);
144       }
145    
146       /**
147        * Executes an operation that interacts with the interface and returns how long it took to complete.
148        *
149        * @param state State flag
150        */
151    
152       protected double timeRunState (int state) throws Exception {
153          assert state > 0;
154          double startTime = currentTime ();
155          this.state = state;
156          EventQueue.invokeLater (benchmarkRunnable);
157          synchronized (this) {
158             wait ();
159          }
160          return currentTime () - startTime - 0.05;
161       }
162    
163       /**
164        * Starts running the benchmark.
165        */
166    
167       private void startBenchmark () {
168          try {
169             if (state == 0)
170                runOffEventThread ();
171             else {
172                try {
173                   Thread.sleep (50);
174                } catch (InterruptedException e) {
175                   throw new RuntimeException ("Unable to pause benchmark for synchronization.");
176                }
177                runOnEventThread (state);
178             }
179          } catch (Exception e) {
180             Compare.throwUncheckedException (e);
181          }
182       }
183    }