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