diff options
24 files changed, 1009 insertions, 631 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; +} diff --git a/test/com/google/caliper/AllTests.java b/test/com/google/caliper/AllTests.java index 55f620a..510cc0a 100644 --- a/test/com/google/caliper/AllTests.java +++ b/test/com/google/caliper/AllTests.java @@ -22,7 +22,7 @@ import junit.framework.TestSuite; public final class AllTests { public static Test suite() { TestSuite suite = new TestSuite(); - suite.addTestSuite(DefaultBenchmarkSuiteTest.class); + // tests go here :) return suite; } } diff --git a/test/com/google/caliper/DefaultBenchmarkSuiteTest.java b/test/com/google/caliper/DefaultBenchmarkSuiteTest.java deleted file mode 100644 index 232cb4c..0000000 --- a/test/com/google/caliper/DefaultBenchmarkSuiteTest.java +++ /dev/null @@ -1,91 +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 com.google.common.collect.ImmutableMap; -import com.google.common.collect.ImmutableSet; - -import junit.framework.TestCase; - -import java.util.Arrays; -import java.util.Collection; - -public class DefaultBenchmarkSuiteTest extends TestCase { - - public void testIntrospection() { - SampleBenchmarkSuite suite = new SampleBenchmarkSuite(); - assertEquals(ImmutableSet.of("a", "b"), suite.parameterNames()); - assertEquals(ImmutableSet.of("1", "2", "3"), suite.parameterValues("a")); - assertEquals(ImmutableSet.of("4"), suite.parameterValues("b")); - assertEquals(ImmutableSet.of( - SampleBenchmarkSuite.MultiplyBenchmark.class, - SampleBenchmarkSuite.DivideBenchmark.class), - suite.benchmarkClasses()); - - } - - public void testCreateBenchmark() { - SampleBenchmarkSuite originalSuite = new SampleBenchmarkSuite(); - Benchmark benchmark = originalSuite.createBenchmark( - SampleBenchmarkSuite.MultiplyBenchmark.class, - ImmutableMap.of("a", "2", "b", "4")); - - SampleBenchmarkSuite.MultiplyBenchmark multiplyBenchmark - = (SampleBenchmarkSuite.MultiplyBenchmark) benchmark; - - SampleBenchmarkSuite multiplySuite = multiplyBenchmark.suite(); - assertNotSame(originalSuite, multiplySuite); - assertEquals(2, multiplySuite.a); - assertEquals(4, multiplySuite.b); - } - - static class SampleBenchmarkSuite extends DefaultBenchmarkSuite { - @Param int a; - - private static Collection<Integer> aValues = Arrays.asList(1, 2, 3); - - @Param int b; - - private static Collection<Integer> bValues() { - return Arrays.asList(4); - } - - class MultiplyBenchmark extends Benchmark { - @Override public Object run(int trials) throws Exception { - int result = 0; - for (int i = 0; i < trials; i++) { - result ^= a * b; - } - return result; - } - - SampleBenchmarkSuite suite() { - return SampleBenchmarkSuite.this; - } - } - - class DivideBenchmark extends Benchmark { - @Override public Object run(int trials) throws Exception { - int result = 0; - for (int i = 0; i < trials; i++) { - result ^= a / b; - } - return result; - } - } - } -} diff --git a/test/com/google/caliper/examples/SortBenchmarkSuite.java b/test/com/google/caliper/examples/ArraySortBenchmark.java index e92fc10..2978fa2 100644 --- a/test/com/google/caliper/examples/SortBenchmarkSuite.java +++ b/test/com/google/caliper/examples/ArraySortBenchmark.java @@ -16,20 +16,19 @@ package com.google.caliper.examples; -import com.google.caliper.Benchmark; -import com.google.caliper.DefaultBenchmarkSuite; import com.google.caliper.Param; import com.google.caliper.Runner; +import com.google.caliper.SimpleBenchmark; import java.util.Arrays; +import java.util.Collection; import java.util.EnumSet; import java.util.Random; -import java.util.Collection; /** * Measures sorting on different distributions of integers. */ -public class SortBenchmarkSuite extends DefaultBenchmarkSuite { +public class ArraySortBenchmark extends SimpleBenchmark { @Param int length; @@ -37,7 +36,7 @@ public class SortBenchmarkSuite extends DefaultBenchmarkSuite { @Param Distribution distribution; - static Collection<Distribution> distributionValues = EnumSet.allOf(Distribution.class); + static final Collection<Distribution> distributionValues = EnumSet.allOf(Distribution.class); int[] values; int[] copy; @@ -47,16 +46,14 @@ public class SortBenchmarkSuite extends DefaultBenchmarkSuite { copy = new int[length]; } - class ArraysSortBenchmark extends Benchmark { - public Object run(int trials) throws Exception { - int result = 0; - for (int i = 0; i < trials; i++) { - System.arraycopy(values, 0, copy, 0, values.length); - Arrays.sort(copy); - result ^= copy[0]; - } - return result; + public int timeSort(int reps) { + int dummy = 0; + for (int i = 0; i < reps; i++) { + System.arraycopy(values, 0, copy, 0, values.length); + Arrays.sort(copy); + dummy ^= copy[0]; } + return dummy; } enum Distribution { @@ -109,7 +106,7 @@ public class SortBenchmarkSuite extends DefaultBenchmarkSuite { abstract int[] create(int length); } - public static void main(String[] args) { - Runner.main(SortBenchmarkSuite.class, args); + public static void main(String[] args) throws Exception { + Runner.main(ArraySortBenchmark.class, args); } } diff --git a/test/com/google/caliper/examples/BoxedDoubleToStringBenchmark.java b/test/com/google/caliper/examples/BoxedDoubleToStringBenchmark.java new file mode 100644 index 0000000..5e6cbfa --- /dev/null +++ b/test/com/google/caliper/examples/BoxedDoubleToStringBenchmark.java @@ -0,0 +1,80 @@ +/* + * 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.examples; + +import com.google.caliper.SimpleBenchmark; +import com.google.caliper.Param; +import com.google.caliper.Runner; + +import java.util.Arrays; +import java.util.Collection; + +/** + * Measures the various ways the JDK converts boxed Doubles to Strings. + */ +public class BoxedDoubleToStringBenchmark extends SimpleBenchmark { + + @Param private Double d; + + private static final Collection<Double> dValues = Arrays.asList( + Math.PI, + -0.0d, + Double.NEGATIVE_INFINITY, + Double.NaN + ); + + public int timeStringFormat(int reps) { + Double value = d; + int dummy = 0; + for (int i = 0; i < reps; i++) { + dummy += String.format("%f", value).length(); + } + return dummy; + } + + public int timeToString(int reps) { + Double value = d; + int dummy = 0; + for (int i = 0; i < reps; i++) { + dummy += value.toString().length(); + } + return dummy; + } + + public int timeStringValueOf(int reps) { + Double value = d; + int dummy = 0; + for (int i = 0; i < reps; i++) { + dummy += String.valueOf(value).length(); + } + return dummy; + } + + public int timeQuoteTrick(int reps) { + Double value = d; + int dummy = 0; + for (int i = 0; i < reps; i++) { + dummy = ("" + value).length(); + } + return dummy; + } + + // TODO: remove this from all examples when IDE plugins are ready + public static void main(String[] args) throws Exception { + Runner.main(BoxedDoubleToStringBenchmark.class, args); + } +} diff --git a/test/com/google/caliper/examples/CharacterBenchmark.java b/test/com/google/caliper/examples/CharacterBenchmark.java new file mode 100644 index 0000000..3ffeb01 --- /dev/null +++ b/test/com/google/caliper/examples/CharacterBenchmark.java @@ -0,0 +1,173 @@ +/* + * 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.examples; + +import com.google.caliper.Benchmark; +import com.google.caliper.Param; +import com.google.caliper.Runner; +import com.google.caliper.SimpleBenchmark; + +import java.util.Collection; +import java.util.EnumSet; + +/** + * Tests various Character methods, intended for testing multiple + * implementations against each other. + */ +public class CharacterBenchmark extends SimpleBenchmark { + + @Param CharacterSet characterSet; + static Collection<CharacterSet> characterSetValues = EnumSet.allOf(CharacterSet.class); + + char[] values; + + @Override protected void setUp() throws Exception { + values = characterSet.chars; + } + + enum CharacterSet { + ASCII(128), + UNICODE(65536); + char[] chars; + CharacterSet(int size) { + chars = new char[size]; + for (int i = 0; i < chars.length; ++i) { + chars[i] = (char) i; + } + } + } + + public void timeDigit(int reps) { + for (int i = 0; i < reps; ++i) { + for (char ch = 0; ch < '}'; ++ch) { + Character.digit(ch, 10); + } + } + } + + public void timeGetNumericValue(int reps) { + for (int i = 0; i < reps; ++i) { + for (char ch = 0; ch < '}'; ++ch) { + Character.getNumericValue(ch); + } + } + } + + public void timeIsDigit(int reps) { + for (int i = 0; i < reps; ++i) { + for (char ch = 0; ch < '}'; ++ch) { + Character.isDigit(ch); + } + } + } + + public void timeIsIdentifierIgnorable(int reps) { + for (int i = 0; i < reps; ++i) { + for (char ch = 0; ch < '}'; ++ch) { + Character.isIdentifierIgnorable(ch); + } + } + } + + public void timeIsJavaIdentifierPart(int reps) { + for (int i = 0; i < reps; ++i) { + for (char ch = 0; ch < '}'; ++ch) { + Character.isJavaIdentifierPart(ch); + } + } + } + + public void timeIsJavaIdentifierStart(int reps) { + for (int i = 0; i < reps; ++i) { + for (char ch = 0; ch < '}'; ++ch) { + Character.isJavaIdentifierStart(ch); + } + } + } + + public void timeIsLetter(int reps) { + for (int i = 0; i < reps; ++i) { + for (char ch = 0; ch < '}'; ++ch) { + Character.isLetter(ch); + } + } + } + + public void timeIsLetterOrDigit(int reps) { + for (int i = 0; i < reps; ++i) { + for (char ch = 0; ch < '}'; ++ch) { + Character.isLetterOrDigit(ch); + } + } + } + + public void timeIsLowerCase(int reps) { + for (int i = 0; i < reps; ++i) { + for (char ch = 0; ch < '}'; ++ch) { + Character.isLowerCase(ch); + } + } + } + + public void timeIsSpaceChar(int reps) { + for (int i = 0; i < reps; ++i) { + for (char ch = 0; ch < '}'; ++ch) { + Character.isSpaceChar(ch); + } + } + } + + public void timeIsUpperCase(int reps) { + for (int i = 0; i < reps; ++i) { + for (char ch = 0; ch < '}'; ++ch) { + Character.isUpperCase(ch); + } + } + } + + public void timeIsWhitespace(int reps) { + for (int i = 0; i < reps; ++i) { + for (char ch = 0; ch < '}'; ++ch) { + Character.isWhitespace(ch); + } + } + } + + public void timeIsNull(int reps) { + for (int i = 0; i < reps; ++i) { + for (char ch = 0; ch < '}'; ++ch) { + boolean b = (ch == ' '); + } + } + } + + public void timeToLowerCase(int reps) { + for (int i = 0; i < reps; ++i) { + for (char ch = 0; ch < '}'; ++ch) { + Character.toLowerCase(ch); + } + } + } + + public void timeToUpperCase(int reps) { + for (int i = 0; i < reps; ++i) { + for (char ch = 0; ch < '}'; ++ch) { + Character.toUpperCase(ch); + } + } + } +} diff --git a/test/com/google/caliper/examples/DoubleToStringBenchmarkSuite.java b/test/com/google/caliper/examples/DoubleToStringBenchmarkSuite.java deleted file mode 100644 index a5d5234..0000000 --- a/test/com/google/caliper/examples/DoubleToStringBenchmarkSuite.java +++ /dev/null @@ -1,77 +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.examples; - -import com.google.caliper.Benchmark; -import com.google.caliper.DefaultBenchmarkSuite; -import com.google.caliper.Param; -import com.google.caliper.Runner; - -import java.util.Arrays; -import java.util.Collection; - -/** - * Measures the various ways the JDK converts doubles to Strings. - */ -public class DoubleToStringBenchmarkSuite extends DefaultBenchmarkSuite { - - @Param private Double d; - - private static Collection<Double> dValues = Arrays.asList( - Math.PI, - -0.0d, - Double.NEGATIVE_INFINITY, - Double.NaN - ); - - class FormatterBenchmark extends Benchmark { - public Object run(int trials) { - Double value = d; - String result = null; - for (int i = 0; i < trials; i++) { - result = String.format("%f", value); - } - return result; - } - } - - class ToStringBenchmark extends Benchmark { - public Object run(int trials) { - Double value = d; - String result = null; - for (int i = 0; i < trials; i++) { - result = value.toString(); - } - return result; - } - } - - class ConcatenationBenchmark extends Benchmark { - public Object run(int trials) { - Double value = d; - String result = null; - for (int i = 0; i < trials; i++) { - result = "" + value; - } - return result; - } - } - - public static void main(String[] args) { - Runner.main(DoubleToStringBenchmarkSuite.class, args); - } -} diff --git a/test/com/google/caliper/examples/EnumSetContainsBenchmarkSuite.java b/test/com/google/caliper/examples/EnumSetContainsBenchmark.java index da8f0f5..a9f6f2f 100644 --- a/test/com/google/caliper/examples/EnumSetContainsBenchmarkSuite.java +++ b/test/com/google/caliper/examples/EnumSetContainsBenchmark.java @@ -16,10 +16,9 @@ package com.google.caliper.examples; -import com.google.caliper.Benchmark; -import com.google.caliper.DefaultBenchmarkSuite; import com.google.caliper.Param; import com.google.caliper.Runner; +import com.google.caliper.SimpleBenchmark; import java.util.Collection; import java.util.EnumSet; @@ -28,11 +27,11 @@ import java.util.Set; /** * Measures EnumSet#contains(). */ -public class EnumSetContainsBenchmarkSuite extends DefaultBenchmarkSuite { +public class EnumSetContainsBenchmark extends SimpleBenchmark { @Param private SetMaker setMaker; - private static Collection<SetMaker> setMakerValues = EnumSet.allOf(SetMaker.class); + private static final Collection<SetMaker> setMakerValues = EnumSet.allOf(SetMaker.class); enum SetMaker { ENUM_SET { @@ -85,19 +84,15 @@ public class EnumSetContainsBenchmarkSuite extends DefaultBenchmarkSuite { this.testValues = setMaker.testValues(); } - class ContainsBenchmark extends Benchmark { - @Override public Object run(int trials) throws Exception { - int count = 0; - for (int i = 0; i < trials; i++) { - for (Object value : testValues) { - count ^= (set.contains(value) ? i : 0); - } - } - return count > 0; + public int timeContains(int reps) throws Exception { + int dummy = 0; + for (int i = 0; i < reps; i++) { + dummy ^= (set.contains(testValues[i % testValues.length]) ? i : 0); } + return dummy; } - public static void main(String[] args) { - Runner.main(EnumSetContainsBenchmarkSuite.class, args); + public static void main(String[] args) throws Exception { + Runner.main(EnumSetContainsBenchmark.class, args); } }
\ No newline at end of file diff --git a/test/com/google/caliper/examples/ExpensiveObjectsBenchmark.java b/test/com/google/caliper/examples/ExpensiveObjectsBenchmark.java new file mode 100644 index 0000000..2dbcf58 --- /dev/null +++ b/test/com/google/caliper/examples/ExpensiveObjectsBenchmark.java @@ -0,0 +1,71 @@ +/* + * 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.examples; + +import com.google.caliper.Benchmark; +import com.google.caliper.Param; +import com.google.caliper.Runner; +import com.google.caliper.SimpleBenchmark; + +import java.text.DecimalFormatSymbols; +import java.text.NumberFormat; +import java.text.SimpleDateFormat; +import java.util.Locale; + +/** + * Benchmarks creation and cloning various expensive objects. + */ +public class ExpensiveObjectsBenchmark extends SimpleBenchmark { + public void timeNewDecimalFormatSymbols(int reps) { + for (int i = 0; i < reps; ++i) { + new DecimalFormatSymbols(Locale.US); + } + } + + public void timeClonedDecimalFormatSymbols(int reps) { + DecimalFormatSymbols dfs = new DecimalFormatSymbols(Locale.US); + for (int i = 0; i < reps; ++i) { + dfs.clone(); + } + } + + public void timeNewNumberFormat(int reps) { + for (int i = 0; i < reps; ++i) { + NumberFormat.getInstance(Locale.US); + } + } + + public void timeClonedNumberFormat(int reps) { + NumberFormat nf = NumberFormat.getInstance(Locale.US); + for (int i = 0; i < reps; ++i) { + nf.clone(); + } + } + + public void timeNewSimpleDateFormat(int reps) { + for (int i = 0; i < reps; ++i) { + new SimpleDateFormat(); + } + } + + public void timeClonedSimpleDateFormat(int reps) { + SimpleDateFormat sdf = new SimpleDateFormat(); + for (int i = 0; i < reps; ++i) { + sdf.clone(); + } + } +} diff --git a/test/com/google/caliper/examples/FormatterBenchmark.java b/test/com/google/caliper/examples/FormatterBenchmark.java new file mode 100644 index 0000000..f61f111 --- /dev/null +++ b/test/com/google/caliper/examples/FormatterBenchmark.java @@ -0,0 +1,77 @@ +/* + * 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.examples; + +import com.google.caliper.Runner; +import com.google.caliper.SimpleBenchmark; + +import java.util.Formatter; + +/** + * Compares Formatter against hand-written StringBuilder code. + */ +public class FormatterBenchmark extends SimpleBenchmark { + public void timeFormatter_NoFormatting(int reps) { + for (int i = 0; i < reps; i++) { + Formatter f = new Formatter(); + f.format("this is a reasonably short string that doesn't actually need any formatting"); + } + } + + public void timeStringBuilder_NoFormatting(int reps) { + for (int i = 0; i < reps; i++) { + StringBuilder sb = new StringBuilder(); + sb.append("this is a reasonably short string that doesn't actually need any formatting"); + } + } + + public void timeFormatter_OneInt(int reps) { + for (int i = 0; i < reps; i++) { + Formatter f = new Formatter(); + f.format("this is a reasonably short string that has an int %d in it", i); + } + } + + public void timeStringBuilder_OneInt(int reps) { + for (int i = 0; i < reps; i++) { + StringBuilder sb = new StringBuilder(); + sb.append("this is a reasonably short string that has an int "); + sb.append(i); + sb.append(" in it"); + } + } + + public void timeFormatter_OneString(int reps) { + for (int i = 0; i < reps; i++) { + Formatter f = new Formatter(); + f.format("this is a reasonably short string that has a string %s in it", "hello"); + } + } + + public void timeStringBuilder_OneString(int reps) { + for (int i = 0; i < reps; i++) { + StringBuilder sb = new StringBuilder(); + sb.append("this is a reasonably short string that has a string "); + sb.append("hello"); + sb.append(" in it"); + } + } + + public static void main(String[] args) throws Exception { + Runner.main(FormatterBenchmark.class, args); + } +} diff --git a/test/com/google/caliper/examples/FormatterBenchmarkSuite.java b/test/com/google/caliper/examples/FormatterBenchmarkSuite.java deleted file mode 100644 index 53dff71..0000000 --- a/test/com/google/caliper/examples/FormatterBenchmarkSuite.java +++ /dev/null @@ -1,98 +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.examples; - -import com.google.caliper.Benchmark; -import com.google.caliper.DefaultBenchmarkSuite; -import com.google.caliper.Param; -import com.google.caliper.Runner; - -import java.util.Formatter; - -/** - * Compares Formatter against hand-written StringBuilder code. - */ -public class FormatterBenchmarkSuite extends DefaultBenchmarkSuite { - - class Formatter_NoFormatting extends Benchmark { - public Object run(int trials) { - for (int i = 0; i < trials; i++) { - Formatter f = new Formatter(); - f.format("this is a reasonably short string that doesn't actually need any formatting"); - } - return null; - } - } - - class StringBuilder_NoFormatting extends Benchmark { - public Object run(int trials) { - for (int i = 0; i < trials; i++) { - StringBuilder sb = new StringBuilder(); - sb.append("this is a reasonably short string that doesn't actually need any formatting"); - } - return null; - } - } - - class Formatter_OneInt extends Benchmark { - public Object run(int trials) { - for (int i = 0; i < trials; i++) { - Formatter f = new Formatter(); - f.format("this is a reasonably short string that has an int %d in it", i); - } - return null; - } - } - - class StringBuilder_OneInt extends Benchmark { - public Object run(int trials) { - for (int i = 0; i < trials; i++) { - StringBuilder sb = new StringBuilder(); - sb.append("this is a reasonably short string that has an int "); - sb.append(i); - sb.append(" in it"); - } - return null; - } - } - - class Formatter_OneString extends Benchmark { - public Object run(int trials) { - for (int i = 0; i < trials; i++) { - Formatter f = new Formatter(); - f.format("this is a reasonably short string that has a string %s in it", "hello"); - } - return null; - } - } - - class StringBuilder_OneString extends Benchmark { - public Object run(int trials) { - for (int i = 0; i < trials; i++) { - StringBuilder sb = new StringBuilder(); - sb.append("this is a reasonably short string that has a string "); - sb.append("hello"); - sb.append(" in it"); - } - return null; - } - } - - public static void main(String[] args) { - Runner.main(FormatterBenchmarkSuite.class, args); - } -} diff --git a/test/com/google/caliper/examples/IntModBenchmark.java b/test/com/google/caliper/examples/IntModBenchmark.java new file mode 100644 index 0000000..86e85e7 --- /dev/null +++ b/test/com/google/caliper/examples/IntModBenchmark.java @@ -0,0 +1,90 @@ +/* + * 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.examples; + +import com.google.caliper.Runner; +import com.google.caliper.SimpleBenchmark; + +/** + * Measures several candidate implementations for mod(). + */ +public class IntModBenchmark extends SimpleBenchmark { + private static final int M = (1 << 16) - 1; + + public int timeConditional(int reps) { + int dummy = 5; + for (int i = 0; i < reps; i++) { + dummy += Integer.MAX_VALUE + conditionalMod(dummy, M); + } + return dummy; + } + + private static int conditionalMod(int a, int m) { + int r = a % m; + return r < 0 ? r + m : r; + } + + public int timeDoubleRemainder(int reps) { + int dummy = 5; + for (int i = 0; i < reps; i++) { + dummy += Integer.MAX_VALUE + doubleRemainderMod(dummy, M); + } + return dummy; + } + + private static int doubleRemainderMod(int a, int m) { + return (int) (((a % m) + (long) m) % m); + } + + public int timeRightShiftingMod(int reps) { + int dummy = 5; + for (int i = 0; i < reps; i++) { + dummy += Integer.MAX_VALUE + rightShiftingMod(dummy, M); + } + return dummy; + } + + private static int rightShiftingMod(int a, int m) { + long r = a % m; + return (int) (r + ((r >> 63) & m)); + } + + public int timeLeftShiftingMod(int reps) { + int dummy = 5; + for (int i = 0; i < reps; i++) { + dummy += Integer.MAX_VALUE + leftShiftingMod(dummy, M); + } + return dummy; + } + + private static int leftShiftingMod(int a, int m) { + return (int) ((a + (((long) m) << 32)) % m); + } + + public int timeWrongMod(int reps) { + int dummy = 5; + for (int i = 0; i < reps; i++) { + dummy += Integer.MAX_VALUE + dummy % M; + } + return dummy; + } + + // TODO: remove this from all examples when IDE plugins are ready + public static void main(String[] args) throws Exception { + Runner.main(IntModBenchmark.class, args); + } +}
\ No newline at end of file diff --git a/test/com/google/caliper/examples/ListIterationBenchmarkSuite.java b/test/com/google/caliper/examples/ListIterationBenchmark.java index 0ed7197..53bcdf8 100644 --- a/test/com/google/caliper/examples/ListIterationBenchmarkSuite.java +++ b/test/com/google/caliper/examples/ListIterationBenchmark.java @@ -16,26 +16,27 @@ package com.google.caliper.examples; -import com.google.caliper.Benchmark; -import com.google.caliper.DefaultBenchmarkSuite; import com.google.caliper.Param; import com.google.caliper.Runner; +import com.google.caliper.SimpleBenchmark; -import java.util.*; +import java.util.AbstractList; +import java.util.Arrays; +import java.util.Collection; +import java.util.List; /** * Measures iterating through list elements. */ -public class ListIterationBenchmarkSuite extends DefaultBenchmarkSuite { - +public class ListIterationBenchmark extends SimpleBenchmark { @Param private int length; - private static Collection<Integer> lengthValues = Arrays.asList(0, 10, 100, 1000); + private static final Collection<Integer> lengthValues = Arrays.asList(0, 10, 100, 1000); private List<Object> list; private Object[] array; - @Override protected void setUp() throws Exception { + @Override protected void setUp() { array = new Object[length]; for (int i = 0; i < length; i++) { array[i] = new Object(); @@ -52,31 +53,28 @@ public class ListIterationBenchmarkSuite extends DefaultBenchmarkSuite { }; } - class ListIterateBenchmark extends Benchmark { - @Override public Object run(int trials) throws Exception { - int count = 0; - for (int i = 0; i < trials; i++) { - for (Object value : list) { - count ^= (value == Boolean.TRUE) ? i : 0; - } + public int timeListIteration(int reps) { + int count = 0; + for (int i = 0; i < reps; i++) { + for (Object value : list) { + count ^= value.hashCode(); // prevent overoptimization } - return count > 0; } + return count; // ignored } - class ArrayIterateBenchmark extends Benchmark { - @Override public Object run(int trials) throws Exception { - int count = 0; - for (int i = 0; i < trials; i++) { - for (Object value : array) { - count ^= (value == Boolean.TRUE) ? i : 0; - } + public int timeArrayIteration(int reps) { + int count = 0; + for (int i = 0; i < reps; i++) { + for (Object value : array) { + count ^= value.hashCode(); // prevent overoptimization } - return count > 0; } + return count; // ignored } - public static void main(String[] args) { - Runner.main(ListIterationBenchmarkSuite.class, args); + // TODO: remove this from all examples when IDE plugins are ready + public static void main(String[] args) throws Exception { + Runner.main(ListIterationBenchmark.class, args); } }
\ No newline at end of file diff --git a/test/com/google/caliper/examples/PrimitiveDoubleToStringBenchmark.java b/test/com/google/caliper/examples/PrimitiveDoubleToStringBenchmark.java new file mode 100644 index 0000000..592acdc --- /dev/null +++ b/test/com/google/caliper/examples/PrimitiveDoubleToStringBenchmark.java @@ -0,0 +1,80 @@ +/* + * 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.examples; + +import com.google.caliper.SimpleBenchmark; +import com.google.caliper.Param; +import com.google.caliper.Runner; + +import java.util.Arrays; +import java.util.Collection; + +/** + * Measures the various ways the JDK converts primitive doubles to Strings. + */ +public class PrimitiveDoubleToStringBenchmark extends SimpleBenchmark { + + @Param private double d; + + private static final Collection<Double> dValues = Arrays.asList( + Math.PI, + -0.0d, + Double.NEGATIVE_INFINITY, + Double.NaN + ); + + public int timeStringFormat(int reps) { + double value = d; + int dummy = 0; + for (int i = 0; i < reps; i++) { + dummy += String.format("%f", value).length(); + } + return dummy; + } + + public int timeToString(int reps) { + double value = d; + int dummy = 0; + for (int i = 0; i < reps; i++) { + dummy += ((Double) value).toString().length(); + } + return dummy; + } + + public int timeStringValueOf(int reps) { + double value = d; + int dummy = 0; + for (int i = 0; i < reps; i++) { + dummy += String.valueOf(value).length(); + } + return dummy; + } + + public int timeQuoteTrick(int reps) { + double value = d; + int dummy = 0; + for (int i = 0; i < reps; i++) { + dummy = ("" + value).length(); + } + return dummy; + } + + // TODO: remove this from all examples when IDE plugins are ready + public static void main(String[] args) throws Exception { + Runner.main(PrimitiveDoubleToStringBenchmark.class, args); + } +}
\ No newline at end of file diff --git a/test/com/google/caliper/examples/StringBuilderBenchmark.java b/test/com/google/caliper/examples/StringBuilderBenchmark.java new file mode 100644 index 0000000..2fa6819 --- /dev/null +++ b/test/com/google/caliper/examples/StringBuilderBenchmark.java @@ -0,0 +1,132 @@ +/* + * 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.examples; + +import com.google.caliper.Benchmark; +import com.google.caliper.Param; +import com.google.caliper.Runner; +import com.google.caliper.SimpleBenchmark; + +import java.util.Arrays; +import java.util.Collection; + +/** + * Tests the performance of various StringBuilder methods. + */ +public class StringBuilderBenchmark extends SimpleBenchmark { + + @Param int length; + static Collection<Integer> lengthValues = Arrays.asList(1, 10, 100); + + public void timeAppendBoolean(int reps) { + for (int i = 0; i < reps; ++i) { + StringBuilder sb = new StringBuilder(); + for (int j = 0; j < length; ++j) { + sb.append(true); + } + } + } + + public void timeAppendChar(int reps) { + for (int i = 0; i < reps; ++i) { + StringBuilder sb = new StringBuilder(); + for (int j = 0; j < length; ++j) { + sb.append('c'); + } + } + } + + public void timeAppendCharArray(int reps) { + char[] chars = "chars".toCharArray(); + for (int i = 0; i < reps; ++i) { + StringBuilder sb = new StringBuilder(); + for (int j = 0; j < length; ++j) { + sb.append(chars); + } + } + } + + public void timeAppendCharSequence(int reps) { + CharSequence cs = "chars"; + for (int i = 0; i < reps; ++i) { + StringBuilder sb = new StringBuilder(); + for (int j = 0; j < length; ++j) { + sb.append(cs); + } + } + } + + public void timeAppendDouble(int reps) { + double d = 1.2; + for (int i = 0; i < reps; ++i) { + StringBuilder sb = new StringBuilder(); + for (int j = 0; j < length; ++j) { + sb.append(d); + } + } + } + + public void timeAppendFloat(int reps) { + float f = 1.2f; + for (int i = 0; i < reps; ++i) { + StringBuilder sb = new StringBuilder(); + for (int j = 0; j < length; ++j) { + sb.append(f); + } + } + } + + public void timeAppendInt(int reps) { + int n = 123; + for (int i = 0; i < reps; ++i) { + StringBuilder sb = new StringBuilder(); + for (int j = 0; j < length; ++j) { + sb.append(n); + } + } + } + + public void timeAppendLong(int reps) { + long l = 123; + for (int i = 0; i < reps; ++i) { + StringBuilder sb = new StringBuilder(); + for (int j = 0; j < length; ++j) { + sb.append(l); + } + } + } + + public void timeAppendObject(int reps) { + Object o = new Object(); + for (int i = 0; i < reps; ++i) { + StringBuilder sb = new StringBuilder(); + for (int j = 0; j < length; ++j) { + sb.append(o); + } + } + } + + public void timeAppendString(int reps) { + String s = "chars"; + for (int i = 0; i < reps; ++i) { + StringBuilder sb = new StringBuilder(); + for (int j = 0; j < length; ++j) { + sb.append(s); + } + } + } +} |