com.google.caliper.runner.instrument.Instrument.java Source code

Java tutorial

Introduction

Here is the source code for com.google.caliper.runner.instrument.Instrument.java

Source

/*
 * 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();
        }
    }
}