From 1440b36663f61ebde1952d91b4a1f4c1a27fcefa Mon Sep 17 00:00:00 2001 From: Jesse Wilson Date: Tue, 15 Dec 2009 18:54:02 -0800 Subject: 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. --- src/com/google/caliper/Benchmark.java | 26 ++-- src/com/google/caliper/BenchmarkSuite.java | 35 ----- src/com/google/caliper/Caliper.java | 9 +- src/com/google/caliper/ConsoleReport.java | 28 ++-- src/com/google/caliper/DefaultBenchmarkSuite.java | 180 ---------------------- src/com/google/caliper/Param.java | 2 +- src/com/google/caliper/Parameter.java | 8 +- src/com/google/caliper/Run.java | 13 +- src/com/google/caliper/Runner.java | 102 ++++-------- src/com/google/caliper/SimpleBenchmark.java | 171 ++++++++++++++++++++ src/com/google/caliper/TimedRunnable.java | 29 ++++ 11 files changed, 277 insertions(+), 326 deletions(-) delete mode 100644 src/com/google/caliper/BenchmarkSuite.java delete mode 100644 src/com/google/caliper/DefaultBenchmarkSuite.java create mode 100644 src/com/google/caliper/SimpleBenchmark.java create mode 100644 src/com/google/caliper/TimedRunnable.java (limited to 'src/com') 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 parameterNames(); + + Set parameterValues(String parameterName); - @Override public String toString() { - return getClass().getSimpleName(); - } -} + TimedRunnable createBenchmark(Map parameterValues); +} \ No newline at end of file diff --git a/src/com/google/caliper/BenchmarkSuite.java b/src/com/google/caliper/BenchmarkSuite.java deleted file mode 100644 index 40106a0..0000000 --- a/src/com/google/caliper/BenchmarkSuite.java +++ /dev/null @@ -1,35 +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.util.Map; -import java.util.Set; - -/** - * A collection of benchmarks that share a set of configuration parameters. - */ -public abstract class BenchmarkSuite { - - protected abstract Set> benchmarkClasses(); - - protected abstract Set parameterNames(); - - protected abstract Set parameterValues(String parameterName); - - protected abstract Benchmark createBenchmark( - Class benchmark, Map 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 parameters; private final Result result; private final List 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 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. - * - *

Benchmarks

- * 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. - * - *

Parameters

- * 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: - *
- *   {@literal @}Param int length;
- * 
- * 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. - *
- *   Iterable<Integer> lengthValues = Arrays.asList(10, 100, 1000, 10000);
- * 
- * 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. - *
- *   Iterable<Integer> lengthValues() {
- *     return Arrays.asList(10, 100, 1000, 10000);
- *   }
- * 
- */ -public abstract class DefaultBenchmarkSuite extends BenchmarkSuite { - - private final Map> parameters; - private final Map, 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> benchmarkClasses() { - return benchmarkFactories.keySet(); - } - - protected Set parameterNames() { - return parameters.keySet(); - } - - protected Set 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 result = new LinkedHashSet(); - for (Object value : values) { - result.add(typeConverter.toString(value, type)); - } - return result; - } catch (Exception e) { - throw new ExecutionException(e); - } - } - - protected Benchmark createBenchmark(Class benchmarkClass, - Map 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 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, BenchmarkFactory> createBenchmarkFactories() { - Map, BenchmarkFactory> result - = new LinkedHashMap, BenchmarkFactory>(); - for (Class c : getClass().getDeclaredClasses()) { - if (!Benchmark.class.isAssignableFrom(c) || c.isInterface()) { - continue; - } - - @SuppressWarnings("unchecked") // guarded by isAssignableFrom - Class benchmarkClass = (Class) c; - - try { - final Constructor 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 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 { @@ -33,7 +33,7 @@ abstract class Parameter { /** * Returns all properties for the given class. */ - public static Map> forClass(Class suiteClass) { + public static Map> forClass(Class suiteClass) { Map> parameters = new TreeMap>(); for (final Field field : suiteClass.getDeclaredFields()) { if (field.isAnnotationPresent(Param.class)) { @@ -46,7 +46,7 @@ abstract class Parameter { } public static Parameter forField( - Class suiteClass, final Field field) { + Class suiteClass, final Field field) { Parameter result = null; Type returnType = null; Member member = null; @@ -110,7 +110,7 @@ abstract class Parameter { /** * 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 parameters; - private final Class benchmarkClass; private final String vm; - public Run(Map parameters, - Class benchmarkClass, - String vm) { - this.benchmarkClass = benchmarkClass; + public Run(Map parameters, String vm) { this.parameters = ImmutableMap.copyOf(parameters); this.vm = vm; } @@ -41,15 +38,11 @@ final class Run { return parameters; } - public Class 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 parameters = LinkedHashMultimap.create(); @@ -49,12 +49,6 @@ public final class Runner { */ private Multimap 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 userBenchmarkClass; - /** * True if each benchmark should run in process. */ @@ -75,13 +69,13 @@ public final class Runner { private void prepareSuite() { try { @SuppressWarnings("unchecked") // guarded by the if statement that follows - Class suiteClass - = (Class) Class.forName(suiteClassName); - if (!BenchmarkSuite.class.isAssignableFrom(suiteClass)) { + Class suiteClass + = (Class) Class.forName(suiteClassName); + if (!Benchmark.class.isAssignableFrom(suiteClass)) { throw new ConfigurationException(suiteClass + " is not a benchmark suite."); } - Constructor constructor = suiteClass.getDeclaredConstructor(); + Constructor constructor = suiteClass.getDeclaredConstructor(); suite = constructor.newInstance(); } catch (InvocationTargetException e) { throw new ExecutionException(e.getCause()); @@ -121,33 +115,14 @@ public final class Runner { private List createRuns() throws Exception { List builders = new ArrayList(); - // create runs for each benchmark class - Set> benchmarkClasses = (userBenchmarkClass != null) - ? ImmutableSet.>of(userBenchmarkClass) - : suite.benchmarkClasses(); - for (Class 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 vms = userVms.isEmpty() ? defaultVms() : userVms; - Iterator 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> 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 parameters = new LinkedHashMap(); - Class 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 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 c = (Class) 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 : 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 suite, String... args) { + public static void main(Class 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. + * + *

Benchmarks

+ * The benchmarks of a suite are defined by . They may be + * static. They are not permitted to take parameters . . .. + * + *

Parameters

+ * 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: + *
+ *   {@literal @}Param int length;
+ * 
+ * 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. + *
+ *   Iterable<Integer> lengthValues = Arrays.asList(10, 100, 1000, 10000);
+ * 
+ * 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. + *
+ *   Iterable<Integer> lengthValues() {
+ *     return Arrays.asList(10, 100, 1000, 10000);
+ *   }
+ * 
+ */ +public abstract class SimpleBenchmark implements Benchmark { + + private static final Class[] ARGUMENT_TYPES = { int.class }; + + private final Map> parameters; + private final Map 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 parameterNames() { + return ImmutableSet.builder() + .add("benchmark") + .addAll(parameters.keySet()) + .build(); + } + + public Set 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 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 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 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 createTimedMethods() { + ImmutableMap.Builder 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/TimedRunnable.java b/src/com/google/caliper/TimedRunnable.java new file mode 100644 index 0000000..d1f9a6c --- /dev/null +++ b/src/com/google/caliper/TimedRunnable.java @@ -0,0 +1,29 @@ +/** + * 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; + +public interface TimedRunnable { + + /** + * 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; +} -- cgit v1.2.3