Java程序  |  189行  |  5.97 KB

/*
 * 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 tutorial;

import com.google.caliper.BeforeExperiment;
import com.google.caliper.Benchmark;
import com.google.caliper.Param;

/**
 * Caliper tutorial. To run the example benchmarks in this file:
 * {@code CLASSPATH=... [caliper_home]/caliper tutorial.Tutorial.Benchmark1}
 */
public class Tutorial {

  /*
   * We begin the Caliper tutorial with the simplest benchmark you can write.
   * We'd like to know how efficient the method System.nanoTime() is.
   *
   * Notice:
   *
   *  - We write a class that extends com.google.caliper.Benchmark.
   *  - It contains a public instance method whose name begins with 'time' and
   *    which accepts a single 'int reps' parameter.
   *  - The body of the method simply executes the code we wish to measure,
   *    'reps' times.
   *
   * Example run:
   *
   *    $ CLASSPATH=build/classes/test caliper tutorial.Tutorial.Benchmark1
   *    [real-time results appear on this line]
   *
   *    Summary report for tutorial.Tutorial$Benchmark1:
   *
   *    Benchmark   ns
   *    ---------  ---
   *    NanoTime   233
   */
  public static class Benchmark1 {
    @Benchmark void timeNanoTime(int reps) {
      for (int i = 0; i < reps; i++) {
        System.nanoTime();
      }
    }
  }

  /*
   * Now let's compare two things: nanoTime() versus currentTimeMillis().
   * Notice:
   *
   *  - We simply add another method, following the same rules as the first.
   *
   * Example run output:
   *
   *   Benchmark           ns
   *   -----------------  ---
   *   NanoTime           248
   *   CurrentTimeMillis  118
   */
  public static class Benchmark2 {
    @Benchmark void timeNanoTime(int reps) {
      for (int i = 0; i < reps; i++) {
        System.nanoTime();
      }
    }
    @Benchmark void timeCurrentTimeMillis(int reps) {
      for (int i = 0; i < reps; i++) {
        System.currentTimeMillis();
      }
    }
  }

  /*
   * Let's try iterating over a large array. This seems simple enough, but
   * there is a problem!
   */
  public static class Benchmark3 {
    private final int[] array = new int[1000000];

    @SuppressWarnings("UnusedDeclaration") // IDEA tries to warn us!
    @Benchmark void timeArrayIteration_BAD(int reps) {
      for (int i = 0; i < reps; i++) {
        for (int ignoreMe : array) {}
      }
    }
  }

  /*
   * Caliper reported that the benchmark above ran in 4 nanoseconds.
   *
   * Wait, what?
   *
   * How can it possibly iterate over a million zeroes in 4 ns!?
   *
   * It is very important to sanity-check benchmark results with common sense!
   * In this case, we're indeed getting a bogus result. The problem is that the
   * Java Virtual Machine is too smart: it detected the fact that the loop was
   * producing no actual result, so it simply compiled it right out. The method
   * never looped at all. To fix this, we need to use a dummy result value.
   *
   * Notice:
   *
   *  - We simply change the 'time' method from 'void' to any return type we
   *    wish. Then we return a value that can't be known without actually
   *    performing the work, and thus we defeat the runtime optimizations.
   *  - We're no longer timing *just* the code we want to be testing - our
   *    result will now be inflated by the (small) cost of addition. This is an
   *    unfortunate fact of life with microbenchmarking. In fact, we were
   *    already inflated by the cost of an int comparison, "i < reps" as it was.
   *
   * With this change, Caliper should report a much more realistic value, more
   * on the order of an entire millisecond.
   */
  public static class Benchmark4 {
    private final int[] array = new int[1000000];

    @Benchmark int timeArrayIteration_fixed(int reps) {
      int dummy = 0;
      for (int i = 0; i < reps; i++) {
        for (int doNotIgnoreMe : array) {
          dummy += doNotIgnoreMe;
        }
      }
      return dummy; // framework ignores this, but it has served its purpose!
    }
  }

  /*
   * Now we'd like to know how various other *sizes* of arrays perform. We
   * don't want to have to cut and paste the whole benchmark just to provide a
   * different size. What we need is a parameter!
   *
   * When you run this benchmark the same way you ran the previous ones, you'll
   * now get an error: "No values provided for benchmark parameter 'size'".
   * You can provide the value requested at the command line like this:
   *
   *   [caliper_home]/caliper tutorial.Tutorial.Benchmark5 -Dsize=100}
   *
   * You'll see output like this:
   *
   *   Benchmark       size   ns
   *   --------------  ----  ---
   *   ArrayIteration   100   51
   *
   * Now that we've parameterized our benchmark, things are starting to get fun.
   * Try passing '-Dsize=10,100,1000' and see what happens!
   *
   *   Benchmark       size   ns
   *   --------------  ----  -----------------------------------
   *   ArrayIteration    10    7 |
   *   ArrayIteration   100   49 ||||
   *   ArrayIteration  1000  477 ||||||||||||||||||||||||||||||
   *
   */
  public static class Benchmark5 {
    @Param int size; // set automatically by framework

    private int[] array; // set by us, in setUp()

    @BeforeExperiment void setUp() {
      // @Param values are guaranteed to have been injected by now
      array = new int[size];
    }

    @Benchmark int timeArrayIteration(int reps) {
      int dummy = 0;
      for (int i = 0; i < reps; i++) {
        for (int doNotIgnoreMe : array) {
          dummy += doNotIgnoreMe;
        }
      }
      return dummy;
    }
  }
}