001: /*
002: * Primitive Collections for Java.
003: * Copyright (C) 2003 Søren Bak
004: *
005: * This library is free software; you can redistribute it and/or
006: * modify it under the terms of the GNU Lesser General Public
007: * License as published by the Free Software Foundation; either
008: * version 2.1 of the License, or (at your option) any later version.
009: *
010: * This library is distributed in the hope that it will be useful,
011: * but WITHOUT ANY WARRANTY; without even the implied warranty of
012: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
013: * Lesser General Public License for more details.
014: *
015: * You should have received a copy of the GNU Lesser General Public
016: * License along with this library; if not, write to the Free Software
017: * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
018: */
019: package bak.pcj.benchmark;
020:
021: import java.lang.reflect.Method;
022: import java.lang.reflect.Constructor;
023: import java.lang.reflect.Modifier;
024: import java.lang.reflect.InvocationTargetException;
025:
026: import java.io.Writer;
027: import java.io.FileWriter;
028: import java.io.IOException;
029:
030: /**
031: * Controls the execution of benchmarks. The runner automatically
032: * discovers benchmark tasks in benchmark classes using the
033: * reflection mechanism. This procedure is inspired by the way that
034: * JUnit works.
035: *
036: * @author Søren Bak
037: * @version 1.1 2003/15/2
038: * @since 1.0
039: */
040: public class BenchmarkRunner {
041:
042: /** Indicates whether verbose output should be enabled. */
043: private boolean verbose = true;
044:
045: /** The report generated by this runner. */
046: private Report report;
047:
048: /** The time used by the relax() method. */
049: private long relaxTime = 4000L;
050:
051: /**
052: * Creates a new benchmark runner with an empty report.
053: */
054: public BenchmarkRunner() {
055: report = new Report();
056: }
057:
058: /**
059: * Enables or disables verbose output to <tt>System.out</tt>.
060: * Verbose output is enabled by default.
061: *
062: * @param verbose
063: * <tt>true</tt> if verbose output should be enabled;
064: * <tt>false</tt> otherwise.
065: */
066: public void setVerbose(boolean verbose) {
067: this .verbose = verbose;
068: }
069:
070: /**
071: * Relaxes the JVM by giving it a few seconds to garbage collect
072: * and stabilize disk I/O.
073: */
074: private void relax() {
075: if (verbose)
076: System.out.println("Garbage collecting...");
077: System.gc();
078: try {
079: Thread.sleep(relaxTime);
080: } catch (InterruptedException e) {
081: }
082: }
083:
084: private String nameOf(Class cls) {
085: String name = cls.getName();
086: int ldot = name.lastIndexOf('.');
087: if (ldot != -1)
088: name = name.substring(ldot + 1);
089: return name;
090: }
091:
092: /**
093: * Returns the report produced by this benchmark runner.
094: * Note that the returned report is mutable and can be modified
095: * before formatting.
096: *
097: * @return the report produced by this benchmark runner.
098: */
099: public Report getReport() {
100: return report;
101: }
102:
103: /**
104: * Runs a specified benchmark on a specified data set.
105: * The results are collected in the runner's report.
106: *
107: * @param bm
108: * the benchmark to run.
109: *
110: * @param dataSet
111: * the data set on which to run the benchmark.
112: *
113: * @see #getReport()
114: */
115: public void runBenchmark(Benchmark bm, DataSet dataSet) {
116: relax();
117:
118: // Set up id's
119: String classId = bm.getClassId();
120: String dataSetId = dataSet.getId();
121: String benchmarkId = nameOf(bm.getClass());
122: String taskDescription = "";
123:
124: if (verbose)
125: System.out.println("Running " + benchmarkId + "/" + classId
126: + " with data " + dataSetId);
127:
128: // Loop over benchmark methods
129: Method[] methods = bm.getClass().getMethods();
130: for (int i = 0; i < methods.length; i++) {
131: Method m = methods[i];
132: // Non-abstract public methods that starts with "benchmark" and has one DataSet argument
133: if ((m.getModifiers() & (Modifier.PUBLIC | Modifier.ABSTRACT)) == Modifier.PUBLIC
134: && m.getName().startsWith("benchmark")
135: && m.getParameterTypes().length == 1
136: && (DataSet.class).isAssignableFrom(m
137: .getParameterTypes()[0])) {
138: if (verbose)
139: System.out.println(" task: "
140: + m.getName().substring(
141: "benchmark".length()));
142: try {
143: taskDescription = (String) m.invoke(bm,
144: new Object[] { dataSet });
145: } catch (IllegalAccessException iae) {
146: throw new RuntimeException(iae.getMessage());
147: } catch (InvocationTargetException ite) {
148: throw new RuntimeException(ite.getMessage());
149: }
150: long time = bm.readTimer();
151: String taskId = m.getName().substring(
152: "benchmark".length());
153: Result result = new Result(benchmarkId, dataSetId,
154: classId, taskId, taskDescription, time);
155: report.addResult(result);
156: }
157: }
158: }
159:
160: private static void printUsageAndExit(Throwable e) {
161: System.err
162: .println("Usage: bak.pcj.benchmark.BenchmarkRunner <dataset class> <dataset size> <benchmark class> <output file>");
163: if (e != null) {
164: System.err.println("An exception was raised:");
165: e.printStackTrace();
166: }
167: System.exit(1);
168: }
169:
170: /**
171: * Runs the a benchmark from the command line.
172: * <br>The first parameter is the name of the data set class to use.
173: * <br>The second parameter is the size of the data sets to use.
174: * <br>The third parameter is the name of the benchmark class to use.
175: * <br>The fourth parameter is name of file on which to write the
176: * results.
177: *
178: * @param args
179: * as specified above.
180: */
181: public static void main(String[] args) {
182: int size = 0;
183: DataSet dataSet = null;
184: Benchmark benchmark = null;
185: Writer out = null;
186:
187: // Check arguments
188: if (args.length != 4)
189: printUsageAndExit(null);
190:
191: // Data set size
192: try {
193: size = Integer.parseInt(args[1]);
194: if (size <= 0)
195: printUsageAndExit(null);
196: } catch (NumberFormatException e) {
197: printUsageAndExit(e);
198: }
199:
200: // Data set
201: try {
202: Class dataSetClass = Class.forName(args[0]);
203: Constructor c = dataSetClass
204: .getConstructor(new Class[] { Integer.TYPE });
205: dataSet = (DataSet) c
206: .newInstance(new Object[] { new Integer(size) });
207: } catch (InstantiationException ie) {
208: printUsageAndExit(ie);
209: } catch (IllegalAccessException iae) {
210: printUsageAndExit(iae);
211: } catch (ClassNotFoundException cnfe) {
212: printUsageAndExit(cnfe);
213: } catch (NoSuchMethodException nsme) {
214: printUsageAndExit(nsme);
215: } catch (InvocationTargetException ite) {
216: printUsageAndExit(ite);
217: }
218:
219: // Benchmark
220: try {
221: Class benchmarkClass = Class.forName(args[2]);
222: benchmark = (Benchmark) benchmarkClass.newInstance();
223: } catch (InstantiationException ie) {
224: printUsageAndExit(ie);
225: } catch (IllegalAccessException iae) {
226: printUsageAndExit(iae);
227: } catch (ClassNotFoundException cnfe) {
228: printUsageAndExit(cnfe);
229: }
230:
231: // Output
232: try {
233: out = new FileWriter(args[3]);
234: } catch (IOException e) {
235: printUsageAndExit(e);
236: }
237:
238: BenchmarkRunner run = new BenchmarkRunner();
239:
240: // Run once to load classes and compile code
241: run.runBenchmark(benchmark, dataSet);
242: run.report.clearResults();
243:
244: // Run again to collect results
245: run.runBenchmark(benchmark, dataSet);
246:
247: try {
248: run.report.writeResults(out);
249: out.flush();
250: out.close();
251: } catch (IOException e) {
252: printUsageAndExit(e);
253: }
254: }
255:
256: }
|