001package org.javasimon.utils;
002
003import org.javasimon.EnabledManager;
004import org.javasimon.Manager;
005import org.javasimon.Split;
006import org.javasimon.StopwatchSample;
007
008/**
009 * Utility class for benchmark execution.
010 *
011 * @author virgo47@gmail.com
012 * @since 3.1
013 */
014public class BenchmarkUtils {
015        private static final Manager MANAGER = new EnabledManager();
016
017        /**
018         * Runs the list of tasks to be benchmarked and returns {@link StopwatchSample} array with measured results.
019         * Number of warmup runs (without measuring) and measured runs must be specified.
020         * {@link Task} provides the name of the {@link org.javasimon.Stopwatch} that will store results.
021         * <p/>
022         * Tasks should not be extremely short - see {@link Task} javadoc for more.
023         *
024         * @param warmupRuns number of runs before the measuring starts
025         * @param measuredRuns number of measured runs
026         * @param tasks list of tasks to measure
027         * @return result of the measured runs as an array of stopwatch objects in the order of the tasks
028         */
029        public static StopwatchSample[] run(int warmupRuns, int measuredRuns, Task... tasks) {
030                warmup(warmupRuns, tasks);
031                measure(measuredRuns, tasks);
032                presentSummary(tasks);
033                StopwatchSample[] result = new StopwatchSample[tasks.length];
034                for (int i = 0; i < result.length; i++) {
035                        result[i] = MANAGER.getStopwatch(tasks[i].stopwatchName).sample();
036                }
037                return result;
038        }
039
040        private static void warmup(int warmupRuns, Task[] tasks) {
041                for (int i = 1; i <= warmupRuns; i++) {
042                        System.out.println("\nWarmup run #" + i);
043                        for (Task task : tasks) {
044                                warmupTask(task);
045                        }
046                }
047        }
048
049        private static void warmupTask(Task task) {
050                try {
051                        task.perform();
052                } catch (Exception e) {
053                        e.printStackTrace();
054                }
055        }
056
057        private static void measure(int measuredRuns, Task[] tasks) {
058                for (int i = 1; i <= measuredRuns; i++) {
059                        System.out.println("\nMeasured run #" + i);
060                        for (Task task : tasks) {
061                                task.run();
062                        }
063                }
064        }
065
066        private static void presentSummary(Task[] tasks) {
067                System.out.println("\nSUMMARY:");
068                for (Task task : tasks) {
069                        System.out.println(task.stopwatchName + ": " +
070                                MANAGER.getStopwatch(task.stopwatchName));
071                }
072        }
073
074        /**
075         * Helper object that requires implementing the {@link #perform()} method with benchmarked block of code.
076         * Calling {@link #run()} executes the code and measures statistics using the stopwatch named in the
077         * constructor. Calling {@link #perform()} executes the code without the stopwatch being used.
078         * <p/>
079         * It is not recommended to implement too short Task repeated for many runs (thousands or millions)
080         * but rather to impelement loop in the task to measure short operations and run the Task for units
081         * of times (tens, hundreds). Otherwise Simon overhead (mostly {@link System#nanoTime()} call) may
082         * distort the results. If the measured operation is extremely short even the for loop can distort the results.
083         */
084        public static abstract class Task implements Runnable {
085                private String stopwatchName;
086
087                /**
088                 * Protected constructor intended for extension.
089                 *
090                 * @param stopwatchName name of the stopwatch (measuring name)
091                 */
092                protected Task(String stopwatchName) {
093                        this.stopwatchName = stopwatchName;
094                }
095
096                /**
097                 * Executes {@link #perform()} and measures the run using the Stopwatch named in the constructor.
098                 */
099                @Override
100                public void run() {
101                        System.out.print(stopwatchName + ": ");
102                        Split split = Split.start();
103                        try {
104                                perform();
105                        } catch (Exception e) {
106                                e.printStackTrace();
107                        } finally {
108                                split.stop();
109                                System.out.println(split.presentRunningFor());
110                                MANAGER.getStopwatch(stopwatchName).addSplit(split);
111                        }
112                }
113
114                /**
115                 * Performes the measured block of code without actual measuring.
116                 *
117                 * @throws Exception any exception can be thrown here
118                 */
119                public abstract void perform() throws Exception;
120        }
121}