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.
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 {
 
-  @Override public String toString() {
-    return getClass().getSimpleName();
-  }
-}
+  Set<String> parameterNames();
+
+  Set<String> parameterValues(String parameterName);
+
+  TimedRunnable createBenchmark(Map<String, String> parameterValues);
+}
\ No newline at end of file
diff --git a/src/com/google/caliper/BenchmarkSuite.java b/src/com/google/caliper/BenchmarkSuite.java
deleted file mode 100644
index 40106a0..0000000
--- a/src/com/google/caliper/BenchmarkSuite.java
+++ /dev/null
@@ -1,35 +0,0 @@
-/*
- * Copyright (C) 2009 Google Inc.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.google.caliper;
-
-import java.util.Map;
-import java.util.Set;
-
-/**
- * A collection of benchmarks that share a set of configuration parameters.
- */
-public abstract class BenchmarkSuite {
-
-  protected abstract Set<Class<? extends Benchmark>> benchmarkClasses();
-
-  protected abstract Set<String> parameterNames();
-
-  protected abstract Set<String> parameterValues(String parameterName);
-
-  protected abstract Benchmark createBenchmark(
-      Class<? extends Benchmark> benchmark, 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 @@
     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 @@
    * 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 @@
 
 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 @@
 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 @@
       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 @@
 
     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 @@
     }
 
     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 @@
    * 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 @@
     }
     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 @@
    * 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.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.util.*;
 
 /**
- * A parameter in a {@link DefaultBenchmarkSuite}.
+ * A parameter in a {@link SimpleBenchmark}.
  */
 abstract class Parameter<T> {
 
@@ -33,7 +33,7 @@
   /**
    * 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 @@
   }
 
   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 @@
   /**
    * 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 @@
 
 import com.google.common.collect.ImmutableMap;
 
+import java.lang.reflect.Method;
 import java.util.Map;
 
 /**
@@ -26,13 +27,9 @@
 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 @@
     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 @@
 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 @@
   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 @@
   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 @@
   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 @@
       }
 
       // 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 @@
 
   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 @@
     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 @@
         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 @@
     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 @@
 
       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 @@
     }
   }
 
-  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 @@
           System.out.println("Too many benchmark classes!");
           return false;
         }
-
         suiteClassName = args[i];
-
       }
     }
 
@@ -388,8 +351,6 @@
     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 @@
     // 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 @@
     }
 
     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/TimedRunnable.java b/src/com/google/caliper/TimedRunnable.java
new file mode 100644
index 0000000..d1f9a6c
--- /dev/null
+++ b/src/com/google/caliper/TimedRunnable.java
@@ -0,0 +1,29 @@
+/**
+ * Copyright (C) 2009 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.google.caliper;
+
+public interface TimedRunnable {
+
+  /**
+   * Runs the benchmark through {@code trials} iterations.
+   *
+   * @return any object or null. Benchmark implementors may keep an accumulating
+   *      value to prevent the runtime from optimizing away the code under test.
+   *      Such an accumulator value can be returned here.
+   */
+  Object run(int reps) throws Exception;
+}
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 @@
 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
similarity index 78%
rename from test/com/google/caliper/examples/SortBenchmarkSuite.java
rename to 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 @@
 
   @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 @@
     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 @@
     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
similarity index 80%
rename from test/com/google/caliper/examples/EnumSetContainsBenchmarkSuite.java
rename to 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 @@
 /**
  * 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 @@
     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/ListIterationBenchmark.java b/test/com/google/caliper/examples/ListIterationBenchmark.java
new file mode 100644
index 0000000..53bcdf8
--- /dev/null
+++ b/test/com/google/caliper/examples/ListIterationBenchmark.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.Param;
+import com.google.caliper.Runner;
+import com.google.caliper.SimpleBenchmark;
+
+import java.util.AbstractList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.List;
+
+/**
+ * Measures iterating through list elements.
+ */
+public class ListIterationBenchmark extends SimpleBenchmark {
+  @Param private int length;
+
+  private static final Collection<Integer> lengthValues = Arrays.asList(0, 10, 100, 1000);
+
+  private List<Object> list;
+  private Object[] array;
+
+  @Override protected void setUp() {
+    array = new Object[length];
+    for (int i = 0; i < length; i++) {
+      array[i] = new Object();
+    }
+
+    list = new AbstractList<Object>() {
+      @Override public int size() {
+        return length;
+      }
+
+      @Override public Object get(int i) {
+        return array[i];
+      }
+    };
+  }
+
+  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; // ignored
+  }
+
+  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; // ignored
+  }
+
+  // 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/ListIterationBenchmarkSuite.java b/test/com/google/caliper/examples/ListIterationBenchmarkSuite.java
deleted file mode 100644
index 0ed7197..0000000
--- a/test/com/google/caliper/examples/ListIterationBenchmarkSuite.java
+++ /dev/null
@@ -1,82 +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.*;
-
-/**
- * Measures iterating through list elements.
- */
-public class ListIterationBenchmarkSuite extends DefaultBenchmarkSuite {
-
-  @Param private int length;
-
-  private static Collection<Integer> lengthValues = Arrays.asList(0, 10, 100, 1000);
-
-  private List<Object> list;
-  private Object[] array;
-
-  @Override protected void setUp() throws Exception {
-    array = new Object[length];
-    for (int i = 0; i < length; i++) {
-      array[i] = new Object();
-    }
-
-    list = new AbstractList<Object>() {
-      @Override public int size() {
-        return length;
-      }
-
-      @Override public Object get(int i) {
-        return array[i];
-      }
-    };
-  }
-
-  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;
-        }
-      }
-      return count > 0;
-    }
-  }
-
-  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;
-        }
-      }
-      return count > 0;
-    }
-  }
-
-  public static void main(String[] args) {
-    Runner.main(ListIterationBenchmarkSuite.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);
+            }
+        }
+    }
+}