blob: 0d6bc737d1da48f5e302593af911caeb9abc1ed5 [file] [log] [blame]
/*
* Copyright (C) 2011 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.runner;
import static com.google.common.base.Preconditions.checkArgument;
import static com.google.common.base.Preconditions.checkNotNull;
import com.google.caliper.bridge.AbstractLogMessageVisitor;
import com.google.caliper.bridge.LogMessageVisitor;
import com.google.caliper.bridge.StopMeasurementLogMessage;
import com.google.caliper.config.VmConfig;
import com.google.caliper.model.InstrumentSpec;
import com.google.caliper.model.Measurement;
import com.google.caliper.worker.Worker;
import com.google.common.base.MoreObjects;
import com.google.common.base.Objects;
import com.google.common.base.Predicates;
import com.google.common.collect.ArrayListMultimap;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.ListMultimap;
import com.google.common.collect.Maps;
import java.lang.reflect.Method;
import javax.inject.Inject;
public abstract class Instrument {
protected ImmutableMap<String, String> options;
private String name = getClass().getSimpleName();
@Inject void setOptions(@InstrumentOptions ImmutableMap<String, String> options) {
this.options = ImmutableMap.copyOf(
Maps.filterKeys(options, Predicates.in(instrumentOptions())));
}
@Inject void setInstrumentName(@InstrumentName String name) {
this.name = name;
}
String name() {
return name;
}
@Override public String toString() {
return name();
}
public abstract boolean isBenchmarkMethod(Method method);
public abstract Instrumentation createInstrumentation(Method benchmarkMethod)
throws InvalidBenchmarkException;
/**
* Indicates that trials using this instrument can be run in parallel with other trials.
*/
public abstract TrialSchedulingPolicy schedulingPolicy();
/**
* The application of an instrument to a particular benchmark method.
*/
// TODO(gak): consider passing in Instrument explicitly for DI
public abstract class Instrumentation {
protected Method benchmarkMethod;
protected Instrumentation(Method benchmarkMethod) {
this.benchmarkMethod = checkNotNull(benchmarkMethod);
}
Instrument instrument() {
return Instrument.this;
}
Method benchmarkMethod() {
return benchmarkMethod;
}
@Override
public final boolean equals(Object obj) {
if (obj == this) {
return true;
} else if (obj instanceof Instrumentation) {
Instrumentation that = (Instrumentation) obj;
return Instrument.this.equals(that.instrument())
&& this.benchmarkMethod.equals(that.benchmarkMethod);
}
return super.equals(obj);
}
@Override
public final int hashCode() {
return Objects.hashCode(Instrument.this, benchmarkMethod);
}
@Override
public String toString() {
return MoreObjects.toStringHelper(Instrumentation.class)
.add("instrument", Instrument.this)
.add("benchmarkMethod", benchmarkMethod)
.toString();
}
public abstract void dryRun(Object benchmark) throws InvalidBenchmarkException;
public abstract Class<? extends Worker> workerClass();
/**
* Return the subset of options (and possibly a transformation thereof) to be used in the
* worker. Returns all instrument options by default.
*/
public ImmutableMap<String, String> workerOptions() {
return options;
}
abstract MeasurementCollectingVisitor getMeasurementCollectingVisitor();
}
public final ImmutableMap<String, String> options() {
return options;
}
final InstrumentSpec getSpec() {
return new InstrumentSpec.Builder()
.instrumentClass(getClass())
.addAllOptions(options())
.build();
}
/**
* Defines the list of options applicable to this instrument. Implementations that use options
* will need to override this method.
*/
protected ImmutableSet<String> instrumentOptions() {
return ImmutableSet.of();
}
/**
* Returns some arguments that should be added to the command line when invoking
* this instrument's worker.
*
* @param vmConfig the configuration for the VM on which this is running.
*/
ImmutableSet<String> getExtraCommandLineArgs(VmConfig vmConfig) {
return vmConfig.commonInstrumentVmArgs();
}
interface MeasurementCollectingVisitor extends LogMessageVisitor {
boolean isDoneCollecting();
boolean isWarmupComplete();
ImmutableList<Measurement> getMeasurements();
/**
* Returns all the messages created while collecting measurments.
*
* <p>A message is some piece of user visible data that should be displayed to the user along
* with the trial results.
*
* <p>TODO(lukes): should we model these as anything more than strings. These messages already
* have a concept of 'level' based on the prefix.
*/
ImmutableList<String> getMessages();
}
/**
* A default implementation of {@link MeasurementCollectingVisitor} that collects measurements for
* pre-specified descriptions.
*/
protected static final class DefaultMeasurementCollectingVisitor
extends AbstractLogMessageVisitor implements MeasurementCollectingVisitor {
static final int DEFAULT_NUMBER_OF_MEASUREMENTS = 9;
final ImmutableSet<String> requiredDescriptions;
final ListMultimap<String, Measurement> measurementsByDescription;
final int requiredMeasurements;
DefaultMeasurementCollectingVisitor(ImmutableSet<String> requiredDescriptions) {
this(requiredDescriptions, DEFAULT_NUMBER_OF_MEASUREMENTS);
}
DefaultMeasurementCollectingVisitor(ImmutableSet<String> requiredDescriptions,
int requiredMeasurements) {
this.requiredDescriptions = requiredDescriptions;
checkArgument(!requiredDescriptions.isEmpty());
this.requiredMeasurements = requiredMeasurements;
checkArgument(requiredMeasurements > 0);
this.measurementsByDescription =
ArrayListMultimap.create(requiredDescriptions.size(), requiredMeasurements);
}
@Override public void visit(StopMeasurementLogMessage logMessage) {
for (Measurement measurement : logMessage.measurements()) {
measurementsByDescription.put(measurement.description(), measurement);
}
}
@Override public boolean isDoneCollecting() {
for (String description : requiredDescriptions) {
if (measurementsByDescription.get(description).size() < requiredMeasurements) {
return false;
}
}
return true;
}
@Override
public boolean isWarmupComplete() {
return true;
}
@Override public ImmutableList<Measurement> getMeasurements() {
return ImmutableList.copyOf(measurementsByDescription.values());
}
@Override public ImmutableList<String> getMessages() {
return ImmutableList.of();
}
}
}