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}