Java tutorial
/* * 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.instrument; 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.StopMeasurementLogMessage; import com.google.caliper.core.BenchmarkClassModel.MethodModel; import com.google.caliper.core.InvalidBenchmarkException; import com.google.caliper.model.InstrumentSpec; import com.google.caliper.model.InstrumentType; import com.google.caliper.model.Measurement; import com.google.caliper.runner.config.VmConfig; import com.google.common.annotations.VisibleForTesting; import com.google.common.base.MoreObjects; import com.google.common.base.Objects; import com.google.common.base.Optional; import com.google.common.base.Predicates; 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 com.google.common.collect.MultimapBuilder; import javax.inject.Inject; public abstract class Instrument { protected ImmutableMap<String, String> options = ImmutableMap.of(); private String name = getClass().getSimpleName(); @VisibleForTesting @Inject public 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; } public String name() { return name; } @Override public String toString() { return name(); } public abstract boolean isBenchmarkMethod(MethodModel method); public abstract InstrumentedMethod createInstrumentedMethod(MethodModel benchmarkMethod) throws InvalidBenchmarkException; /** Indicates that trials using this instrument can be run in parallel with other trials. */ public boolean parallelizable() { return false; } /** The application of an instrument to a particular benchmark method. */ // TODO(gak): consider passing in Instrument explicitly for DI public abstract class InstrumentedMethod { protected MethodModel benchmarkMethod; protected InstrumentedMethod(MethodModel benchmarkMethod) { this.benchmarkMethod = checkNotNull(benchmarkMethod); } public final Instrument instrument() { return Instrument.this; } public final MethodModel benchmarkMethod() { return benchmarkMethod; } @Override public final boolean equals(Object obj) { if (obj == this) { return true; } else if (obj instanceof InstrumentedMethod) { InstrumentedMethod that = (InstrumentedMethod) 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 final String toString() { return MoreObjects.toStringHelper(InstrumentedMethod.class).add("instrument", Instrument.this) .add("benchmarkMethod", benchmarkMethod).toString(); } public abstract InstrumentType type(); /** * 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; } public abstract MeasurementCollectingVisitor getMeasurementCollectingVisitor(); /** * Subclasses can override this to validate results across all trials for a given instrumented * method. * * @return an optional warning message to be printed to the console. */ public Optional<String> validateMeasurements(Iterable<ImmutableList<Measurement>> results) { return Optional.absent(); } } public final ImmutableMap<String, String> options() { return options; } public 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. */ public ImmutableSet<String> getExtraCommandLineArgs(VmConfig vmConfig) { return ImmutableSet.of(); } /** * 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 = MultimapBuilder.hashKeys(requiredDescriptions.size()) .arrayListValues(requiredMeasurements).build(); } @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(); } } }