• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2012 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 com.google.caliper.api.ResultProcessor;
20 import com.google.caliper.config.CaliperConfig;
21 import com.google.caliper.config.InstrumentConfig;
22 import com.google.caliper.model.Host;
23 import com.google.caliper.options.CaliperOptions;
24 import com.google.caliper.platform.Platform;
25 import com.google.caliper.runner.Instrument.Instrumentation;
26 import com.google.caliper.util.InvalidCommandException;
27 import com.google.caliper.util.ShortDuration;
28 import com.google.caliper.util.Stderr;
29 import com.google.caliper.util.Util;
30 import com.google.common.base.Function;
31 import com.google.common.collect.ImmutableSet;
32 import com.google.common.collect.ImmutableSetMultimap;
33 import com.google.common.collect.ImmutableSortedSet;
34 import com.google.common.collect.Ordering;
35 import com.google.common.util.concurrent.ListeningExecutorService;
36 import com.google.common.util.concurrent.MoreExecutors;
37 import com.google.common.util.concurrent.Service;
38 
39 import dagger.MapKey;
40 import dagger.Module;
41 import dagger.Provides;
42 import dagger.multibindings.IntoMap;
43 import dagger.multibindings.IntoSet;
44 
45 import java.io.PrintWriter;
46 import java.lang.reflect.Method;
47 import java.util.HashSet;
48 import java.util.Map;
49 import java.util.Set;
50 import java.util.TreeSet;
51 import java.util.UUID;
52 import java.util.concurrent.Executors;
53 
54 import javax.inject.Provider;
55 import javax.inject.Singleton;
56 
57 /**
58  * Configures a {@link CaliperRun} that performs experiments.
59  */
60 @Module
61 final class ExperimentingRunnerModule {
62   private static final String RUNNER_MAX_PARALLELISM_OPTION = "runner.maxParallelism";
63 
64   @Provides
65   @IntoSet
provideServerSocketService(ServerSocketService impl)66   static Service provideServerSocketService(ServerSocketService impl) {
67     return impl;
68   }
69 
70   @Provides
71   @IntoSet
provideTrialOutputFactoryService(TrialOutputFactoryService impl)72   static Service provideTrialOutputFactoryService(TrialOutputFactoryService impl) {
73     return impl;
74   }
75 
76   @Provides
provideTrialOutputFactory(TrialOutputFactoryService impl)77   static TrialOutputFactory provideTrialOutputFactory(TrialOutputFactoryService impl) {
78     return impl;
79   }
80 
81   @Provides
provideExperimentSelector(FullCartesianExperimentSelector impl)82   static ExperimentSelector provideExperimentSelector(FullCartesianExperimentSelector impl) {
83     return impl;
84   }
85 
86   @Provides
provideExecutorService(CaliperConfig config)87   static ListeningExecutorService provideExecutorService(CaliperConfig config) {
88     int poolSize = Integer.parseInt(config.properties().get(RUNNER_MAX_PARALLELISM_OPTION));
89     return MoreExecutors.listeningDecorator(Executors.newFixedThreadPool(poolSize));
90   }
91 
92   @LocalPort
93   @Provides
providePortNumber(ServerSocketService serverSocketService)94   static int providePortNumber(ServerSocketService serverSocketService) {
95     return serverSocketService.getPort();
96   }
97 
98   /**
99    * Specifies the {@link Class} object to use as a key in the map of available
100    * {@link ResultProcessor result processors} passed to
101    * {@link #provideResultProcessors(CaliperConfig, Map)}.
102    */
103   @MapKey(unwrapValue = true)
104   public @interface ResultProcessorClassKey {
value()105     Class<? extends ResultProcessor> value();
106   }
107 
108   @Provides
109   @IntoMap
110   @ResultProcessorClassKey(OutputFileDumper.class)
provideOutputFileDumper(OutputFileDumper impl)111   static ResultProcessor provideOutputFileDumper(OutputFileDumper impl) {
112     return impl;
113   }
114 
115   @Provides
116   @IntoMap
117   @ResultProcessorClassKey(HttpUploader.class)
provideHttpUploader(HttpUploader impl)118   static ResultProcessor provideHttpUploader(HttpUploader impl) {
119     return impl;
120   }
121 
provideResultProcessors( CaliperConfig config, Map<Class<? extends ResultProcessor>, Provider<ResultProcessor>> availableProcessors)122   @Provides static ImmutableSet<ResultProcessor> provideResultProcessors(
123       CaliperConfig config,
124       Map<Class<? extends ResultProcessor>, Provider<ResultProcessor>> availableProcessors) {
125     ImmutableSet.Builder<ResultProcessor> builder = ImmutableSet.builder();
126     for (Class<? extends ResultProcessor> processorClass : config.getConfiguredResultProcessors()) {
127       Provider<ResultProcessor> resultProcessorProvider = availableProcessors.get(processorClass);
128       ResultProcessor resultProcessor = resultProcessorProvider == null
129           ? ResultProcessorCreator.createResultProcessor(processorClass)
130           : resultProcessorProvider.get();
131       builder.add(resultProcessor);
132     }
133     return builder.build();
134   }
135 
provideUuid()136   @Provides static UUID provideUuid() {
137     return UUID.randomUUID();
138   }
139 
140   @Provides @BenchmarkParameters
provideBenchmarkParameters( BenchmarkClass benchmarkClass, CaliperOptions options)141   static ImmutableSetMultimap<String, String> provideBenchmarkParameters(
142       BenchmarkClass benchmarkClass, CaliperOptions options) throws InvalidBenchmarkException {
143     return benchmarkClass.userParameters().fillInDefaultsFor(options.userParameters());
144   }
145 
146   @Provides @Singleton
provideHost(EnvironmentGetter environmentGetter)147   static Host provideHost(EnvironmentGetter environmentGetter) {
148     return environmentGetter.getHost();
149   }
150 
151   @Provides @Singleton
provideEnvironmentGetter()152   static EnvironmentGetter provideEnvironmentGetter() {
153     return new EnvironmentGetter();
154   }
155 
156   /**
157    * Specifies the {@link Class} object to use as a key in the map of available
158    * {@link Instrument instruments} passed to {@link #provideInstruments},
159    */
160   @MapKey(unwrapValue = true)
161   public @interface InstrumentClassKey {
value()162     Class<? extends Instrument> value();
163   }
164 
165   @Provides
166   @IntoMap
167   @InstrumentClassKey(ArbitraryMeasurementInstrument.class)
provideArbitraryMeasurementInstrument()168   static Instrument provideArbitraryMeasurementInstrument() {
169     return new ArbitraryMeasurementInstrument();
170   }
171 
172   @Provides
173   @IntoMap
174   @InstrumentClassKey(AllocationInstrument.class)
provideAllocationInstrument()175   static Instrument provideAllocationInstrument() {
176     return new AllocationInstrument();
177   }
178 
179   @Provides
180   @IntoMap
181   @InstrumentClassKey(RuntimeInstrument.class)
provideRuntimeInstrument( @anoTimeGranularity ShortDuration nanoTimeGranularity)182   static Instrument provideRuntimeInstrument(
183       @NanoTimeGranularity ShortDuration nanoTimeGranularity) {
184     return new RuntimeInstrument(nanoTimeGranularity);
185   }
186 
187   @Provides
provideInstruments( CaliperOptions options, final CaliperConfig config, Map<Class<? extends Instrument>, Provider<Instrument>> availableInstruments, Platform platform, @Stderr PrintWriter stderr)188   static ImmutableSet<Instrument> provideInstruments(
189       CaliperOptions options,
190       final CaliperConfig config,
191       Map<Class<? extends Instrument>, Provider<Instrument>> availableInstruments,
192       Platform platform,
193       @Stderr PrintWriter stderr)
194       throws InvalidCommandException {
195 
196     ImmutableSet.Builder<Instrument> builder = ImmutableSet.builder();
197     ImmutableSet<String> configuredInstruments = config.getConfiguredInstruments();
198     for (final String instrumentName : options.instrumentNames()) {
199       if (!configuredInstruments.contains(instrumentName)) {
200         throw new InvalidCommandException("%s is not a configured instrument (%s). "
201             + "use --print-config to see the configured instruments.",
202             instrumentName, configuredInstruments);
203       }
204       final InstrumentConfig instrumentConfig = config.getInstrumentConfig(instrumentName);
205       String className = instrumentConfig.className();
206       try {
207         Class<? extends Instrument> clazz =
208             Util.lenientClassForName(className).asSubclass(Instrument.class);
209         Provider<Instrument> instrumentProvider = availableInstruments.get(clazz);
210         if (instrumentProvider == null) {
211           throw new InvalidInstrumentException("Instrument %s not supported", className);
212         }
213 
214         // Make sure that the instrument is supported on the platform.
215         if (platform.supports(clazz)) {
216           Instrument instrument = instrumentProvider.get();
217           InstrumentInjectorModule injectorModule =
218               new InstrumentInjectorModule(instrumentConfig, instrumentName);
219           InstrumentComponent instrumentComponent = DaggerInstrumentComponent.builder()
220               .instrumentInjectorModule(injectorModule)
221               .build();
222           instrumentComponent.injectInstrument(instrument);
223           builder.add(instrument);
224         } else {
225           stderr.format("Instrument %s not supported on %s, ignoring\n",
226               className, platform.name());
227         }
228       } catch (ClassNotFoundException e) {
229         throw new InvalidCommandException("Cannot find instrument class '%s'", className);
230       }
231     }
232     return builder.build();
233   }
234 
provideNanoTimeGranularityTester()235   @Provides @Singleton static NanoTimeGranularityTester provideNanoTimeGranularityTester() {
236     return new NanoTimeGranularityTester();
237   }
238 
provideNanoTimeGranularity( NanoTimeGranularityTester tester)239   @Provides @Singleton @NanoTimeGranularity static ShortDuration provideNanoTimeGranularity(
240       NanoTimeGranularityTester tester) {
241     return tester.testNanoTimeGranularity();
242   }
243 
provideInstrumentations(CaliperOptions options, BenchmarkClass benchmarkClass, ImmutableSet<Instrument> instruments)244   @Provides static ImmutableSet<Instrumentation> provideInstrumentations(CaliperOptions options,
245       BenchmarkClass benchmarkClass, ImmutableSet<Instrument> instruments)
246           throws InvalidBenchmarkException {
247     ImmutableSet.Builder<Instrumentation> builder = ImmutableSet.builder();
248     ImmutableSet<String> benchmarkMethodNames = options.benchmarkMethodNames();
249     Set<String> unusedBenchmarkNames = new HashSet<String>(benchmarkMethodNames);
250     for (Instrument instrument : instruments) {
251       for (Method method : findAllBenchmarkMethods(benchmarkClass.benchmarkClass(), instrument)) {
252         if (benchmarkMethodNames.isEmpty() || benchmarkMethodNames.contains(method.getName())) {
253           builder.add(instrument.createInstrumentation(method));
254           unusedBenchmarkNames.remove(method.getName());
255         }
256       }
257     }
258     if (!unusedBenchmarkNames.isEmpty()) {
259       throw new InvalidBenchmarkException(
260           "Invalid benchmark method(s) specified in options: " + unusedBenchmarkNames);
261     }
262     return builder.build();
263   }
264 
findAllBenchmarkMethods(Class<?> benchmarkClass, Instrument instrument)265   private static ImmutableSortedSet<Method> findAllBenchmarkMethods(Class<?> benchmarkClass,
266       Instrument instrument) throws InvalidBenchmarkException {
267     ImmutableSortedSet.Builder<Method> result = ImmutableSortedSet.orderedBy(
268         Ordering.natural().onResultOf(new Function<Method, String>() {
269           @Override public String apply(Method method) {
270             return method.getName();
271           }
272         }));
273     Set<String> benchmarkMethodNames = new HashSet<String>();
274     Set<String> overloadedMethodNames = new TreeSet<String>();
275     for (Method method : benchmarkClass.getDeclaredMethods()) {
276       if (instrument.isBenchmarkMethod(method)) {
277         method.setAccessible(true);
278         result.add(method);
279         if (!benchmarkMethodNames.add(method.getName())) {
280           overloadedMethodNames.add(method.getName());
281         }
282       }
283     }
284     if (!overloadedMethodNames.isEmpty()) {
285       throw new InvalidBenchmarkException(
286           "Overloads are disallowed for benchmark methods, found overloads of %s in benchmark %s",
287           overloadedMethodNames,
288           benchmarkClass);
289     }
290     return result.build();
291   }
292 }
293