diff options
author | Jesse Wilson <jessewilson@google.com> | 2009-12-15 18:54:02 -0800 |
---|---|---|
committer | Jesse Wilson <jessewilson@google.com> | 2009-12-15 18:54:02 -0800 |
commit | 1440b36663f61ebde1952d91b4a1f4c1a27fcefa (patch) | |
tree | 35e2458d47b0de58bad416e002dc9f1e039ba9fd /src/com | |
parent | da6e661c7e42d1358c2a49f0f02c7adc8e0a1671 (diff) | |
download | caliper-1440b36663f61ebde1952d91b4a1f4c1a27fcefa.tar.gz |
Updating caliper to current SVN as of 20091215
A test
A test/com
A test/com/google
A test/com/google/caliper
A test/com/google/caliper/AllTests.java
A test/com/google/caliper/examples
A test/com/google/caliper/examples/ArraySortBenchmark.java
A test/com/google/caliper/examples/BoxedDoubleToStringBenchmark.java
A test/com/google/caliper/examples/ListIterationBenchmark.java
A test/com/google/caliper/examples/IntModBenchmark.java
A test/com/google/caliper/examples/CharacterBenchmark.java
A test/com/google/caliper/examples/PrimitiveDoubleToStringBenchmark.java
A test/com/google/caliper/examples/StringBuilderBenchmark.java
A test/com/google/caliper/examples/EnumSetContainsBenchmark.java
A test/com/google/caliper/examples/ExpensiveObjectsBenchmark.java
A test/com/google/caliper/examples/FormatterBenchmark.java
A lib
A lib/junit.jar
A lib/google-collect-1.0-rc4.jar
A src
A src/com
A src/com/google
A src/com/google/caliper
A src/com/google/caliper/Caliper.java
A src/com/google/caliper/Param.java
A src/com/google/caliper/Parameter.java
A src/com/google/caliper/ExecutionException.java
A src/com/google/caliper/Run.java
A src/com/google/caliper/SimpleBenchmark.java
A src/com/google/caliper/ConfigurationException.java
A src/com/google/caliper/Runner.java
A src/com/google/caliper/TypeConverter.java
A src/com/google/caliper/TimedRunnable.java
A src/com/google/caliper/Benchmark.java
A src/com/google/caliper/ConsoleReport.java
A src/com/google/caliper/Result.java
A caliper.ipr
A core.iml
A COPYING
A build.xml
Checked out revision 23.
Diffstat (limited to 'src/com')
-rw-r--r-- | src/com/google/caliper/Benchmark.java | 26 | ||||
-rw-r--r-- | src/com/google/caliper/Caliper.java | 9 | ||||
-rw-r--r-- | src/com/google/caliper/ConsoleReport.java | 28 | ||||
-rw-r--r-- | src/com/google/caliper/DefaultBenchmarkSuite.java | 180 | ||||
-rw-r--r-- | src/com/google/caliper/Param.java | 2 | ||||
-rw-r--r-- | src/com/google/caliper/Parameter.java | 8 | ||||
-rw-r--r-- | src/com/google/caliper/Run.java | 13 | ||||
-rw-r--r-- | src/com/google/caliper/Runner.java | 102 | ||||
-rw-r--r-- | src/com/google/caliper/SimpleBenchmark.java | 171 | ||||
-rw-r--r-- | src/com/google/caliper/TimedRunnable.java (renamed from src/com/google/caliper/BenchmarkSuite.java) | 28 |
10 files changed, 259 insertions, 308 deletions
diff --git a/src/com/google/caliper/Benchmark.java b/src/com/google/caliper/Benchmark.java index bd60b45..19426e6 100644 --- a/src/com/google/caliper/Benchmark.java +++ b/src/com/google/caliper/Benchmark.java @@ -16,18 +16,18 @@ package com.google.caliper; -public abstract class Benchmark { +import java.lang.reflect.Method; +import java.util.Map; +import java.util.Set; - /** - * Runs the benchmark through {@code trials} iterations. - * - * @return any object or null. Benchmark implementors may keep an accumulating - * value to prevent the runtime from optimizing away the code under test. - * Such an accumulator value can be returned here. - */ - public abstract Object run(int trials) throws Exception; +/** + * A collection of benchmarks that share a set of configuration parameters. + */ +public interface Benchmark { + + Set<String> parameterNames(); + + Set<String> parameterValues(String parameterName); - @Override public String toString() { - return getClass().getSimpleName(); - } -} + TimedRunnable createBenchmark(Map<String, String> parameterValues); +}
\ No newline at end of file diff --git a/src/com/google/caliper/Caliper.java b/src/com/google/caliper/Caliper.java index 855101e..315431f 100644 --- a/src/com/google/caliper/Caliper.java +++ b/src/com/google/caliper/Caliper.java @@ -34,13 +34,13 @@ class Caliper { this.runNanos = runMillis * 1000000; } - public double warmUp(Benchmark benchmark) throws Exception { + public double warmUp(TimedRunnable timedRunnable) throws Exception { long startNanos = System.nanoTime(); long endNanos = startNanos + warmupNanos; int trials = 0; long currentNanos; while ((currentNanos = System.nanoTime()) < endNanos) { - benchmark.run(1); + timedRunnable.run(1); trials++; } double nanosPerExecution = (currentNanos - startNanos) / trials; @@ -54,8 +54,11 @@ class Caliper { * In the run proper, we predict how extrapolate based on warmup how many * runs we're going to need, and run them all in a single batch. */ - public double run(Benchmark test, double estimatedNanosPerTrial) throws Exception { + public double run(TimedRunnable test, double estimatedNanosPerTrial) throws Exception { int trials = (int) (runNanos / estimatedNanosPerTrial); + if (trials == 0) { + trials = 1; + } long startNanos = System.nanoTime(); test.run(trials); long endNanos = System.nanoTime(); diff --git a/src/com/google/caliper/ConsoleReport.java b/src/com/google/caliper/ConsoleReport.java index 790ffe0..b367ec4 100644 --- a/src/com/google/caliper/ConsoleReport.java +++ b/src/com/google/caliper/ConsoleReport.java @@ -18,7 +18,10 @@ package com.google.caliper; import com.google.common.collect.*; -import java.util.*; +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; +import java.util.Map; /** * Prints a report containing the tested values and the corresponding @@ -36,13 +39,14 @@ import java.util.*; final class ConsoleReport { private static final int bargraphWidth = 30; - private static final String benchmarkKey = "benchmark"; private static final String vmKey = "vm"; private final List<Parameter> parameters; private final Result result; private final List<Run> runs; + private final double minValue; + private final double maxValue; private final double logMaxValue; private final int decimalDigits; private final double divideBy; @@ -61,15 +65,14 @@ final class ConsoleReport { Run run = entry.getKey(); double d = entry.getValue(); - minValue = minValue < d ? minValue : d; - maxValue = maxValue > d ? maxValue : d; + minValue = Math.min(minValue, d); + maxValue = Math.max(maxValue, d); for (Map.Entry<String, String> parameter : run.getParameters().entrySet()) { String name = parameter.getKey(); nameToValues.put(name, parameter.getValue()); } - nameToValues.put(benchmarkKey, run.getBenchmarkClass().getSimpleName()); nameToValues.put(vmKey, run.getVm()); } @@ -109,6 +112,8 @@ final class ConsoleReport { this.parameters = new StandardDeviationOrdering().reverse().sortedCopy(parametersBuilder); this.runs = new ByParametersOrdering().sortedCopy(result.getMeasurements().keySet()); + this.minValue = minValue; + this.maxValue = maxValue; this.logMaxValue = Math.log(maxValue); int numDigitsInMin = (int) Math.ceil(Math.log10(minValue)); @@ -155,9 +160,7 @@ final class ConsoleReport { } String get(Run run) { - if (benchmarkKey.equals(name)) { - return run.getBenchmarkClass().getSimpleName(); - } else if (vmKey.equals(name)) { + if (vmKey.equals(name)) { return run.getVm(); } else { return run.getParameters().get(name); @@ -210,6 +213,7 @@ final class ConsoleReport { * Prints a table of values. */ private void printValues() { + // header for (Parameter parameter : parameters) { if (parameter.isInteresting()) { System.out.printf("%" + parameter.maxLength + "s ", parameter.name); @@ -217,6 +221,7 @@ final class ConsoleReport { } System.out.printf("%" + measurementColumnLength + "s logarithmic runtime%n", units); + // rows String numbersFormat = "%" + measurementColumnLength + "." + decimalDigits + "f %s%n"; for (Run run : runs) { for (Parameter parameter : parameters) { @@ -245,10 +250,15 @@ final class ConsoleReport { * value. */ private String bargraph(double value) { + int numLinearChars = (int) ((value / maxValue) * bargraphWidth); double logValue = Math.log(value); int numChars = (int) ((logValue / logMaxValue) * bargraphWidth); StringBuilder result = new StringBuilder(numChars); - for (int i = 0; i < numChars; i++) { + for (int i = 0; i < numLinearChars; i++) { + result.append("X"); + } + + for (int i = numLinearChars; i < numChars; i++) { result.append("|"); } return result.toString(); diff --git a/src/com/google/caliper/DefaultBenchmarkSuite.java b/src/com/google/caliper/DefaultBenchmarkSuite.java deleted file mode 100644 index 0e197f6..0000000 --- a/src/com/google/caliper/DefaultBenchmarkSuite.java +++ /dev/null @@ -1,180 +0,0 @@ -/* - * Copyright (C) 2009 Google Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.google.caliper; - -import java.lang.reflect.Constructor; -import java.lang.reflect.Type; -import java.util.*; - -/** - * A convenience class for implementing benchmark suites in plain code. - * Implementing classes must have a no-arguments constructor. - * - * <h3>Benchmarks</h3> - * The benchmarks of a suite are defined by inner classes within the suite. - * These inner classes implement the {@link Benchmark} interface. They may be - * static. They are not permitted to take parameters in their constructors. - * - * <h3>Parameters</h3> - * Implementing classes may be configured using parameters. Each parameter is a - * property of a benchmark, plus the default values that fulfill it. Parameters - * are specified by annotated fields: - * <pre> - * {@literal @}Param int length; - * </pre> - * The available values for a parameter are specified by another field with the - * same name plus the {@code Values} suffix. The type of this field must be an - * {@code Iterable} of the parameter's type. - * <pre> - * Iterable<Integer> lengthValues = Arrays.asList(10, 100, 1000, 10000); - * </pre> - * Alternatively, the available values may be specified with a method. The - * method's name follows the same naming convention and returns the same type. - * Such methods may not accept parameters of their own. - * <pre> - * Iterable<Integer> lengthValues() { - * return Arrays.asList(10, 100, 1000, 10000); - * } - * </pre> - */ -public abstract class DefaultBenchmarkSuite extends BenchmarkSuite { - - private final Map<String, Parameter<?>> parameters; - private final Map<Class<? extends Benchmark>, BenchmarkFactory> benchmarkFactories; - - protected void setUp() throws Exception {} - - protected DefaultBenchmarkSuite() { - parameters = Parameter.forClass(getClass()); - benchmarkFactories = createBenchmarkFactories(); - - if (benchmarkFactories.isEmpty()) { - throw new ConfigurationException( - "No benchmarks defined in " + getClass().getName()); - } - } - - protected Set<Class<? extends Benchmark>> benchmarkClasses() { - return benchmarkFactories.keySet(); - } - - protected Set<String> parameterNames() { - return parameters.keySet(); - } - - protected Set<String> parameterValues(String parameterName) { - try { - TypeConverter typeConverter = new TypeConverter(); - Parameter<?> parameter = parameters.get(parameterName); - if (parameter == null) { - throw new IllegalArgumentException(); - } - Collection<?> values = parameter.values(); - Type type = parameter.getType(); - Set<String> result = new LinkedHashSet<String>(); - for (Object value : values) { - result.add(typeConverter.toString(value, type)); - } - return result; - } catch (Exception e) { - throw new ExecutionException(e); - } - } - - protected Benchmark createBenchmark(Class<? extends Benchmark> benchmarkClass, - Map<String, String> parameterValues) { - TypeConverter typeConverter = new TypeConverter(); - - BenchmarkFactory benchmarkFactory = benchmarkFactories.get(benchmarkClass); - if (benchmarkFactory == null) { - throw new IllegalArgumentException(); - } - - if (!parameters.keySet().equals(parameterValues.keySet())) { - throw new IllegalArgumentException("Invalid parameters specified. Expected " - + parameters.keySet() + " but was " + parameterValues.keySet()); - } - - try { - DefaultBenchmarkSuite copyOfSelf = getClass().newInstance(); - Benchmark benchmark = benchmarkFactory.create(copyOfSelf); - for (Map.Entry<String, String> entry : parameterValues.entrySet()) { - Parameter parameter = parameters.get(entry.getKey()); - Object value = typeConverter.fromString(entry.getValue(), parameter.getType()); - parameter.set(copyOfSelf, value); - } - - copyOfSelf.setUp(); - return benchmark; - - } catch (Exception e) { - throw new ExecutionException(e); - } - } - - /** - * Returns a spec for each benchmark defined in the specified class. The - * returned specs have no parameter values; those must be added separately. - */ - private Map<Class<? extends Benchmark>, BenchmarkFactory> createBenchmarkFactories() { - Map<Class<? extends Benchmark>, BenchmarkFactory> result - = new LinkedHashMap<Class<? extends Benchmark>, BenchmarkFactory>(); - for (Class<?> c : getClass().getDeclaredClasses()) { - if (!Benchmark.class.isAssignableFrom(c) || c.isInterface()) { - continue; - } - - @SuppressWarnings("unchecked") // guarded by isAssignableFrom - Class<? extends Benchmark> benchmarkClass = (Class<? extends Benchmark>) c; - - try { - final Constructor<? extends Benchmark> constructor - = benchmarkClass.getDeclaredConstructor(); - constructor.setAccessible(true); - result.put(benchmarkClass, new BenchmarkFactory() { - public Benchmark create(BenchmarkSuite suite) throws Exception { - return constructor.newInstance(); - } - }); - continue; - } catch (NoSuchMethodException ignored) { - } - - try { - final Constructor<? extends Benchmark> constructor - = benchmarkClass.getDeclaredConstructor(getClass()); - constructor.setAccessible(true); - result.put(benchmarkClass, new BenchmarkFactory() { - public Benchmark create(BenchmarkSuite suite) throws Exception { - return constructor.newInstance(suite); - } - }); - continue; - } catch (NoSuchMethodException ignored) { - } - - throw new ConfigurationException("No usable constructor for " - + benchmarkClass.getName() + "\n Benchmarks may only use no arguments constructors."); - } - - return result; - } - - interface BenchmarkFactory { - Benchmark create(BenchmarkSuite suite) throws Exception; - } -}
\ No newline at end of file diff --git a/src/com/google/caliper/Param.java b/src/com/google/caliper/Param.java index 0a9b203..28d3588 100644 --- a/src/com/google/caliper/Param.java +++ b/src/com/google/caliper/Param.java @@ -22,7 +22,7 @@ import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; /** - * Annotates the field accepting a parameter in a {@link DefaultBenchmarkSuite}. + * Annotates the field accepting a parameter in a {@link SimpleBenchmark}. */ @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.FIELD) diff --git a/src/com/google/caliper/Parameter.java b/src/com/google/caliper/Parameter.java index a5ab6c8..1ba77b5 100644 --- a/src/com/google/caliper/Parameter.java +++ b/src/com/google/caliper/Parameter.java @@ -20,7 +20,7 @@ import java.lang.reflect.*; import java.util.*; /** - * A parameter in a {@link DefaultBenchmarkSuite}. + * A parameter in a {@link SimpleBenchmark}. */ abstract class Parameter<T> { @@ -33,7 +33,7 @@ abstract class Parameter<T> { /** * Returns all properties for the given class. */ - public static Map<String, Parameter<?>> forClass(Class<? extends BenchmarkSuite> suiteClass) { + public static Map<String, Parameter<?>> forClass(Class<? extends Benchmark> suiteClass) { Map<String, Parameter<?>> parameters = new TreeMap<String, Parameter<?>>(); for (final Field field : suiteClass.getDeclaredFields()) { if (field.isAnnotationPresent(Param.class)) { @@ -46,7 +46,7 @@ abstract class Parameter<T> { } public static Parameter forField( - Class<? extends BenchmarkSuite> suiteClass, final Field field) { + Class<? extends Benchmark> suiteClass, final Field field) { Parameter result = null; Type returnType = null; Member member = null; @@ -110,7 +110,7 @@ abstract class Parameter<T> { /** * Sets the value of this property to the specified value for the given suite. */ - public void set(BenchmarkSuite suite, Object value) throws Exception { + public void set(Benchmark suite, Object value) throws Exception { field.set(suite, value); } diff --git a/src/com/google/caliper/Run.java b/src/com/google/caliper/Run.java index 5ab8856..a9109de 100644 --- a/src/com/google/caliper/Run.java +++ b/src/com/google/caliper/Run.java @@ -18,6 +18,7 @@ package com.google.caliper; import com.google.common.collect.ImmutableMap; +import java.lang.reflect.Method; import java.util.Map; /** @@ -26,13 +27,9 @@ import java.util.Map; final class Run { private final ImmutableMap<String, String> parameters; - private final Class<? extends Benchmark> benchmarkClass; private final String vm; - public Run(Map<String, String> parameters, - Class<? extends Benchmark> benchmarkClass, - String vm) { - this.benchmarkClass = benchmarkClass; + public Run(Map<String, String> parameters, String vm) { this.parameters = ImmutableMap.copyOf(parameters); this.vm = vm; } @@ -41,15 +38,11 @@ final class Run { return parameters; } - public Class<? extends Benchmark> getBenchmarkClass() { - return benchmarkClass; - } - public String getVm() { return vm; } @Override public String toString() { - return benchmarkClass.getSimpleName() + " " + parameters; + return "Run" + parameters; } } diff --git a/src/com/google/caliper/Runner.java b/src/com/google/caliper/Runner.java index 68f26f1..72442db 100644 --- a/src/com/google/caliper/Runner.java +++ b/src/com/google/caliper/Runner.java @@ -16,10 +16,10 @@ package com.google.caliper; -import com.google.common.collect.LinkedHashMultimap; -import com.google.common.collect.Multimap; import com.google.common.collect.ImmutableMap; import com.google.common.collect.ImmutableSet; +import com.google.common.collect.LinkedHashMultimap; +import com.google.common.collect.Multimap; import java.io.BufferedReader; import java.io.File; @@ -35,7 +35,7 @@ import java.util.*; public final class Runner { private String suiteClassName; - private BenchmarkSuite suite; + private Benchmark suite; /** Effective parameters to run in the benchmark. */ private Multimap<String, String> parameters = LinkedHashMultimap.create(); @@ -50,12 +50,6 @@ public final class Runner { private Multimap<String, String> userParameters = LinkedHashMultimap.create(); /** - * Benchmark class specified by the user on the command line; or null to run - * the complete set of benchmark classes. - */ - private Class<? extends Benchmark> userBenchmarkClass; - - /** * True if each benchmark should run in process. */ private boolean inProcess; @@ -75,13 +69,13 @@ public final class Runner { private void prepareSuite() { try { @SuppressWarnings("unchecked") // guarded by the if statement that follows - Class<? extends BenchmarkSuite> suiteClass - = (Class<? extends BenchmarkSuite>) Class.forName(suiteClassName); - if (!BenchmarkSuite.class.isAssignableFrom(suiteClass)) { + Class<? extends Benchmark> suiteClass + = (Class<? extends Benchmark>) Class.forName(suiteClassName); + if (!Benchmark.class.isAssignableFrom(suiteClass)) { throw new ConfigurationException(suiteClass + " is not a benchmark suite."); } - Constructor<? extends BenchmarkSuite> constructor = suiteClass.getDeclaredConstructor(); + Constructor<? extends Benchmark> constructor = suiteClass.getDeclaredConstructor(); suite = constructor.newInstance(); } catch (InvocationTargetException e) { throw new ExecutionException(e.getCause()); @@ -121,33 +115,14 @@ public final class Runner { private List<Run> createRuns() throws Exception { List<RunBuilder> builders = new ArrayList<RunBuilder>(); - // create runs for each benchmark class - Set<Class<? extends Benchmark>> benchmarkClasses = (userBenchmarkClass != null) - ? ImmutableSet.<Class<? extends Benchmark>>of(userBenchmarkClass) - : suite.benchmarkClasses(); - for (Class<? extends Benchmark> benchmarkClass : benchmarkClasses) { - RunBuilder builder = new RunBuilder(); - builder.benchmarkClass = benchmarkClass; - builders.add(builder); - } - - // multiply the runs by the number of VMs + // create runs for each VMs Set<String> vms = userVms.isEmpty() ? defaultVms() : userVms; - Iterator<String> vmIterator = vms.iterator(); - String firstVm = vmIterator.next(); - for (RunBuilder builder : builders) { - builder.vm = firstVm; - } - int length = builders.size(); - while (vmIterator.hasNext()) { - String alternateVm = vmIterator.next(); - for (int s = 0; s < length; s++) { - RunBuilder copy = builders.get(s).copy(); - copy.vm = alternateVm; - builders.add(copy); - } + for (String vm : vms) { + RunBuilder runBuilder = new RunBuilder(); + runBuilder.vm = vm; + builders.add(runBuilder); } for (Map.Entry<String, Collection<String>> parameter : parameters.asMap().entrySet()) { @@ -164,10 +139,10 @@ public final class Runner { } // multiply the size of the specs by the number of alternate values - length = builders.size(); + int size = builders.size(); while (values.hasNext()) { String alternate = values.next(); - for (int s = 0; s < length; s++) { + for (int s = 0; s < size; s++) { RunBuilder copy = builders.get(s).copy(); copy.parameters.put(key, alternate); builders.add(copy); @@ -185,19 +160,17 @@ public final class Runner { static class RunBuilder { Map<String, String> parameters = new LinkedHashMap<String, String>(); - Class<? extends Benchmark> benchmarkClass; String vm; RunBuilder copy() { RunBuilder result = new RunBuilder(); result.parameters.putAll(parameters); - result.benchmarkClass = benchmarkClass; result.vm = vm; return result; } public Run build() { - return new Run(parameters, benchmarkClass, vm); + return new Run(parameters, vm); } } @@ -213,8 +186,6 @@ public final class Runner { command.add("--runMillis"); command.add(String.valueOf(runMillis)); command.add("--inProcess"); - command.add("--benchmark"); - command.add(run.getBenchmarkClass().getName()); for (Map.Entry<String, String> entry : run.getParameters().entrySet()) { command.add("-D" + entry.getKey() + "=" + entry.getValue()); } @@ -270,6 +241,14 @@ public final class Runner { afterRun(nanosPerTrial); resultsBuilder.put(run, nanosPerTrial); } + + // blat out our progress bar + System.out.print("\r"); + for (int j = 0; j < 80; j++) { + System.out.print(" "); + } + System.out.print("\r"); + return new Result(resultsBuilder.build()); } catch (Exception e) { throw new ExecutionException(e); @@ -283,12 +262,12 @@ public final class Runner { if (runString.length() > runStringLength) { runString = runString.substring(0, runStringLength); } - System.out.printf("%2.0f%% %-" + runStringLength + "s", + System.out.printf("\r%2.0f%% %-" + runStringLength + "s", percentDone * 100, runString); } private void afterRun(double nanosPerTrial) { - System.out.printf(" %10.0fns%n", nanosPerTrial); + System.out.printf(" %10.0fns", nanosPerTrial); } private void runInProcess() { @@ -297,10 +276,9 @@ public final class Runner { for (Run run : createRuns()) { double result; - Benchmark benchmark = suite.createBenchmark( - run.getBenchmarkClass(), run.getParameters()); - double warmupNanosPerTrial = caliper.warmUp(benchmark); - result = caliper.run(benchmark, warmupNanosPerTrial); + TimedRunnable timedRunnable = suite.createBenchmark(run.getParameters()); + double warmupNanosPerTrial = caliper.warmUp(timedRunnable); + result = caliper.run(timedRunnable, warmupNanosPerTrial); double nanosPerTrial = result; System.out.println(nanosPerTrial); } @@ -309,24 +287,11 @@ public final class Runner { } } - private boolean parseArgs(String[] args) { + private boolean parseArgs(String[] args) throws Exception { for (int i = 0; i < args.length; i++) { if ("--help".equals(args[i])) { return false; - } else if ("--benchmark".equals(args[i])) { - try { - @SuppressWarnings("unchecked") // guarded immediately afterwards! - Class<? extends Benchmark> c = (Class<? extends Benchmark>) Class.forName(args[++i]); - if (!Benchmark.class.isAssignableFrom(c)) { - System.out.println("Not a benchmark class: " + c); - return false; - } - userBenchmarkClass = c; - } catch (ClassNotFoundException e) { - throw new RuntimeException(e); - } - } else if ("--inProcess".equals(args[i])) { inProcess = true; @@ -358,9 +323,7 @@ public final class Runner { System.out.println("Too many benchmark classes!"); return false; } - suiteClassName = args[i]; - } } @@ -388,8 +351,6 @@ public final class Runner { System.out.println(" When multiple values for the same parameter are given (via"); System.out.println(" multiple --Dx=y args), all supplied values are used."); System.out.println(); - System.out.println(" --benchmark <class>: fix a benchmark executable to the named class"); - System.out.println(); System.out.println(" --inProcess: run the benchmark in the same JVM rather than spawning"); System.out.println(" another with the same classpath. By default each benchmark is"); System.out.println(" run in a separate VM"); @@ -403,7 +364,7 @@ public final class Runner { // adding new options? don't forget to update executeForked() } - public static void main(String... args) { + public static void main(String... args) throws Exception { // TODO: cleaner error reporting Runner runner = new Runner(); if (!runner.parseArgs(args)) { runner.printUsage(); @@ -418,11 +379,10 @@ public final class Runner { } Result result = runner.runOutOfProcess(); - System.out.println(); new ConsoleReport(result).displayResults(); } - public static void main(Class<? extends BenchmarkSuite> suite, String... args) { + public static void main(Class<? extends Benchmark> suite, String... args) throws Exception { String[] argsWithSuiteName = new String[args.length + 1]; System.arraycopy(args, 0, argsWithSuiteName, 0, args.length); argsWithSuiteName[args.length] = suite.getName(); diff --git a/src/com/google/caliper/SimpleBenchmark.java b/src/com/google/caliper/SimpleBenchmark.java new file mode 100644 index 0000000..8d2d4b1 --- /dev/null +++ b/src/com/google/caliper/SimpleBenchmark.java @@ -0,0 +1,171 @@ +/* + * Copyright (C) 2009 Google Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.caliper; + +import com.google.common.collect.ImmutableMap; +import com.google.common.collect.ImmutableSet; + +import java.lang.reflect.Method; +import java.lang.reflect.Modifier; +import java.lang.reflect.Type; +import java.util.Arrays; +import java.util.Collection; +import java.util.Map; +import java.util.Set; + +/** + * A convenience class for implementing benchmarks in plain code. + * Implementing classes must have a no-arguments constructor. + * + * <h3>Benchmarks</h3> + * The benchmarks of a suite are defined by . They may be + * static. They are not permitted to take parameters . . .. + * + * <h3>Parameters</h3> + * Implementing classes may be configured using parameters. Each parameter is a + * property of a benchmark, plus the default values that fulfill it. Parameters + * are specified by annotated fields: + * <pre> + * {@literal @}Param int length; + * </pre> + * The available values for a parameter are specified by another field with the + * same name plus the {@code Values} suffix. The type of this field must be an + * {@code Iterable} of the parameter's type. + * <pre> + * Iterable<Integer> lengthValues = Arrays.asList(10, 100, 1000, 10000); + * </pre> + * Alternatively, the available values may be specified with a method. The + * method's name follows the same naming convention and returns the same type. + * Such methods may not accept parameters of their own. + * <pre> + * Iterable<Integer> lengthValues() { + * return Arrays.asList(10, 100, 1000, 10000); + * } + * </pre> + */ +public abstract class SimpleBenchmark implements Benchmark { + + private static final Class<?>[] ARGUMENT_TYPES = { int.class }; + + private final Map<String, Parameter<?>> parameters; + private final Map<String, Method> methods; + + protected SimpleBenchmark() { + parameters = Parameter.forClass(getClass()); + methods = createTimedMethods(); + + if (methods.isEmpty()) { + throw new ConfigurationException( + "No benchmarks defined in " + getClass().getName()); + } + } + + protected void setUp() throws Exception {} + + public Set<String> parameterNames() { + return ImmutableSet.<String>builder() + .add("benchmark") + .addAll(parameters.keySet()) + .build(); + } + + public Set<String> parameterValues(String parameterName) { + if ("benchmark".equals(parameterName)) { + return methods.keySet(); + } + + try { + TypeConverter typeConverter = new TypeConverter(); + Parameter<?> parameter = parameters.get(parameterName); + if (parameter == null) { + throw new IllegalArgumentException(); + } + Collection<?> values = parameter.values(); + Type type = parameter.getType(); + + ImmutableSet.Builder<String> result = ImmutableSet.builder(); + for (Object value : values) { + result.add(typeConverter.toString(value, type)); + } + return result.build(); + } catch (Exception e) { + throw new ExecutionException(e); + } + } + + public TimedRunnable createBenchmark(Map<String, String> parameterValues) { + TypeConverter typeConverter = new TypeConverter(); + + if (!parameterNames().equals(parameterValues.keySet())) { + throw new IllegalArgumentException("Invalid parameters specified. Expected " + + parameterNames() + " but was " + parameterValues.keySet()); + } + + try { + final SimpleBenchmark copyOfSelf = getClass().newInstance(); + final Method method = methods.get(parameterValues.get("benchmark")); + + for (Map.Entry<String, String> entry : parameterValues.entrySet()) { + String parameterName = entry.getKey(); + if ("benchmark".equals(parameterName)) { + continue; + } + + Parameter parameter = parameters.get(parameterName); + Object value = typeConverter.fromString(entry.getValue(), parameter.getType()); + parameter.set(copyOfSelf, value); + } + copyOfSelf.setUp(); + + return new TimedRunnable() { + public Object run(int reps) throws Exception { + return method.invoke(copyOfSelf, reps); + } + }; + + } catch (Exception e) { + throw new ExecutionException(e); + } + } + + /** + * Returns a spec for each benchmark defined in the specified class. The + * returned specs have no parameter values; those must be added separately. + */ + private Map<String, Method> createTimedMethods() { + ImmutableMap.Builder<String, Method> result = ImmutableMap.builder(); + for (final Method method : getClass().getDeclaredMethods()) { + int modifiers = method.getModifiers(); + if (!method.getName().startsWith("time")) { + continue; + } + + if (!Modifier.isPublic(modifiers) + || Modifier.isStatic(modifiers) + || Modifier.isAbstract(modifiers) + || !Arrays.equals(method.getParameterTypes(), ARGUMENT_TYPES)) { + throw new ConfigurationException("Timed methods must be public, " + + "non-static, non-abstract and take a single int parameter. " + + "But " + method + " violates these requirements."); + } + + result.put(method.getName().substring(4), method); + } + + return result.build(); + } +}
\ No newline at end of file diff --git a/src/com/google/caliper/BenchmarkSuite.java b/src/com/google/caliper/TimedRunnable.java index 40106a0..d1f9a6c 100644 --- a/src/com/google/caliper/BenchmarkSuite.java +++ b/src/com/google/caliper/TimedRunnable.java @@ -1,4 +1,4 @@ -/* +/** * Copyright (C) 2009 Google Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -16,20 +16,14 @@ package com.google.caliper; -import java.util.Map; -import java.util.Set; - -/** - * A collection of benchmarks that share a set of configuration parameters. - */ -public abstract class BenchmarkSuite { - - protected abstract Set<Class<? extends Benchmark>> benchmarkClasses(); - - protected abstract Set<String> parameterNames(); - - protected abstract Set<String> parameterValues(String parameterName); +public interface TimedRunnable { - protected abstract Benchmark createBenchmark( - Class<? extends Benchmark> benchmark, Map<String, String> parameterValues); -}
\ No newline at end of file + /** + * Runs the benchmark through {@code trials} iterations. + * + * @return any object or null. Benchmark implementors may keep an accumulating + * value to prevent the runtime from optimizing away the code under test. + * Such an accumulator value can be returned here. + */ + Object run(int reps) throws Exception; +} |