• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2013 Google Inc.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package com.google.caliper.runner;
18 
19 import static java.util.concurrent.TimeUnit.NANOSECONDS;
20 import static org.junit.Assert.assertEquals;
21 import static org.junit.Assert.assertFalse;
22 import static org.junit.Assert.assertTrue;
23 import static org.junit.Assert.fail;
24 
25 import com.google.caliper.Benchmark;
26 import com.google.caliper.api.BeforeRep;
27 import com.google.caliper.api.Macrobenchmark;
28 import com.google.caliper.runner.Instrument.Instrumentation;
29 import com.google.caliper.util.ShortDuration;
30 import com.google.caliper.worker.MacrobenchmarkWorker;
31 import com.google.caliper.worker.RuntimeWorker;
32 import com.google.common.base.Function;
33 import com.google.common.base.Predicate;
34 import com.google.common.collect.FluentIterable;
35 import com.google.common.collect.ImmutableSet;
36 import com.google.common.util.concurrent.Uninterruptibles;
37 
38 import org.junit.Before;
39 import org.junit.Rule;
40 import org.junit.Test;
41 import org.junit.runner.RunWith;
42 import org.junit.runners.JUnit4;
43 
44 import java.lang.reflect.Method;
45 import java.util.Arrays;
46 import java.util.concurrent.TimeUnit;
47 
48 /**
49  * Tests {@link RuntimeInstrument}.
50  */
51 @RunWith(JUnit4.class)
52 public class RuntimeInstrumentTest {
53   @Rule public CaliperTestWatcher runner = new CaliperTestWatcher();
54 
55   private RuntimeInstrument instrument;
56 
createInstrument()57   @Before public void createInstrument() {
58     this.instrument = new RuntimeInstrument(ShortDuration.of(100, NANOSECONDS));
59   }
60 
isBenchmarkMethod()61   @Test public void isBenchmarkMethod() {
62     assertEquals(
63         ImmutableSet.of("macrobenchmark", "microbenchmark", "picobenchmark", "integerParam"),
64         FluentIterable.from(Arrays.asList(RuntimeBenchmark.class.getDeclaredMethods()))
65             .filter(new Predicate<Method>() {
66               @Override public boolean apply(Method input) {
67                 return instrument.isBenchmarkMethod(input);
68               }
69             })
70             .transform(new Function<Method, String>() {
71               @Override public String apply(Method input) {
72                 return input.getName();
73               }
74             })
75             .toSet());
76   }
77 
createInstrumentation_macrobenchmark()78   @Test public void createInstrumentation_macrobenchmark() throws Exception {
79     Method benchmarkMethod = RuntimeBenchmark.class.getDeclaredMethod("macrobenchmark");
80     Instrumentation instrumentation = instrument.createInstrumentation(benchmarkMethod);
81     assertEquals(benchmarkMethod, instrumentation.benchmarkMethod());
82     assertEquals(instrument, instrumentation.instrument());
83     assertEquals(MacrobenchmarkWorker.class, instrumentation.workerClass());
84   }
85 
createInstrumentation_microbenchmark()86   @Test public void createInstrumentation_microbenchmark() throws Exception {
87     Method benchmarkMethod = RuntimeBenchmark.class.getDeclaredMethod("microbenchmark", int.class);
88     Instrumentation instrumentation = instrument.createInstrumentation(benchmarkMethod);
89     assertEquals(benchmarkMethod, instrumentation.benchmarkMethod());
90     assertEquals(instrument, instrumentation.instrument());
91     assertEquals(RuntimeWorker.Micro.class, instrumentation.workerClass());
92   }
93 
createInstrumentation_picobenchmark()94   @Test public void createInstrumentation_picobenchmark() throws Exception {
95     Method benchmarkMethod = RuntimeBenchmark.class.getDeclaredMethod("picobenchmark", long.class);
96     Instrumentation instrumentation = instrument.createInstrumentation(benchmarkMethod);
97     assertEquals(benchmarkMethod, instrumentation.benchmarkMethod());
98     assertEquals(instrument, instrumentation.instrument());
99     assertEquals(RuntimeWorker.Pico.class, instrumentation.workerClass());
100   }
101 
createInstrumentation_badParam()102   @Test public void createInstrumentation_badParam() throws Exception {
103     Method benchmarkMethod =
104         RuntimeBenchmark.class.getDeclaredMethod("integerParam", Integer.class);
105     try {
106       instrument.createInstrumentation(benchmarkMethod);
107       fail();
108     } catch (InvalidBenchmarkException expected) {}
109   }
110 
createInstrumentation_notAMacrobenchmark()111   @Test public void createInstrumentation_notAMacrobenchmark() throws Exception {
112     Method benchmarkMethod = RuntimeBenchmark.class.getDeclaredMethod("notAMacrobenchmark");
113     try {
114       instrument.createInstrumentation(benchmarkMethod);
115       fail();
116     } catch (IllegalArgumentException expected) {}
117   }
118 
createInstrumentationnotAMicrobenchmark()119   @Test public void createInstrumentationnotAMicrobenchmark() throws Exception {
120     Method benchmarkMethod =
121         RuntimeBenchmark.class.getDeclaredMethod("notAMicrobenchmark", int.class);
122     try {
123       instrument.createInstrumentation(benchmarkMethod);
124       fail();
125     } catch (IllegalArgumentException expected) {}
126   }
127 
createInstrumentation_notAPicobenchmark()128   @Test public void createInstrumentation_notAPicobenchmark() throws Exception {
129     Method benchmarkMethod =
130         RuntimeBenchmark.class.getDeclaredMethod("notAPicobenchmark", long.class);
131     try {
132       instrument.createInstrumentation(benchmarkMethod);
133       fail();
134     } catch (IllegalArgumentException expected) {}
135   }
136 
137   @SuppressWarnings("unused")
138   private static final class RuntimeBenchmark {
macrobenchmark()139     @Benchmark void macrobenchmark() {}
microbenchmark(int reps)140     @Benchmark void microbenchmark(int reps) {}
picobenchmark(long reps)141     @Benchmark void picobenchmark(long reps) {}
142 
integerParam(Integer oops)143     @Benchmark void integerParam(Integer oops) {}
144 
notAMacrobenchmark()145     void notAMacrobenchmark() {}
notAMicrobenchmark(int reps)146     void notAMicrobenchmark(int reps) {}
notAPicobenchmark(long reps)147     void notAPicobenchmark(long reps) {}
148   }
149 
relativeDifference(double a, double b)150   private double relativeDifference(double a, double b) {
151     return Math.abs(a - b) / ((a + b) / 2.0);
152   }
153 
154   static final class TestBenchmark {
pico(long reps)155     @Benchmark long pico(long reps) {
156       long dummy = 0;
157       for (long i = 0; i < reps; i++) {
158         dummy += spin();
159       }
160       return dummy;
161     }
162 
micro(int reps)163     @Benchmark long micro(int reps) {
164       long dummy = 0;
165       for (int i = 0; i < reps; i++) {
166         dummy += spin();
167       }
168       return dummy;
169     }
170 
macro()171     @Macrobenchmark long macro() {
172       return spin();
173     }
174   }
175 
176   // busy spin for 10ms and return the elapsed time.  N.B. we busy spin instead of sleeping so
177   // that we aren't put at the mercy (and variance) of the thread scheduler.
spin()178   private static long spin() {
179     long remainingNanos = TimeUnit.MILLISECONDS.toNanos(10);
180     long start = System.nanoTime();
181     long elapsed;
182     while ((elapsed = System.nanoTime() - start) < remainingNanos) {}
183     return elapsed;
184   }
185 
186   @Test
187 
gcBeforeEachOptionIsHonored()188   public void gcBeforeEachOptionIsHonored() throws Exception {
189     runBenchmarkWithKnownHeap(true);
190     // The GC error will only be avoided if gcBeforeEach is true, and
191     // honored by the MacrobenchmarkWorker.
192     assertFalse("No GC warning should be printed to stderr",
193         runner.getStdout().toString().contains("WARNING: GC occurred during timing."));
194   }
195 
196   @Test
197 
gcBeforeEachOptionIsReallyNecessary()198   public void gcBeforeEachOptionIsReallyNecessary() throws Exception {
199     // Verifies that we indeed get a GC warning if gcBeforeEach = false.
200     runBenchmarkWithKnownHeap(false);
201     assertTrue("A GC warning should be printed to stderr if gcBeforeEach isn't honored",
202         runner.getStdout().toString().contains("WARNING: GC occurred during timing."));
203   }
204 
runBenchmarkWithKnownHeap(boolean gcBeforeEach)205   private void runBenchmarkWithKnownHeap(boolean gcBeforeEach) throws Exception {
206     runner.forBenchmark(BenchmarkThatAllocatesALot.class)
207         .instrument("runtime")
208         .options(
209             "-Cvm.args=-Xmx512m",
210             "-Cinstrument.runtime.options.measurements=10",
211             "-Cinstrument.runtime.options.gcBeforeEach=" + gcBeforeEach,
212             "--time-limit=30s")
213         .run();
214   }
215 
216   static final class BenchmarkThatAllocatesALot {
217     @Benchmark
benchmarkMethod()218     int benchmarkMethod() {
219       // Any larger and the GC doesn't manage to make enough space, resulting in
220       // OOMErrors in both test cases above.
221       long[] array = new long[32 * 1024 * 1024];
222       return array.length;
223     }
224   }
225 
226   @Test
227 
maxWarmupWallTimeOptionIsHonored()228   public void maxWarmupWallTimeOptionIsHonored() throws Exception {
229     runner.forBenchmark(MacroBenchmarkWithLongBeforeRep.class)
230         .instrument("runtime")
231         .options(
232             "-Cinstrument.runtime.options.maxWarmupWallTime=100ms",
233             "--time-limit=10s")
234         .run();
235 
236     assertTrue(
237         "The maxWarmupWallTime should trigger an interruption of warmup and a warning "
238             + "should be printed to stderr",
239         runner.getStdout().toString().contains(
240             "WARNING: Warmup was interrupted "
241                 + "because it took longer than 100ms of wall-clock time."));
242   }
243 
244   static final class MacroBenchmarkWithLongBeforeRep {
245     @BeforeRep
beforeRepMuchLongerThanBenchmark()246     public void beforeRepMuchLongerThanBenchmark() {
247       Uninterruptibles.sleepUninterruptibly(100, TimeUnit.MILLISECONDS);
248     }
249 
250     @Benchmark
prettyFastMacroBenchmark()251     long prettyFastMacroBenchmark() {
252       return spin();
253     }
254   }
255 }
256