aboutsummaryrefslogtreecommitdiff
path: root/src/com
diff options
context:
space:
mode:
authorJesse Wilson <jessewilson@google.com>2009-12-15 18:54:02 -0800
committerJesse Wilson <jessewilson@google.com>2009-12-15 18:54:02 -0800
commit1440b36663f61ebde1952d91b4a1f4c1a27fcefa (patch)
tree35e2458d47b0de58bad416e002dc9f1e039ba9fd /src/com
parentda6e661c7e42d1358c2a49f0f02c7adc8e0a1671 (diff)
downloadcaliper-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.java26
-rw-r--r--src/com/google/caliper/Caliper.java9
-rw-r--r--src/com/google/caliper/ConsoleReport.java28
-rw-r--r--src/com/google/caliper/DefaultBenchmarkSuite.java180
-rw-r--r--src/com/google/caliper/Param.java2
-rw-r--r--src/com/google/caliper/Parameter.java8
-rw-r--r--src/com/google/caliper/Run.java13
-rw-r--r--src/com/google/caliper/Runner.java102
-rw-r--r--src/com/google/caliper/SimpleBenchmark.java171
-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&lt;Integer&gt; 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&lt;Integer&gt; 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&lt;Integer&gt; 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&lt;Integer&gt; 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;
+}