com.github.bluetiger9.nosql.benchmarking.benchmarks.GenericPerformanceBenchmark.java Source code

Java tutorial

Introduction

Here is the source code for com.github.bluetiger9.nosql.benchmarking.benchmarks.GenericPerformanceBenchmark.java

Source

/*
 * Copyright (c) 2013 Attila Tks. All rights reserved.
 * 
 * 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.github.bluetiger9.nosql.benchmarking.benchmarks;

import java.io.File;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.concurrent.atomic.AtomicInteger;

import org.apache.commons.math3.distribution.EnumeratedDistribution;
import org.apache.commons.math3.util.Pair;
import org.apache.log4j.Logger;

import com.github.bluetiger9.nosql.benchmarking.Util;
import com.github.bluetiger9.nosql.benchmarking.clients.ClientException;
import com.github.bluetiger9.nosql.benchmarking.clients.DatabaseClient;
import com.github.bluetiger9.nosql.benchmarking.util.ExportTools;
import com.github.bluetiger9.nosql.benchmarking.util.StopWatch;
import com.github.bluetiger9.nosql.benchmarking.util.TimeMeasurment;

public abstract class GenericPerformanceBenchmark<ClientType extends DatabaseClient>
        extends MultiThreadedBenchmark<ClientType> {
    private final AtomicInteger taskCount = new AtomicInteger(0);
    private final int numOps;
    private final int throughput;

    private final List<BenchmarkTask> benchmarkTasks;

    public GenericPerformanceBenchmark(Properties props) {
        super(props);
        this.numOps = Integer.parseInt(Util.getMandatoryProperty(props, "operations"));
        this.throughput = Integer.parseInt(Util.getMandatoryProperty(props, "throughput"));
        this.benchmarkTasks = new ArrayList<>();
    }

    protected abstract List<Pair<String, Double>> getAvailableOperations();

    protected void addBenchmarkTask(BenchmarkTask task) {
        this.benchmarkTasks.add(task);
    }

    @Override
    public void exportResults(File outputFolder) throws IOException {
        final PrintWriter writer = new PrintWriter(new File(outputFolder, "summary.txt"));
        writer.println("Operations: " + numOps);
        writer.println("Target throughput: " + throughput + " op/sec");
        double actualThroughput = 0;
        for (BenchmarkTask task : benchmarkTasks) {
            actualThroughput += task.getActualThroughput();
        }
        writer.println("Actual throughput: " + actualThroughput + " op/sec");
        writer.println();

        for (Pair<String, Double> operationPair : getAvailableOperations()) {
            exportOperationResults(writer, operationPair.getFirst(), outputFolder);
        }
        writer.close();
    }

    private void exportOperationResults(PrintWriter writer, String operation, File outputFolder) {
        long sum = 0;
        long min = Long.MAX_VALUE;
        long max = -1;
        long count = 0;
        for (BenchmarkTask task : benchmarkTasks) {
            for (TimeMeasurment tm : task.getLatencies().get(operation)) {
                long latency = tm.getLatency();
                count++;
                sum += latency;
                if (latency < min)
                    min = latency;
                if (latency > max)
                    max = latency;
            }
        }

        if (count != 0) {
            writer.println("# " + operation + " latencies");
            writer.println(String.format("%s.ops=%d", operation, count));
            writer.println(String.format("%s.avg=%.4f ms", operation, sum / 1000000.0 / count));
            writer.println(String.format("%s.min=%.4f ms", operation, min / 1000000.0));
            writer.println(String.format("%s.max=%.4f ms", operation, max / 1000000.0));
            writer.println();

            try {
                final File tsFile = new File(outputFolder, operation + ".txt");
                for (BenchmarkTask task : benchmarkTasks) {
                    ExportTools.exportTimeSeries(task.getLatencies().get(operation), tsFile);
                }
            } catch (IOException e) {
                logger.error(e);
            }
        }
    }

    protected abstract class BenchmarkTask implements MultiThreadedBenchmark.BenchmarkTask<ClientType> {
        protected final Logger logger;
        protected final int taskNr;
        private final int nrOps;
        private final double timeBeetweenOps;
        private final EnumeratedDistribution<String> opGenerator;
        private final StopWatch stopWatch;
        protected ClientType client;
        private final Map<String, List<TimeMeasurment>> latencies;
        private volatile double actualThroughput;

        public BenchmarkTask() {
            this.taskNr = taskCount.incrementAndGet();
            this.logger = Logger.getLogger(this.getClass().getSimpleName() + "-" + taskNr);
            this.nrOps = numOps / nrThreads;
            final int localThroughput = throughput / nrThreads;
            this.timeBeetweenOps = 1000.0 / localThroughput;
            logger.info("Target th: " + localThroughput + ", ms/op = " + timeBeetweenOps);
            this.stopWatch = new StopWatch();
            this.opGenerator = new EnumeratedDistribution<>(getAvailableOperations());
            this.latencies = new HashMap<>();
            for (Pair<String, Double> opPair : getAvailableOperations()) {
                latencies.put(opPair.getFirst(), new ArrayList<TimeMeasurment>());
            }
        }

        public abstract void doOperation(String op) throws ClientException;

        public void init(ClientType client) {
            this.client = client;

            try {
                client.connect();
                logger.info("Database client connected.");
            } catch (ClientException e) {
                logger.error("Failed to connect to the database");
            }
        }

        @Override
        public void run() {
            final long startTime = System.currentTimeMillis();
            for (int i = 1; i <= nrOps; ++i) {
                final long time = System.currentTimeMillis();
                try {
                    final String operation = opGenerator.sample();
                    stopWatch.start();
                    doOperation(operation);
                    final long latency = stopWatch.stop();
                    latencies.get(operation).add(new TimeMeasurment(time, latency));
                } catch (ClientException e) {
                    logger.error("Error when executing operation: ", e);
                }

                try {
                    while (startTime + i * timeBeetweenOps > System.currentTimeMillis()) {
                        Thread.sleep(1);
                    }
                } catch (InterruptedException e) {
                }
            }
            final long totalTime = System.currentTimeMillis() - startTime;
            actualThroughput = 1000.0 * nrOps / totalTime;
            logger.info(String.format("Completed %s operations in %s ms. Actual throughput: %s ops/sec", nrOps,
                    totalTime, actualThroughput));
        }

        @Override
        public void cleanUp() {
            logger.info("Disconnecting...");
            try {
                client.disconnect();
            } catch (ClientException e) {
                logger.error("Disconnect failed: ", e);
            }
        }

        public Map<String, List<TimeMeasurment>> getLatencies() {
            return latencies;
        }

        public double getActualThroughput() {
            return actualThroughput;
        }

    }

}