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

Java tutorial

Introduction

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

Source

/*
 * Copyright (C) 2009 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 com.google.caliper.Benchmark;
import com.google.caliper.Environment;
import com.google.caliper.MeasurementSet;
import com.google.caliper.MeasurementType;
import com.google.caliper.Result;
import com.google.caliper.Runner;
import com.google.caliper.Scenario;
import com.google.caliper.ScenarioResult;
import com.google.caliper.ScenarioSelection;
import com.google.caliper.SuiteRun;
import com.google.caliper.util.DebugMeasurer;
import com.google.caliper.util.CommandLineParser;
import com.google.caliper.util.InterleavedReader;
import com.google.caliper.report.ReportGenerator;
import com.google.caliper.vm.Vm;
import com.google.caliper.vm.VmFactory;
import com.google.caliper.exception.ConfigurationException;
import com.google.caliper.exception.UserException;
import com.google.caliper.exception.UserException.DisplayUsageException;
import com.google.caliper.exception.UserException.ExceptionFromUserCodeException;
import com.google.caliper.report.DefaultRendererRegistry;
import com.google.caliper.report.RendererRegistry;
import com.google.common.base.Joiner;
import com.google.common.base.Splitter;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ObjectArrays;
import com.google.common.io.Closeables;
import java.io.File;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.regex.Pattern;

/**
 * Creates, executes and reports benchmark runs.
 */
public final class DefaultRunner implements Runner {

    private static final Splitter ARGUMENT_SPLITTER = Splitter.on(Pattern.compile("\\s+")).omitEmptyStrings();

    /** Command line arguments to the process */
    private CommandLineParser arguments;
    private ScenarioSelection scenarioSelection;
    private final String DEFAULT_PLUGIN_LOCATION = "plugin";

    public void run(String... args) {
        this.arguments = CommandLineParser.parse(args);

        this.scenarioSelection = new ScenarioSelection(arguments);
        if (arguments.getDebug()) {
            debug();
            return;
        }

        File pluginFolder = arguments.getPluginFolder();
        RendererRegistry registry = new DefaultRendererRegistry(
                (null != pluginFolder) ? pluginFolder : new File(DEFAULT_PLUGIN_LOCATION), arguments);

        final String reportType = arguments.getReportType();
        if (registry.containsRenderer(reportType)) {
            Result result = runOutOfProcess();
            new ReportGenerator(result.getRun(), registry, arguments).displayResults();
        } else {
            System.out.println("No renderers found for type: " + reportType);
            System.out.println("Available renderers are:");
            for (String renderer : registry.listRenderers()) {
                System.out.println("  - " + renderer + "  : v" + registry.getRenderer(renderer).getVersion());
            }
        }
    }

    private ScenarioResult runScenario(Scenario scenario) {
        MeasurementResult timeMeasurementResult = measure(scenario, MeasurementType.TIME);
        MeasurementSet allocationMeasurements = null;
        String allocationEventLog = null;
        MeasurementSet memoryMeasurements = null;
        String memoryEventLog = null;
        if (arguments.getMeasureMemory()) {
            MeasurementResult allocationsMeasurementResult = measure(scenario, MeasurementType.INSTANCE);
            allocationMeasurements = allocationsMeasurementResult.getMeasurements();
            allocationEventLog = allocationsMeasurementResult.getEventLog();

            MeasurementResult memoryMeasurementResult = measure(scenario, MeasurementType.MEMORY);
            memoryMeasurements = memoryMeasurementResult.getMeasurements();
            memoryEventLog = memoryMeasurementResult.getEventLog();
        }

        return new ScenarioResult(timeMeasurementResult.getMeasurements(), timeMeasurementResult.getEventLog(),
                allocationMeasurements, allocationEventLog, memoryMeasurements, memoryEventLog);
    }

    private class MeasurementResult {

        private final MeasurementSet measurements;
        private final String eventLog;

        MeasurementResult(MeasurementSet measurements, String eventLog) {
            this.measurements = measurements;
            this.eventLog = eventLog;
        }

        public MeasurementSet getMeasurements() {
            return measurements;
        }

        public String getEventLog() {
            return eventLog;
        }
    }

    private MeasurementResult measure(Scenario scenario, MeasurementType type) {
        Vm vm = new VmFactory().createVm(scenario);
        // this must be done before starting the forked process on certain VMs
        List<String> command = createCommand(scenario, vm, type);
        Process timeProcess = startForkedProcess(command);

        MeasurementSet measurementSet = null;
        StringBuilder eventLog = new StringBuilder();
        InterleavedReader reader = null;
        try {
            reader = new InterleavedReader(arguments.getMarker(),
                    new InputStreamReader(timeProcess.getInputStream()));
            Object o;
            while ((o = reader.read()) != null) {
                if (o instanceof String) {
                    eventLog.append(o);
                } else if (measurementSet == null) {
                    measurementSet = (MeasurementSet) o;
                } else {
                    throw new RuntimeException("Unexpected value: " + o);
                }
            }
        } catch (Exception e) {
            throw new RuntimeException(e);
        } finally {
            Closeables.closeQuietly(reader);
            timeProcess.destroy();
        }

        if (measurementSet == null) {
            String message = "Failed to execute " + Joiner.on(" ").join(command);
            System.err.println("  " + message);
            System.err.println(eventLog.toString());
            throw new ConfigurationException(message);
        }

        return new MeasurementResult(measurementSet, eventLog.toString());
    }

    private List<String> createCommand(Scenario scenario, Vm vm, MeasurementType type) {
        String classPath = System.getProperty("java.class.path");
        if (classPath == null || classPath.length() == 0) {
            throw new IllegalStateException("java.class.path is undefined in " + System.getProperties());
        }

        ImmutableList.Builder<String> command = ImmutableList.builder();
        command.addAll(ARGUMENT_SPLITTER.split(scenario.getVariables().get(Scenario.VM_KEY)));
        command.add("-cp").add(classPath);
        if (type == MeasurementType.INSTANCE || type == MeasurementType.MEMORY) {
            String allocationJarFile = System.getenv("ALLOCATION_JAR");
            command.add("-javaagent:" + allocationJarFile);
        }
        command.addAll(vm.getVmSpecificOptions(type, arguments));

        Map<String, String> vmParameters = scenario.getVariables(scenarioSelection.getVmParameterNames());
        for (String vmParameter : vmParameters.values()) {
            command.addAll(ARGUMENT_SPLITTER.split(vmParameter));
        }

        command.add(InProcessRunner.class.getName());
        createCommand(command, scenario, type);
        return command.build();
    }

    private void createCommand(ImmutableList.Builder<String> command, Scenario scenario, MeasurementType type) {
        command.add("--warmupMillis").add(Long.toString(arguments.getWarmupMillis()));
        command.add("--runMillis").add(Long.toString(arguments.getRunMillis()));

        Map<String, String> userParameters = scenario.getVariables(scenarioSelection.getUserParameterNames());
        for (Entry<String, String> entry : userParameters.entrySet()) {
            command.add("-D" + entry.getKey() + "=" + entry.getValue());
        }

        command.add("--measurementType").add(type.toString());
        command.add("--marker").add(arguments.getMarker());
        command.add(arguments.getSuiteClassName());
    }

    private Process startForkedProcess(List<String> command) {
        ProcessBuilder builder = new ProcessBuilder();
        builder.command(command);
        builder.redirectErrorStream(true);
        builder.directory(new File(System.getProperty("user.dir")));
        try {
            return builder.start();
        } catch (IOException e) {
            throw new RuntimeException("failed to start subprocess", e);
        }
    }

    private void debug() {
        try {
            int debugReps = arguments.getDebugReps();
            InProcessRunner runner = new InProcessRunner();
            DebugMeasurer measurer = new DebugMeasurer(debugReps);
            for (Scenario scenario : scenarioSelection.select()) {
                runner.run(scenarioSelection, scenario, measurer);
            }
        } catch (Exception e) {
            throw new ExceptionFromUserCodeException(e);
        }
    }

    private Result runOutOfProcess() {
        Date executedDate = new Date();
        ImmutableMap.Builder<Scenario, ScenarioResult> resultsBuilder = ImmutableMap.builder();

        try {
            List<Scenario> scenarios = scenarioSelection.select();

            int i = 0;
            for (Scenario scenario : scenarios) {
                beforeMeasurement(i++, scenarios.size(), scenario);
                ScenarioResult scenarioResult = runScenario(scenario);
                afterMeasurement(arguments.getMeasureMemory(), scenarioResult);
                resultsBuilder.put(scenario, scenarioResult);
            }

            Environment environment = new EnvironmentGetter().getEnvironmentSnapshot();
            return new Result(new SuiteRun(resultsBuilder.build(), arguments.getSuiteClassName(), executedDate),
                    environment);
        } catch (Exception e) {
            throw new ExceptionFromUserCodeException(e);
        }
    }

    private void beforeMeasurement(int index, int total, Scenario scenario) {
        double percentDone = (double) index / total;
        System.out.printf("%2.0f%% %s", percentDone * 100, scenario);
    }

    private void afterMeasurement(boolean memoryMeasured, ScenarioResult scenarioResult) {
        String memoryMeasurements = "";
        if (memoryMeasured) {
            MeasurementSet instanceMeasurementSet = scenarioResult.getMeasurementSet(MeasurementType.INSTANCE);
            String instanceUnit = ReportGenerator.UNIT_ORDERING
                    .min(instanceMeasurementSet.getUnitNames().entrySet()).getKey();
            MeasurementSet memoryMeasurementSet = scenarioResult.getMeasurementSet(MeasurementType.MEMORY);
            String memoryUnit = ReportGenerator.UNIT_ORDERING.min(memoryMeasurementSet.getUnitNames().entrySet())
                    .getKey();
            memoryMeasurements = String.format(", allocated %s%s for a total of %s%s",
                    Math.round(instanceMeasurementSet.medianUnits()), instanceUnit,
                    Math.round(memoryMeasurementSet.medianUnits()), memoryUnit);
        }

        MeasurementSet timeMeasurementSet = scenarioResult.getMeasurementSet(MeasurementType.TIME);
        String unit = ReportGenerator.UNIT_ORDERING.min(timeMeasurementSet.getUnitNames().entrySet()).getKey();
        System.out.printf(" %.2f %s; \u03C3=%.2f %s @ %d trials%s%n", timeMeasurementSet.medianUnits(), unit,
                timeMeasurementSet.standardDeviationUnits(), unit, timeMeasurementSet.getMeasurements().size(),
                memoryMeasurements);
    }

    public static void main(String... args) {
        try {
            new DefaultRunner().run(args);
            System.exit(0); // user code may have leave non-daemon threads behind!
        } catch (DisplayUsageException e) {
            e.display();
            System.exit(0);
        } catch (UserException e) {
            e.display();
            System.exit(1);
        }
    }

    public static void main(Class<? extends Benchmark> suite, String... args) {
        main(ObjectArrays.concat(args, suite.getName()));
    }
}