Coverage Report - org.perfidix.Benchmark
 
Classes in this File Line Coverage Branch Coverage Complexity
Benchmark
90%
108/120
88%
46/52
3.909
 
 1  
 /**
 2  
  * Copyright (c) 2012, University of Konstanz, Distributed Systems Group
 3  
  * All rights reserved.
 4  
  * 
 5  
  * Redistribution and use in source and binary forms, with or without
 6  
  * modification, are permitted provided that the following conditions are met:
 7  
  * * Redistributions of source code must retain the above copyright
 8  
  * notice, this list of conditions and the following disclaimer.
 9  
  * * Redistributions in binary form must reproduce the above copyright
 10  
  * notice, this list of conditions and the following disclaimer in the
 11  
  * documentation and/or other materials provided with the distribution.
 12  
  * * Neither the name of the University of Konstanz nor the
 13  
  * names of its contributors may be used to endorse or promote products
 14  
  * derived from this software without specific prior written permission.
 15  
  * 
 16  
  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
 17  
  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 18  
  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 19  
  * DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY
 20  
  * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 21  
  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 22  
  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 23  
  * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 24  
  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 25  
  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 26  
  */
 27  
 package org.perfidix;
 28  
 
 29  
 import java.lang.reflect.Method;
 30  
 import java.util.ArrayList;
 31  
 import java.util.HashMap;
 32  
 import java.util.Hashtable;
 33  
 import java.util.LinkedHashSet;
 34  
 import java.util.List;
 35  
 import java.util.Map;
 36  
 import java.util.Random;
 37  
 import java.util.Set;
 38  
 
 39  
 import org.perfidix.AbstractConfig.StandardConfig;
 40  
 import org.perfidix.annotation.AfterBenchClass;
 41  
 import org.perfidix.annotation.BeforeBenchClass;
 42  
 import org.perfidix.annotation.Bench;
 43  
 import org.perfidix.element.AbstractMethodArrangement;
 44  
 import org.perfidix.element.BenchmarkElement;
 45  
 import org.perfidix.element.BenchmarkExecutor;
 46  
 import org.perfidix.element.BenchmarkMethod;
 47  
 import org.perfidix.exceptions.PerfidixMethodCheckException;
 48  
 import org.perfidix.exceptions.PerfidixMethodInvocationException;
 49  
 import org.perfidix.meter.AbstractMeter;
 50  
 import org.perfidix.result.BenchmarkResult;
 51  
 
 52  
 /**
 53  
  * Class to hold all classes which want to be benchmarked.
 54  
  * <p>
 55  
  * 
 56  
  * <pre>
 57  
  * 
 58  
  * 
 59  
  * public class MyBenchmark {
 60  
  * 
 61  
  *     public static void main(final String[] args) {
 62  
  *         // Build up own config
 63  
  *         final AbstractConfig config = new MyConfig();
 64  
  * 
 65  
  *         // Initialise benchmark with config
 66  
  *         final Benchmark benchmark = new Benchmark(config);
 67  
  * 
 68  
  *         // Adding Classes to benchmark
 69  
  *         benchmark.add(Class1.class);
 70  
  *         benchmark.add(Class2.class);
 71  
  *         benchmark.add(Object3); // of Class3, every class is allowed to be inserted only once
 72  
  * 
 73  
  *         // Running benchmark
 74  
  *         final BenchmarkResult result = benchmark.run();
 75  
  * 
 76  
  *         // Printing out Result in a Tabular, every AbstractOutput implementing class is allowed.
 77  
  *         new TabularSummaryOutput().visitBenchmark(result);
 78  
  * 
 79  
  *     }
 80  
  * }
 81  
  * 
 82  
  * </pre>
 83  
  * 
 84  
  * </p>
 85  
  * 
 86  
  * @see AbstractConfig
 87  
  * @see BenchmarkResult
 88  
  * @author Sebastian Graf, University of Konstanz
 89  
  */
 90  
 public final class Benchmark {
 91  
 
 92  
     /** Set with all used classes. */
 93  
     private transient final Set<Class<?>> clazzes;
 94  
 
 95  
     /** Already instantiated objects */
 96  
     private transient final Set<Object> objects;
 97  
 
 98  
     /** Simple random for gc-prob */
 99  5
     private transient static final Random RAN = new Random();
 100  
 
 101  
     /** Configuration of benchmark, holding everything. */
 102  
     private transient final AbstractConfig conf;
 103  
 
 104  
     /**
 105  
      * Constructor with a fixed set of used meters.
 106  
      * 
 107  
      * @param paramConf
 108  
      *            Configuration for Benchmark
 109  
      */
 110  65
     public Benchmark(final AbstractConfig paramConf) {
 111  65
         conf = paramConf;
 112  65
         this.clazzes = new LinkedHashSet<Class<?>>();
 113  65
         this.objects = new LinkedHashSet<Object>();
 114  
 
 115  65
     }
 116  
 
 117  
     /**
 118  
      * Convience constructor using the {@link StandardConfig}
 119  
      */
 120  
     public Benchmark() {
 121  5
         this(new StandardConfig());
 122  5
     }
 123  
 
 124  
     /**
 125  
      * Adding a class to bench to this benchmark. This class should contain
 126  
      * benchmarkable methods, otherwise it will be ignored.
 127  
      * 
 128  
      * @param clazz
 129  
      *            to be added.
 130  
      */
 131  
     public void add(final Class<?> clazz) {
 132  75
         if (this.clazzes.contains(clazz)) {
 133  10
             throw new IllegalArgumentException("Only one class-instance per benchmark allowed");
 134  
         } else {
 135  65
             this.clazzes.add(clazz);
 136  
         }
 137  65
     }
 138  
 
 139  
     /**
 140  
      * Adding a already instantiated objects to benchmark. Per benchmark, only
 141  
      * one objects of each class is allowed.
 142  
      * 
 143  
      * @param obj
 144  
      *            to be added
 145  
      */
 146  
     public void add(final Object obj) {
 147  25
         final Class<?> clazz = obj.getClass();
 148  
 
 149  25
         if (this.clazzes.contains(clazz)) {
 150  5
             throw new IllegalArgumentException("Only one class-instance per benchmark allowed");
 151  
         } else {
 152  20
             this.clazzes.add(clazz);
 153  20
             this.objects.add(obj);
 154  
         }
 155  20
     }
 156  
 
 157  
     /**
 158  
      * Getting the number of all methods and all runs
 159  
      * 
 160  
      * @return a map with all methods and the runs.
 161  
      */
 162  
     public Map<BenchmarkMethod, Integer> getNumberOfMethodsAndRuns() {
 163  15
         final Map<BenchmarkMethod, Integer> returnVal = new HashMap<BenchmarkMethod, Integer>();
 164  15
         final List<BenchmarkMethod> meths = getBenchmarkMethods();
 165  15
         for (final BenchmarkMethod meth : meths) {
 166  80
             int numberOfRuns = BenchmarkMethod.getNumberOfAnnotatedRuns(meth.getMethodToBench());
 167  80
             if (numberOfRuns == Bench.NONE_RUN) {
 168  5
                 numberOfRuns = conf.getRuns();
 169  
             }
 170  80
             returnVal.put(meth, numberOfRuns);
 171  80
         }
 172  15
         return returnVal;
 173  
     }
 174  
 
 175  
     /**
 176  
      * Running this benchmark
 177  
      * 
 178  
      * @return {@link BenchmarkResult} the result in an {@link BenchmarkResult} container.
 179  
      */
 180  
     public BenchmarkResult run() {
 181  40
         final BenchmarkResult res = new BenchmarkResult(conf.getListener());
 182  40
         BenchmarkExecutor.initialize(conf, res);
 183  
 
 184  
         // getting Benchmarkables
 185  40
         final List<BenchmarkElement> elements = getBenchmarkElements();
 186  
 
 187  
         // arranging them
 188  40
         final AbstractMethodArrangement arrangement =
 189  
             AbstractMethodArrangement.getMethodArrangement(elements, conf.getArrangement());
 190  
 
 191  
         // instantiate methods
 192  40
         final Map<Class<?>, Object> instantiatedObj = instantiateMethods(res);
 193  
 
 194  
         // getting the mapping and executing beforemethod
 195  40
         final Map<Class<?>, Object> objectsToExecute = executeBeforeBenchClass(instantiatedObj, res);
 196  
 
 197  
         // executing the bench for the arrangement
 198  40
         for (final BenchmarkElement elem : arrangement) {
 199  
             // invoking gc if possible
 200  3975
             if (RAN.nextDouble() < conf.getGcProb()) {
 201  3975
                 System.gc();
 202  
             }
 203  
 
 204  3975
             final BenchmarkExecutor exec = BenchmarkExecutor.getExecutor(elem);
 205  
 
 206  3975
             final Object obj = objectsToExecute.get(elem.getMeth().getMethodToBench().getDeclaringClass());
 207  
             // check needed because of failed initialization of objects
 208  3975
             if (obj != null) {
 209  3925
                 exec.executeBeforeMethods(obj);
 210  3925
                 exec.executeBench(obj);
 211  3925
                 exec.executeAfterMethods(obj);
 212  
             }
 213  3975
         }
 214  
 
 215  
         // cleaning up methods to benchmark
 216  40
         tearDownObjectsToExecute(objectsToExecute, res);
 217  40
         return res;
 218  
     }
 219  
 
 220  
     /**
 221  
      * Setting up executable objects for all registered classes and executing {@link BeforeBenchClass}
 222  
      * annotated methods. If an {@link Exception} occurs, this failure will be stored in the
 223  
      * {@link BenchmarkResult} and
 224  
      * the class will not be instantiated
 225  
      * 
 226  
      * @param res
 227  
      *            {@link BenchmarkResult} for storing possible failures.
 228  
      * @return a mapping with class->objects for all registered classes-
 229  
      */
 230  
     private Map<Class<?>, Object> instantiateMethods(final BenchmarkResult res) {
 231  
         // datastructure initialization for all objects
 232  40
         final Map<Class<?>, Object> objectsToUse = new Hashtable<Class<?>, Object>();
 233  
 
 234  
         // generating including already instaniated objects
 235  40
         for (final Object obj : this.objects) {
 236  5
             final Class<?> clazz = obj.getClass();
 237  5
             objectsToUse.put(clazz, obj);
 238  5
         }
 239  
 
 240  
         // generating objects for each registered class
 241  40
         for (final Class<?> clazz : clazzes) {
 242  
             // generating a new instance on which the benchmark will be
 243  
             // performed if there isn't a user generated one
 244  50
             if (!objectsToUse.containsKey(clazz)) {
 245  
                 try {
 246  45
                     final Object obj = clazz.newInstance();
 247  45
                     objectsToUse.put(clazz, obj);
 248  
                     // otherwise adding an exception to the result
 249  0
                 } catch (final InstantiationException e) {
 250  0
                     res.addException(new PerfidixMethodInvocationException(e, BeforeBenchClass.class));
 251  0
                 } catch (final IllegalAccessException e) {
 252  0
                     res.addException(new PerfidixMethodInvocationException(e, BeforeBenchClass.class));
 253  45
                 }
 254  
 
 255  
             }
 256  50
         }
 257  
 
 258  40
         return objectsToUse;
 259  
     }
 260  
 
 261  
     /**
 262  
      * Executing beforeBenchClass if present.
 263  
      * 
 264  
      * @param instantiatedObj
 265  
      *            with the instantiatedObj;
 266  
      * @param res
 267  
      *            where the Exceptions should be stored to
 268  
      * @return valid instances with valid beforeCall
 269  
      */
 270  
     private Map<Class<?>, Object> executeBeforeBenchClass(final Map<Class<?>, Object> instantiatedObj,
 271  
         final BenchmarkResult res) {
 272  
 
 273  40
         final Map<Class<?>, Object> returnVal = new Hashtable<Class<?>, Object>();
 274  
 
 275  
         // invoking before bench class
 276  40
         for (final Class<?> clazz : instantiatedObj.keySet()) {
 277  
 
 278  50
             final Object objectToUse = instantiatedObj.get(clazz);
 279  
             // ..the search for the beforeClassMeth begins...
 280  50
             Method beforeClassMeth = null;
 281  50
             boolean continueVal = true;
 282  
             try {
 283  50
                 beforeClassMeth =
 284  
                     BenchmarkMethod.findAndCheckAnyMethodByAnnotation(clazz, BeforeBenchClass.class);
 285  
                 // ... and if this search is throwing an exception, the
 286  
                 // exception will be added and a flag is set to break up
 287  0
             } catch (final PerfidixMethodCheckException e) {
 288  0
                 res.addException(e);
 289  0
                 continueVal = false;
 290  50
             }
 291  
             // if everything worked well...
 292  50
             if (continueVal) {
 293  50
                 if (beforeClassMeth == null) {
 294  
                     // ...either the objects is directly mapped to the class
 295  
                     // for executing the benches
 296  15
                     returnVal.put(clazz, objectToUse);
 297  
                 } else {
 298  
                     // ... or the beforeMethod will be executed and a
 299  
                     // possible exception stored to the result...
 300  35
                     final PerfidixMethodCheckException beforeByCheck =
 301  
                         BenchmarkExecutor.checkMethod(objectToUse, BeforeBenchClass.class, beforeClassMeth);
 302  35
                     if (beforeByCheck == null) {
 303  35
                         final PerfidixMethodInvocationException beforeByInvok =
 304  
                             BenchmarkExecutor.invokeMethod(objectToUse, BeforeBenchClass.class,
 305  
                                 beforeClassMeth);
 306  35
                         if (beforeByInvok == null) {
 307  30
                             returnVal.put(clazz, objectToUse);
 308  
                         } else {
 309  5
                             res.addException(beforeByInvok);
 310  
                         }
 311  35
                     } else {
 312  0
                         res.addException(beforeByCheck);
 313  
                     }
 314  
                 }
 315  
             }
 316  50
         }
 317  40
         return returnVal;
 318  
     }
 319  
 
 320  
     /**
 321  
      * Tear down executable objects for all registered classes and executing {@link AfterBenchClass} annotated
 322  
      * methods.
 323  
      * 
 324  
      * @param objects
 325  
      *            a mapping with class->objects to be teared down
 326  
      * @param res
 327  
      *            the {@link BenchmarkResult} for storing possible failures.
 328  
      */
 329  
     private void tearDownObjectsToExecute(final Map<Class<?>, Object> objects, final BenchmarkResult res) {
 330  
 
 331  
         // executing tearDown for all clazzes registered in given Map
 332  40
         for (final Class<?> clazz : objects.keySet()) {
 333  45
             final Object objectToUse = objects.get(clazz);
 334  45
             if (objectToUse != null) {
 335  
                 // executing AfterClass for all objects.
 336  45
                 Method afterClassMeth = null;
 337  
                 try {
 338  45
                     afterClassMeth =
 339  
                         BenchmarkMethod.findAndCheckAnyMethodByAnnotation(clazz, AfterBenchClass.class);
 340  0
                 } catch (final PerfidixMethodCheckException e) {
 341  0
                     res.addException(e);
 342  45
                 }
 343  
                 // if afterClassMethod exists, the method will be executed and
 344  
                 // possible failures will be stored in the BenchmarkResult
 345  45
                 if (afterClassMeth != null) {
 346  25
                     final PerfidixMethodCheckException afterByCheck =
 347  
                         BenchmarkExecutor.checkMethod(objectToUse, AfterBenchClass.class, afterClassMeth);
 348  25
                     if (afterByCheck == null) {
 349  25
                         final PerfidixMethodInvocationException afterByInvok =
 350  
                             BenchmarkExecutor
 351  
                                 .invokeMethod(objectToUse, AfterBenchClass.class, afterClassMeth);
 352  25
                         if (afterByInvok != null) {
 353  0
                             res.addException(afterByInvok);
 354  
                         }
 355  25
                     } else {
 356  0
                         res.addException(afterByCheck);
 357  
                     }
 358  
                 }
 359  
             }
 360  45
         }
 361  40
     }
 362  
 
 363  
     /**
 364  
      * Getting all Benchmarkable methods out of the registered class.
 365  
      * 
 366  
      * @return a Set with {@link BenchmarkMethod}
 367  
      */
 368  
     public List<BenchmarkMethod> getBenchmarkMethods() {
 369  
         // Generating Set for returnVal
 370  55
         final List<BenchmarkMethod> elems = new ArrayList<BenchmarkMethod>();
 371  
         // Getting all Methods and testing if its benchmarkable
 372  55
         for (final Class<?> clazz : clazzes) {
 373  765
             for (final Method meth : clazz.getDeclaredMethods()) {
 374  
                 // Check if benchmarkable, if so, insert to returnVal;
 375  680
                 if (BenchmarkMethod.isBenchmarkable(meth)) {
 376  170
                     final BenchmarkMethod benchmarkMeth = new BenchmarkMethod(meth);
 377  170
                     elems.add(benchmarkMeth);
 378  
                 }
 379  
             }
 380  85
         }
 381  55
         return elems;
 382  
     }
 383  
 
 384  
     /**
 385  
      * Getting all benchmarkable objects out of the registered classes with the
 386  
      * annotated number of runs.
 387  
      * 
 388  
      * @return a Set with {@link BenchmarkMethod}
 389  
      */
 390  
     public List<BenchmarkElement> getBenchmarkElements() {
 391  
 
 392  40
         final List<BenchmarkElement> elems = new ArrayList<BenchmarkElement>();
 393  
 
 394  40
         final List<BenchmarkMethod> meths = getBenchmarkMethods();
 395  
 
 396  40
         for (final BenchmarkMethod meth : meths) {
 397  90
             int numberOfRuns = BenchmarkMethod.getNumberOfAnnotatedRuns(meth.getMethodToBench());
 398  90
             if (numberOfRuns == Bench.NONE_RUN) {
 399  30
                 numberOfRuns = conf.getRuns();
 400  
             }
 401  
 
 402  
             // getting the number of runs and adding this number of
 403  
             // elements to the set to be evaluated.
 404  4065
             for (int i = 0; i < numberOfRuns; i++) {
 405  3975
                 elems.add(new BenchmarkElement(meth));
 406  
             }
 407  90
         }
 408  
 
 409  40
         return elems;
 410  
     }
 411  
 }