diff options
Diffstat (limited to 'src/com/google/caliper/ScenarioSelection.java')
-rw-r--r-- | src/com/google/caliper/ScenarioSelection.java | 208 |
1 files changed, 208 insertions, 0 deletions
diff --git a/src/com/google/caliper/ScenarioSelection.java b/src/com/google/caliper/ScenarioSelection.java new file mode 100644 index 0000000..7814818 --- /dev/null +++ b/src/com/google/caliper/ScenarioSelection.java @@ -0,0 +1,208 @@ +/* + * 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); + } + } +} |