blob: 7814818f9aacd8733085e8aecb80448ea4fb2cce [file] [log] [blame]
/*
* Copyright (C) 2010 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;
import com.google.caliper.UserException.AbstractBenchmarkException;
import com.google.caliper.UserException.DoesntImplementBenchmarkException;
import com.google.caliper.UserException.ExceptionFromUserCodeException;
import com.google.caliper.UserException.NoParameterlessConstructorException;
import com.google.caliper.UserException.NoSuchClassException;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.LinkedHashMultimap;
import com.google.common.collect.Multimap;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
/**
* Figures out which scenarios to benchmark given a benchmark suite, set of user
* parameters, and set of user VMs.
*/
public final class ScenarioSelection {
private final String suiteClassName;
private final Multimap<String, String> userParameters;
private final Set<String> userVms;
private Benchmark suite;
/** Effective parameters to run in the benchmark. */
private final Multimap<String, String> parameters = LinkedHashMultimap.create();
public ScenarioSelection(Arguments arguments) {
this(arguments.getSuiteClassName(), arguments.getUserParameters(), arguments.getUserVms());
}
public ScenarioSelection(String suiteClassName,
Multimap<String, String> userParameters, Set<String> userVms) {
this.suiteClassName = suiteClassName;
this.userParameters = userParameters;
this.userVms = userVms;
}
/**
* Returns the selected scenarios for this benchmark.
*/
public List<Scenario> select() {
prepareSuite();
prepareParameters();
return createScenarios();
}
public TimedRunnable createBenchmark(Scenario scenario) {
return suite.createBenchmark(scenario.getParameters());
}
private void prepareSuite() {
Class<?> benchmarkClass;
try {
benchmarkClass = getClassByName(suiteClassName);
} catch (ExceptionInInitializerError e) {
throw new ExceptionFromUserCodeException(e.getCause());
} catch (ClassNotFoundException ignored) {
throw new NoSuchClassException(suiteClassName);
}
Object s;
try {
Constructor<?> constructor = benchmarkClass.getDeclaredConstructor();
constructor.setAccessible(true);
s = constructor.newInstance();
} catch (InstantiationException ignore) {
throw new AbstractBenchmarkException(benchmarkClass);
} catch (NoSuchMethodException ignore) {
throw new NoParameterlessConstructorException(benchmarkClass);
} catch (IllegalAccessException impossible) {
throw new AssertionError(impossible); // shouldn't happen since we setAccessible(true)
} catch (InvocationTargetException e) {
throw new ExceptionFromUserCodeException(e.getCause());
}
if (s instanceof Benchmark) {
this.suite = (Benchmark) s;
} else {
throw new DoesntImplementBenchmarkException(benchmarkClass);
}
}
private static Class<?> getClassByName(String className) throws ClassNotFoundException {
try {
return Class.forName(className);
} catch (ClassNotFoundException ignored) {
// try replacing the last dot with a $, in case that helps
// example: tutorial.Tutorial.Benchmark1 becomes tutorial.Tutorial$Benchmark1
// amusingly, the $ character means three different things in this one line alone
String newName = className.replaceFirst("\\.([^.]+)$", "\\$$1");
return Class.forName(newName);
}
}
private void prepareParameters() {
for (String key : suite.parameterNames()) {
// first check if the user has specified values
Collection<String> userValues = userParameters.get(key);
if (!userValues.isEmpty()) {
parameters.putAll(key, userValues);
// TODO: type convert 'em to validate?
} else { // otherwise use the default values from the suite
Set<String> values = suite.parameterValues(key);
if (values.isEmpty()) {
throw new ConfigurationException(key + " has no values");
}
parameters.putAll(key, values);
}
}
}
private ImmutableSet<String> defaultVms() {
return "Dalvik".equals(System.getProperty("java.vm.name"))
? ImmutableSet.of("dalvikvm")
: ImmutableSet.of("java");
}
/**
* Returns a complete set of scenarios with every combination of values and
* benchmark classes.
*/
private List<Scenario> createScenarios() {
List<ScenarioBuilder> builders = new ArrayList<ScenarioBuilder>();
// create scenarios for each VM
Set<String> vms = userVms.isEmpty()
? defaultVms()
: userVms;
for (String vm : vms) {
ScenarioBuilder scenarioBuilder = new ScenarioBuilder();
scenarioBuilder.parameters.put(Scenario.VM_KEY, vm);
builders.add(scenarioBuilder);
}
for (Entry<String, Collection<String>> parameter : parameters.asMap().entrySet()) {
Iterator<String> values = parameter.getValue().iterator();
if (!values.hasNext()) {
throw new ConfigurationException("Not enough values for " + parameter);
}
String key = parameter.getKey();
String firstValue = values.next();
for (ScenarioBuilder builder : builders) {
builder.parameters.put(key, firstValue);
}
// multiply the size of the specs by the number of alternate values
int size = builders.size();
while (values.hasNext()) {
String alternate = values.next();
for (int s = 0; s < size; s++) {
ScenarioBuilder copy = builders.get(s).copy();
copy.parameters.put(key, alternate);
builders.add(copy);
}
}
}
List<Scenario> result = new ArrayList<Scenario>();
for (ScenarioBuilder builder : builders) {
result.add(builder.build());
}
return result;
}
private static class ScenarioBuilder {
final Map<String, String> parameters = new LinkedHashMap<String, String>();
ScenarioBuilder copy() {
ScenarioBuilder result = new ScenarioBuilder();
result.parameters.putAll(parameters);
return result;
}
public Scenario build() {
return new Scenario(parameters);
}
}
}