• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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