blob: 7abcbd989bdae0fc9946d6c4bebb8084dcda1b30 [file] [log] [blame]
/*
* Copyright (C) 2012 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 com.google.caliper.runner;
import com.google.caliper.api.ResultProcessor;
import com.google.caliper.config.CaliperConfig;
import com.google.caliper.config.InstrumentConfig;
import com.google.caliper.model.Host;
import com.google.caliper.options.CaliperOptions;
import com.google.caliper.platform.Platform;
import com.google.caliper.runner.Instrument.Instrumentation;
import com.google.caliper.util.InvalidCommandException;
import com.google.caliper.util.ShortDuration;
import com.google.caliper.util.Stderr;
import com.google.caliper.util.Util;
import com.google.common.base.Function;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.ImmutableSetMultimap;
import com.google.common.collect.ImmutableSortedSet;
import com.google.common.collect.Ordering;
import com.google.common.util.concurrent.ListeningExecutorService;
import com.google.common.util.concurrent.MoreExecutors;
import com.google.common.util.concurrent.Service;
import dagger.MapKey;
import dagger.Module;
import dagger.Provides;
import dagger.Provides.Type;
import java.io.PrintWriter;
import java.lang.reflect.Method;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import java.util.TreeSet;
import java.util.UUID;
import java.util.concurrent.Executors;
import javax.inject.Provider;
import javax.inject.Singleton;
/**
* Configures a {@link CaliperRun} that performs experiments.
*/
@Module
final class ExperimentingRunnerModule {
private static final String RUNNER_MAX_PARALLELISM_OPTION = "runner.maxParallelism";
@Provides(type = Type.SET)
static Service provideServerSocketService(ServerSocketService impl) {
return impl;
}
@Provides(type = Type.SET)
static Service provideTrialOutputFactoryService(TrialOutputFactoryService impl) {
return impl;
}
@Provides
static TrialOutputFactory provideTrialOutputFactory(TrialOutputFactoryService impl) {
return impl;
}
@Provides
static ExperimentSelector provideExperimentSelector(FullCartesianExperimentSelector impl) {
return impl;
}
@Provides
static ListeningExecutorService provideExecutorService(CaliperConfig config) {
int poolSize = Integer.parseInt(config.properties().get(RUNNER_MAX_PARALLELISM_OPTION));
return MoreExecutors.listeningDecorator(Executors.newFixedThreadPool(poolSize));
}
@LocalPort
@Provides
static int providePortNumber(ServerSocketService serverSocketService) {
return serverSocketService.getPort();
}
/**
* Specifies the {@link Class} object to use as a key in the map of available
* {@link ResultProcessor result processors} passed to
* {@link #provideResultProcessors(CaliperConfig, Map)}.
*/
@MapKey(unwrapValue = true)
public @interface ResultProcessorClassKey {
Class<? extends ResultProcessor> value();
}
@Provides(type = Type.MAP)
@ResultProcessorClassKey(OutputFileDumper.class)
static ResultProcessor provideOutputFileDumper(OutputFileDumper impl) {
return impl;
}
@Provides(type = Type.MAP)
@ResultProcessorClassKey(HttpUploader.class)
static ResultProcessor provideHttpUploader(HttpUploader impl) {
return impl;
}
@Provides static ImmutableSet<ResultProcessor> provideResultProcessors(
CaliperConfig config,
Map<Class<? extends ResultProcessor>, Provider<ResultProcessor>> availableProcessors) {
ImmutableSet.Builder<ResultProcessor> builder = ImmutableSet.builder();
for (Class<? extends ResultProcessor> processorClass : config.getConfiguredResultProcessors()) {
Provider<ResultProcessor> resultProcessorProvider = availableProcessors.get(processorClass);
ResultProcessor resultProcessor = resultProcessorProvider == null
? ResultProcessorCreator.createResultProcessor(processorClass)
: resultProcessorProvider.get();
builder.add(resultProcessor);
}
return builder.build();
}
@Provides static UUID provideUuid() {
return UUID.randomUUID();
}
@Provides @BenchmarkParameters
static ImmutableSetMultimap<String, String> provideBenchmarkParameters(
BenchmarkClass benchmarkClass, CaliperOptions options) throws InvalidBenchmarkException {
return benchmarkClass.userParameters().fillInDefaultsFor(options.userParameters());
}
@Provides @Singleton
static Host provideHost(EnvironmentGetter environmentGetter) {
return environmentGetter.getHost();
}
@Provides @Singleton
static EnvironmentGetter provideEnvironmentGetter() {
return new EnvironmentGetter();
}
/**
* Specifies the {@link Class} object to use as a key in the map of available
* {@link Instrument instruments} passed to {@link #provideInstruments},
*/
@MapKey(unwrapValue = true)
public @interface InstrumentClassKey {
Class<? extends Instrument> value();
}
@Provides(type = Type.MAP)
@InstrumentClassKey(ArbitraryMeasurementInstrument.class)
static Instrument provideArbitraryMeasurementInstrument() {
return new ArbitraryMeasurementInstrument();
}
@Provides(type = Type.MAP)
@InstrumentClassKey(AllocationInstrument.class)
static Instrument provideAllocationInstrument() {
return new AllocationInstrument();
}
@Provides(type = Type.MAP)
@InstrumentClassKey(RuntimeInstrument.class)
static Instrument provideRuntimeInstrument(
@NanoTimeGranularity ShortDuration nanoTimeGranularity) {
return new RuntimeInstrument(nanoTimeGranularity);
}
@Provides
static ImmutableSet<Instrument> provideInstruments(
CaliperOptions options,
final CaliperConfig config,
Map<Class<? extends Instrument>, Provider<Instrument>> availableInstruments,
Platform platform,
@Stderr PrintWriter stderr)
throws InvalidCommandException {
ImmutableSet.Builder<Instrument> builder = ImmutableSet.builder();
ImmutableSet<String> configuredInstruments = config.getConfiguredInstruments();
for (final String instrumentName : options.instrumentNames()) {
if (!configuredInstruments.contains(instrumentName)) {
throw new InvalidCommandException("%s is not a configured instrument (%s). "
+ "use --print-config to see the configured instruments.",
instrumentName, configuredInstruments);
}
final InstrumentConfig instrumentConfig = config.getInstrumentConfig(instrumentName);
String className = instrumentConfig.className();
try {
Class<? extends Instrument> clazz =
Util.lenientClassForName(className).asSubclass(Instrument.class);
Provider<Instrument> instrumentProvider = availableInstruments.get(clazz);
if (instrumentProvider == null) {
throw new InvalidInstrumentException("Instrument %s not supported", className);
}
// Make sure that the instrument is supported on the platform.
if (platform.supports(clazz)) {
Instrument instrument = instrumentProvider.get();
InstrumentInjectorModule injectorModule =
new InstrumentInjectorModule(instrumentConfig, instrumentName);
InstrumentComponent instrumentComponent = DaggerInstrumentComponent.builder()
.instrumentInjectorModule(injectorModule)
.build();
instrumentComponent.injectInstrument(instrument);
builder.add(instrument);
} else {
stderr.format("Instrument %s not supported on %s, ignoring\n",
className, platform.name());
}
} catch (ClassNotFoundException e) {
throw new InvalidCommandException("Cannot find instrument class '%s'", className);
}
}
return builder.build();
}
@Provides @Singleton static NanoTimeGranularityTester provideNanoTimeGranularityTester() {
return new NanoTimeGranularityTester();
}
@Provides @Singleton @NanoTimeGranularity static ShortDuration provideNanoTimeGranularity(
NanoTimeGranularityTester tester) {
return tester.testNanoTimeGranularity();
}
@Provides static ImmutableSet<Instrumentation> provideInstrumentations(CaliperOptions options,
BenchmarkClass benchmarkClass, ImmutableSet<Instrument> instruments)
throws InvalidBenchmarkException {
ImmutableSet.Builder<Instrumentation> builder = ImmutableSet.builder();
ImmutableSet<String> benchmarkMethodNames = options.benchmarkMethodNames();
Set<String> unusedBenchmarkNames = new HashSet<String>(benchmarkMethodNames);
for (Instrument instrument : instruments) {
for (Method method : findAllBenchmarkMethods(benchmarkClass.benchmarkClass(), instrument)) {
if (benchmarkMethodNames.isEmpty() || benchmarkMethodNames.contains(method.getName())) {
builder.add(instrument.createInstrumentation(method));
unusedBenchmarkNames.remove(method.getName());
}
}
}
if (!unusedBenchmarkNames.isEmpty()) {
throw new InvalidBenchmarkException(
"Invalid benchmark method(s) specified in options: " + unusedBenchmarkNames);
}
return builder.build();
}
private static ImmutableSortedSet<Method> findAllBenchmarkMethods(Class<?> benchmarkClass,
Instrument instrument) throws InvalidBenchmarkException {
ImmutableSortedSet.Builder<Method> result = ImmutableSortedSet.orderedBy(
Ordering.natural().onResultOf(new Function<Method, String>() {
@Override public String apply(Method method) {
return method.getName();
}
}));
Set<String> benchmarkMethodNames = new HashSet<String>();
Set<String> overloadedMethodNames = new TreeSet<String>();
for (Method method : benchmarkClass.getDeclaredMethods()) {
if (instrument.isBenchmarkMethod(method)) {
method.setAccessible(true);
result.add(method);
if (!benchmarkMethodNames.add(method.getName())) {
overloadedMethodNames.add(method.getName());
}
}
}
if (!overloadedMethodNames.isEmpty()) {
throw new InvalidBenchmarkException(
"Overloads are disallowed for benchmark methods, found overloads of %s in benchmark %s",
overloadedMethodNames,
benchmarkClass);
}
return result.build();
}
}