1*e1319447SKrzysztof Kosiński /* 2*e1319447SKrzysztof Kosiński * Copyright (C) 2009 Google Inc. 3*e1319447SKrzysztof Kosiński * 4*e1319447SKrzysztof Kosiński * Licensed under the Apache License, Version 2.0 (the "License"); 5*e1319447SKrzysztof Kosiński * you may not use this file except in compliance with the License. 6*e1319447SKrzysztof Kosiński * You may obtain a copy of the License at 7*e1319447SKrzysztof Kosiński * 8*e1319447SKrzysztof Kosiński * http://www.apache.org/licenses/LICENSE-2.0 9*e1319447SKrzysztof Kosiński * 10*e1319447SKrzysztof Kosiński * Unless required by applicable law or agreed to in writing, software 11*e1319447SKrzysztof Kosiński * distributed under the License is distributed on an "AS IS" BASIS, 12*e1319447SKrzysztof Kosiński * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13*e1319447SKrzysztof Kosiński * See the License for the specific language governing permissions and 14*e1319447SKrzysztof Kosiński * limitations under the License. 15*e1319447SKrzysztof Kosiński */ 16*e1319447SKrzysztof Kosiński 17*e1319447SKrzysztof Kosiński package tutorial; 18*e1319447SKrzysztof Kosiński 19*e1319447SKrzysztof Kosiński import com.google.caliper.BeforeExperiment; 20*e1319447SKrzysztof Kosiński import com.google.caliper.Benchmark; 21*e1319447SKrzysztof Kosiński import com.google.caliper.Param; 22*e1319447SKrzysztof Kosiński 23*e1319447SKrzysztof Kosiński /** 24*e1319447SKrzysztof Kosiński * Caliper tutorial. To run the example benchmarks in this file: 25*e1319447SKrzysztof Kosiński * {@code CLASSPATH=... [caliper_home]/caliper tutorial.Tutorial.Benchmark1} 26*e1319447SKrzysztof Kosiński */ 27*e1319447SKrzysztof Kosiński public class Tutorial { 28*e1319447SKrzysztof Kosiński 29*e1319447SKrzysztof Kosiński /* 30*e1319447SKrzysztof Kosiński * We begin the Caliper tutorial with the simplest benchmark you can write. 31*e1319447SKrzysztof Kosiński * We'd like to know how efficient the method System.nanoTime() is. 32*e1319447SKrzysztof Kosiński * 33*e1319447SKrzysztof Kosiński * Notice: 34*e1319447SKrzysztof Kosiński * 35*e1319447SKrzysztof Kosiński * - We write a class that extends com.google.caliper.Benchmark. 36*e1319447SKrzysztof Kosiński * - It contains a public instance method whose name begins with 'time' and 37*e1319447SKrzysztof Kosiński * which accepts a single 'int reps' parameter. 38*e1319447SKrzysztof Kosiński * - The body of the method simply executes the code we wish to measure, 39*e1319447SKrzysztof Kosiński * 'reps' times. 40*e1319447SKrzysztof Kosiński * 41*e1319447SKrzysztof Kosiński * Example run: 42*e1319447SKrzysztof Kosiński * 43*e1319447SKrzysztof Kosiński * $ CLASSPATH=build/classes/test caliper tutorial.Tutorial.Benchmark1 44*e1319447SKrzysztof Kosiński * [real-time results appear on this line] 45*e1319447SKrzysztof Kosiński * 46*e1319447SKrzysztof Kosiński * Summary report for tutorial.Tutorial$Benchmark1: 47*e1319447SKrzysztof Kosiński * 48*e1319447SKrzysztof Kosiński * Benchmark ns 49*e1319447SKrzysztof Kosiński * --------- --- 50*e1319447SKrzysztof Kosiński * NanoTime 233 51*e1319447SKrzysztof Kosiński */ 52*e1319447SKrzysztof Kosiński public static class Benchmark1 { timeNanoTime(int reps)53*e1319447SKrzysztof Kosiński @Benchmark void timeNanoTime(int reps) { 54*e1319447SKrzysztof Kosiński for (int i = 0; i < reps; i++) { 55*e1319447SKrzysztof Kosiński System.nanoTime(); 56*e1319447SKrzysztof Kosiński } 57*e1319447SKrzysztof Kosiński } 58*e1319447SKrzysztof Kosiński } 59*e1319447SKrzysztof Kosiński 60*e1319447SKrzysztof Kosiński /* 61*e1319447SKrzysztof Kosiński * Now let's compare two things: nanoTime() versus currentTimeMillis(). 62*e1319447SKrzysztof Kosiński * Notice: 63*e1319447SKrzysztof Kosiński * 64*e1319447SKrzysztof Kosiński * - We simply add another method, following the same rules as the first. 65*e1319447SKrzysztof Kosiński * 66*e1319447SKrzysztof Kosiński * Example run output: 67*e1319447SKrzysztof Kosiński * 68*e1319447SKrzysztof Kosiński * Benchmark ns 69*e1319447SKrzysztof Kosiński * ----------------- --- 70*e1319447SKrzysztof Kosiński * NanoTime 248 71*e1319447SKrzysztof Kosiński * CurrentTimeMillis 118 72*e1319447SKrzysztof Kosiński */ 73*e1319447SKrzysztof Kosiński public static class Benchmark2 { timeNanoTime(int reps)74*e1319447SKrzysztof Kosiński @Benchmark void timeNanoTime(int reps) { 75*e1319447SKrzysztof Kosiński for (int i = 0; i < reps; i++) { 76*e1319447SKrzysztof Kosiński System.nanoTime(); 77*e1319447SKrzysztof Kosiński } 78*e1319447SKrzysztof Kosiński } timeCurrentTimeMillis(int reps)79*e1319447SKrzysztof Kosiński @Benchmark void timeCurrentTimeMillis(int reps) { 80*e1319447SKrzysztof Kosiński for (int i = 0; i < reps; i++) { 81*e1319447SKrzysztof Kosiński System.currentTimeMillis(); 82*e1319447SKrzysztof Kosiński } 83*e1319447SKrzysztof Kosiński } 84*e1319447SKrzysztof Kosiński } 85*e1319447SKrzysztof Kosiński 86*e1319447SKrzysztof Kosiński /* 87*e1319447SKrzysztof Kosiński * Let's try iterating over a large array. This seems simple enough, but 88*e1319447SKrzysztof Kosiński * there is a problem! 89*e1319447SKrzysztof Kosiński */ 90*e1319447SKrzysztof Kosiński public static class Benchmark3 { 91*e1319447SKrzysztof Kosiński private final int[] array = new int[1000000]; 92*e1319447SKrzysztof Kosiński 93*e1319447SKrzysztof Kosiński @SuppressWarnings("UnusedDeclaration") // IDEA tries to warn us! timeArrayIteration_BAD(int reps)94*e1319447SKrzysztof Kosiński @Benchmark void timeArrayIteration_BAD(int reps) { 95*e1319447SKrzysztof Kosiński for (int i = 0; i < reps; i++) { 96*e1319447SKrzysztof Kosiński for (int ignoreMe : array) {} 97*e1319447SKrzysztof Kosiński } 98*e1319447SKrzysztof Kosiński } 99*e1319447SKrzysztof Kosiński } 100*e1319447SKrzysztof Kosiński 101*e1319447SKrzysztof Kosiński /* 102*e1319447SKrzysztof Kosiński * Caliper reported that the benchmark above ran in 4 nanoseconds. 103*e1319447SKrzysztof Kosiński * 104*e1319447SKrzysztof Kosiński * Wait, what? 105*e1319447SKrzysztof Kosiński * 106*e1319447SKrzysztof Kosiński * How can it possibly iterate over a million zeroes in 4 ns!? 107*e1319447SKrzysztof Kosiński * 108*e1319447SKrzysztof Kosiński * It is very important to sanity-check benchmark results with common sense! 109*e1319447SKrzysztof Kosiński * In this case, we're indeed getting a bogus result. The problem is that the 110*e1319447SKrzysztof Kosiński * Java Virtual Machine is too smart: it detected the fact that the loop was 111*e1319447SKrzysztof Kosiński * producing no actual result, so it simply compiled it right out. The method 112*e1319447SKrzysztof Kosiński * never looped at all. To fix this, we need to use a dummy result value. 113*e1319447SKrzysztof Kosiński * 114*e1319447SKrzysztof Kosiński * Notice: 115*e1319447SKrzysztof Kosiński * 116*e1319447SKrzysztof Kosiński * - We simply change the 'time' method from 'void' to any return type we 117*e1319447SKrzysztof Kosiński * wish. Then we return a value that can't be known without actually 118*e1319447SKrzysztof Kosiński * performing the work, and thus we defeat the runtime optimizations. 119*e1319447SKrzysztof Kosiński * - We're no longer timing *just* the code we want to be testing - our 120*e1319447SKrzysztof Kosiński * result will now be inflated by the (small) cost of addition. This is an 121*e1319447SKrzysztof Kosiński * unfortunate fact of life with microbenchmarking. In fact, we were 122*e1319447SKrzysztof Kosiński * already inflated by the cost of an int comparison, "i < reps" as it was. 123*e1319447SKrzysztof Kosiński * 124*e1319447SKrzysztof Kosiński * With this change, Caliper should report a much more realistic value, more 125*e1319447SKrzysztof Kosiński * on the order of an entire millisecond. 126*e1319447SKrzysztof Kosiński */ 127*e1319447SKrzysztof Kosiński public static class Benchmark4 { 128*e1319447SKrzysztof Kosiński private final int[] array = new int[1000000]; 129*e1319447SKrzysztof Kosiński timeArrayIteration_fixed(int reps)130*e1319447SKrzysztof Kosiński @Benchmark int timeArrayIteration_fixed(int reps) { 131*e1319447SKrzysztof Kosiński int dummy = 0; 132*e1319447SKrzysztof Kosiński for (int i = 0; i < reps; i++) { 133*e1319447SKrzysztof Kosiński for (int doNotIgnoreMe : array) { 134*e1319447SKrzysztof Kosiński dummy += doNotIgnoreMe; 135*e1319447SKrzysztof Kosiński } 136*e1319447SKrzysztof Kosiński } 137*e1319447SKrzysztof Kosiński return dummy; // framework ignores this, but it has served its purpose! 138*e1319447SKrzysztof Kosiński } 139*e1319447SKrzysztof Kosiński } 140*e1319447SKrzysztof Kosiński 141*e1319447SKrzysztof Kosiński /* 142*e1319447SKrzysztof Kosiński * Now we'd like to know how various other *sizes* of arrays perform. We 143*e1319447SKrzysztof Kosiński * don't want to have to cut and paste the whole benchmark just to provide a 144*e1319447SKrzysztof Kosiński * different size. What we need is a parameter! 145*e1319447SKrzysztof Kosiński * 146*e1319447SKrzysztof Kosiński * When you run this benchmark the same way you ran the previous ones, you'll 147*e1319447SKrzysztof Kosiński * now get an error: "No values provided for benchmark parameter 'size'". 148*e1319447SKrzysztof Kosiński * You can provide the value requested at the command line like this: 149*e1319447SKrzysztof Kosiński * 150*e1319447SKrzysztof Kosiński * [caliper_home]/caliper tutorial.Tutorial.Benchmark5 -Dsize=100} 151*e1319447SKrzysztof Kosiński * 152*e1319447SKrzysztof Kosiński * You'll see output like this: 153*e1319447SKrzysztof Kosiński * 154*e1319447SKrzysztof Kosiński * Benchmark size ns 155*e1319447SKrzysztof Kosiński * -------------- ---- --- 156*e1319447SKrzysztof Kosiński * ArrayIteration 100 51 157*e1319447SKrzysztof Kosiński * 158*e1319447SKrzysztof Kosiński * Now that we've parameterized our benchmark, things are starting to get fun. 159*e1319447SKrzysztof Kosiński * Try passing '-Dsize=10,100,1000' and see what happens! 160*e1319447SKrzysztof Kosiński * 161*e1319447SKrzysztof Kosiński * Benchmark size ns 162*e1319447SKrzysztof Kosiński * -------------- ---- ----------------------------------- 163*e1319447SKrzysztof Kosiński * ArrayIteration 10 7 | 164*e1319447SKrzysztof Kosiński * ArrayIteration 100 49 |||| 165*e1319447SKrzysztof Kosiński * ArrayIteration 1000 477 |||||||||||||||||||||||||||||| 166*e1319447SKrzysztof Kosiński * 167*e1319447SKrzysztof Kosiński */ 168*e1319447SKrzysztof Kosiński public static class Benchmark5 { 169*e1319447SKrzysztof Kosiński @Param int size; // set automatically by framework 170*e1319447SKrzysztof Kosiński 171*e1319447SKrzysztof Kosiński private int[] array; // set by us, in setUp() 172*e1319447SKrzysztof Kosiński setUp()173*e1319447SKrzysztof Kosiński @BeforeExperiment void setUp() { 174*e1319447SKrzysztof Kosiński // @Param values are guaranteed to have been injected by now 175*e1319447SKrzysztof Kosiński array = new int[size]; 176*e1319447SKrzysztof Kosiński } 177*e1319447SKrzysztof Kosiński timeArrayIteration(int reps)178*e1319447SKrzysztof Kosiński @Benchmark int timeArrayIteration(int reps) { 179*e1319447SKrzysztof Kosiński int dummy = 0; 180*e1319447SKrzysztof Kosiński for (int i = 0; i < reps; i++) { 181*e1319447SKrzysztof Kosiński for (int doNotIgnoreMe : array) { 182*e1319447SKrzysztof Kosiński dummy += doNotIgnoreMe; 183*e1319447SKrzysztof Kosiński } 184*e1319447SKrzysztof Kosiński } 185*e1319447SKrzysztof Kosiński return dummy; 186*e1319447SKrzysztof Kosiński } 187*e1319447SKrzysztof Kosiński } 188*e1319447SKrzysztof Kosiński } 189