1 /* 2 * Copyright (C) 2011 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 static com.google.common.base.Preconditions.checkArgument; 20 import static com.google.common.base.Preconditions.checkNotNull; 21 22 import com.google.caliper.bridge.AbstractLogMessageVisitor; 23 import com.google.caliper.bridge.LogMessageVisitor; 24 import com.google.caliper.bridge.StopMeasurementLogMessage; 25 import com.google.caliper.config.VmConfig; 26 import com.google.caliper.model.InstrumentSpec; 27 import com.google.caliper.model.Measurement; 28 import com.google.caliper.worker.Worker; 29 import com.google.common.base.MoreObjects; 30 import com.google.common.base.Objects; 31 import com.google.common.base.Predicates; 32 import com.google.common.collect.ArrayListMultimap; 33 import com.google.common.collect.ImmutableList; 34 import com.google.common.collect.ImmutableMap; 35 import com.google.common.collect.ImmutableSet; 36 import com.google.common.collect.ListMultimap; 37 import com.google.common.collect.Maps; 38 39 import java.lang.reflect.Method; 40 41 import javax.inject.Inject; 42 43 public abstract class Instrument { 44 protected ImmutableMap<String, String> options; 45 private String name = getClass().getSimpleName(); 46 setOptions(@nstrumentOptions ImmutableMap<String, String> options)47 @Inject void setOptions(@InstrumentOptions ImmutableMap<String, String> options) { 48 this.options = ImmutableMap.copyOf( 49 Maps.filterKeys(options, Predicates.in(instrumentOptions()))); 50 } 51 setInstrumentName(@nstrumentName String name)52 @Inject void setInstrumentName(@InstrumentName String name) { 53 this.name = name; 54 } 55 name()56 String name() { 57 return name; 58 } 59 toString()60 @Override public String toString() { 61 return name(); 62 } 63 isBenchmarkMethod(Method method)64 public abstract boolean isBenchmarkMethod(Method method); 65 createInstrumentation(Method benchmarkMethod)66 public abstract Instrumentation createInstrumentation(Method benchmarkMethod) 67 throws InvalidBenchmarkException; 68 69 /** 70 * Indicates that trials using this instrument can be run in parallel with other trials. 71 */ schedulingPolicy()72 public abstract TrialSchedulingPolicy schedulingPolicy(); 73 74 /** 75 * The application of an instrument to a particular benchmark method. 76 */ 77 // TODO(gak): consider passing in Instrument explicitly for DI 78 public abstract class Instrumentation { 79 protected Method benchmarkMethod; 80 Instrumentation(Method benchmarkMethod)81 protected Instrumentation(Method benchmarkMethod) { 82 this.benchmarkMethod = checkNotNull(benchmarkMethod); 83 } 84 instrument()85 Instrument instrument() { 86 return Instrument.this; 87 } 88 benchmarkMethod()89 Method benchmarkMethod() { 90 return benchmarkMethod; 91 } 92 93 @Override equals(Object obj)94 public final boolean equals(Object obj) { 95 if (obj == this) { 96 return true; 97 } else if (obj instanceof Instrumentation) { 98 Instrumentation that = (Instrumentation) obj; 99 return Instrument.this.equals(that.instrument()) 100 && this.benchmarkMethod.equals(that.benchmarkMethod); 101 } 102 return super.equals(obj); 103 } 104 105 @Override hashCode()106 public final int hashCode() { 107 return Objects.hashCode(Instrument.this, benchmarkMethod); 108 } 109 110 @Override toString()111 public String toString() { 112 return MoreObjects.toStringHelper(Instrumentation.class) 113 .add("instrument", Instrument.this) 114 .add("benchmarkMethod", benchmarkMethod) 115 .toString(); 116 } 117 dryRun(Object benchmark)118 public abstract void dryRun(Object benchmark) throws InvalidBenchmarkException; 119 workerClass()120 public abstract Class<? extends Worker> workerClass(); 121 122 /** 123 * Return the subset of options (and possibly a transformation thereof) to be used in the 124 * worker. Returns all instrument options by default. 125 */ workerOptions()126 public ImmutableMap<String, String> workerOptions() { 127 return options; 128 } 129 getMeasurementCollectingVisitor()130 abstract MeasurementCollectingVisitor getMeasurementCollectingVisitor(); 131 } 132 options()133 public final ImmutableMap<String, String> options() { 134 return options; 135 } 136 getSpec()137 final InstrumentSpec getSpec() { 138 return new InstrumentSpec.Builder() 139 .instrumentClass(getClass()) 140 .addAllOptions(options()) 141 .build(); 142 } 143 144 /** 145 * Defines the list of options applicable to this instrument. Implementations that use options 146 * will need to override this method. 147 */ instrumentOptions()148 protected ImmutableSet<String> instrumentOptions() { 149 return ImmutableSet.of(); 150 } 151 152 /** 153 * Returns some arguments that should be added to the command line when invoking 154 * this instrument's worker. 155 * 156 * @param vmConfig the configuration for the VM on which this is running. 157 */ getExtraCommandLineArgs(VmConfig vmConfig)158 ImmutableSet<String> getExtraCommandLineArgs(VmConfig vmConfig) { 159 return vmConfig.commonInstrumentVmArgs(); 160 } 161 162 interface MeasurementCollectingVisitor extends LogMessageVisitor { isDoneCollecting()163 boolean isDoneCollecting(); isWarmupComplete()164 boolean isWarmupComplete(); getMeasurements()165 ImmutableList<Measurement> getMeasurements(); 166 /** 167 * Returns all the messages created while collecting measurments. 168 * 169 * <p>A message is some piece of user visible data that should be displayed to the user along 170 * with the trial results. 171 * 172 * <p>TODO(lukes): should we model these as anything more than strings. These messages already 173 * have a concept of 'level' based on the prefix. 174 */ getMessages()175 ImmutableList<String> getMessages(); 176 } 177 178 /** 179 * A default implementation of {@link MeasurementCollectingVisitor} that collects measurements for 180 * pre-specified descriptions. 181 */ 182 protected static final class DefaultMeasurementCollectingVisitor 183 extends AbstractLogMessageVisitor implements MeasurementCollectingVisitor { 184 static final int DEFAULT_NUMBER_OF_MEASUREMENTS = 9; 185 final ImmutableSet<String> requiredDescriptions; 186 final ListMultimap<String, Measurement> measurementsByDescription; 187 final int requiredMeasurements; 188 DefaultMeasurementCollectingVisitor(ImmutableSet<String> requiredDescriptions)189 DefaultMeasurementCollectingVisitor(ImmutableSet<String> requiredDescriptions) { 190 this(requiredDescriptions, DEFAULT_NUMBER_OF_MEASUREMENTS); 191 } 192 DefaultMeasurementCollectingVisitor(ImmutableSet<String> requiredDescriptions, int requiredMeasurements)193 DefaultMeasurementCollectingVisitor(ImmutableSet<String> requiredDescriptions, 194 int requiredMeasurements) { 195 this.requiredDescriptions = requiredDescriptions; 196 checkArgument(!requiredDescriptions.isEmpty()); 197 this.requiredMeasurements = requiredMeasurements; 198 checkArgument(requiredMeasurements > 0); 199 this.measurementsByDescription = 200 ArrayListMultimap.create(requiredDescriptions.size(), requiredMeasurements); 201 } 202 visit(StopMeasurementLogMessage logMessage)203 @Override public void visit(StopMeasurementLogMessage logMessage) { 204 for (Measurement measurement : logMessage.measurements()) { 205 measurementsByDescription.put(measurement.description(), measurement); 206 } 207 } 208 isDoneCollecting()209 @Override public boolean isDoneCollecting() { 210 for (String description : requiredDescriptions) { 211 if (measurementsByDescription.get(description).size() < requiredMeasurements) { 212 return false; 213 } 214 } 215 return true; 216 } 217 218 @Override isWarmupComplete()219 public boolean isWarmupComplete() { 220 return true; 221 } 222 getMeasurements()223 @Override public ImmutableList<Measurement> getMeasurements() { 224 return ImmutableList.copyOf(measurementsByDescription.values()); 225 } 226 getMessages()227 @Override public ImmutableList<String> getMessages() { 228 return ImmutableList.of(); 229 } 230 } 231 } 232