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

Java tutorial

Introduction

Here is the source code for com.google.caliper.runner.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;

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.model.InstrumentSpec;
import com.google.caliper.model.Measurement;
import com.google.caliper.worker.Worker;
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 com.google.inject.Inject;

import java.lang.reflect.Method;

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 Objects.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();
    }

    /**
     * Some default JVM args to keep worker VMs somewhat predictable.
     */
    static final ImmutableSet<String> JVM_ARGS = ImmutableSet.of(
            // do compilation serially
            "-Xbatch",
            // make sure compilation doesn't run in parallel with itself
            "-XX:CICompilerCount=1",
            // ensure the parallel garbage collector
            "-XX:+UseParallelGC",
            // generate classes or don't, but do it immediately
            "-Dsun.reflect.inflationThreshold=0");

    /**
     * Returns some arguments that should be added to the command line when invoking
     * this instrument's worker.
     */
    ImmutableSet<String> getExtraCommandLineArgs() {
        return JVM_ARGS;
    }

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