Java tutorial
/* * 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.util.CommandLineParser; import com.google.caliper.exception.ConfigurationException; import com.google.caliper.exception.UserException.AbstractBenchmarkException; import com.google.caliper.exception.UserException.DoesntImplementBenchmarkException; import com.google.caliper.exception.UserException.ExceptionFromUserCodeException; import com.google.caliper.exception.UserException.NoParameterlessConstructorException; import com.google.caliper.exception.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. * * @author somebody@google */ public final class ScenarioSelection { private final Set<String> userVms; private final Multimap<String, String> vmParameters; private final String suiteClassName; /** * The user parameters specified on the command line. This may be a subset of * the effective user parameters because parameters not specified here may get * default values from the benchmark class. */ private final Multimap<String, String> userParameterArguments; /** * The actual user parameters we'll use to run in the benchmark. This contains * the userParameterArguments plus the default user parameters. */ private Multimap<String, String> userParameters; private final int trials; private Benchmark suite; public ScenarioSelection(CommandLineParser arguments) { this(arguments.getUserVms(), arguments.getVmParameters(), arguments.getSuiteClassName(), arguments.getUserParameters(), arguments.getTrials()); } public ScenarioSelection(Set<String> userVms, Multimap<String, String> vmParameters, String suiteClassName, Multimap<String, String> userParameterArguments, int trials) { this.userVms = userVms; this.vmParameters = vmParameters; this.suiteClassName = suiteClassName; this.userParameterArguments = userParameterArguments; this.trials = trials; } /** * Returns the selected scenarios for this benchmark. */ public List<Scenario> select() { prepareSuite(); userParameters = computeUserParameters(); return createScenarios(); } /** * Returns a normalized version of {@code scenario}, with information from {@code suite} * assisting in correcting problems. */ public Scenario normalizeScenario(Scenario scenario) { suite.normalizeScenario(scenario); return scenario; } public Set<String> getUserParameterNames() { if (userParameters == null) { throw new IllegalStateException(); } return userParameters.keySet(); } public Set<String> getVmParameterNames() { return vmParameters.keySet(); } public BenchmarkWrapper createBenchmark(Scenario scenario) { return suite.createBenchmark(scenario.getVariables(getUserParameterNames())); } 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 Multimap<String, String> computeUserParameters() { Multimap<String, String> result = LinkedHashMultimap.create(); for (String key : suite.parameterNames()) { // first check if the user has specified values Collection<String> userValues = userParameterArguments.get(key); if (!userValues.isEmpty()) { result.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. " + "Did you forget a -D" + key + "=<value> command line argument?"); } result.putAll(key, values); } } return result; } private ImmutableSet<String> defaultVms() { return ImmutableSet.of("java"); } /** * Returns a complete set of scenarios with every combination of variables. */ private List<Scenario> createScenarios() { List<ScenarioBuilder> builders = new ArrayList<ScenarioBuilder>(); builders.add(new ScenarioBuilder()); Map<String, Collection<String>> variables = new LinkedHashMap<String, Collection<String>>(); variables.put(Scenario.VM_KEY, userVms.isEmpty() ? defaultVms() : userVms); variables.put(Scenario.TRIAL_KEY, newListOfSize(trials)); variables.putAll(userParameters.asMap()); variables.putAll(vmParameters.asMap()); for (Entry<String, Collection<String>> entry : variables.entrySet()) { Iterator<String> values = entry.getValue().iterator(); if (!values.hasNext()) { throw new ConfigurationException("Not enough values for " + entry); } String firstValue = values.next(); for (ScenarioBuilder builder : builders) { builder.variables.put(entry.getKey(), 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.variables.put(entry.getKey(), alternate); builders.add(copy); } } } List<Scenario> result = new ArrayList<Scenario>(); for (ScenarioBuilder builder : builders) { result.add(normalizeScenario(builder.build())); } return result; } /** * Returns a list containing {@code count} distinct elements. */ private Collection<String> newListOfSize(int count) { List<String> result = new ArrayList<String>(); for (int i = 0; i < count; i++) { result.add(Integer.toString(i)); } return result; } private static class ScenarioBuilder { final Map<String, String> variables = new LinkedHashMap<String, String>(); ScenarioBuilder copy() { ScenarioBuilder result = new ScenarioBuilder(); result.variables.putAll(variables); return result; } public Scenario build() { return new Scenario(variables); } } }