Package org.prevayler.demos.scalability

Source Code of org.prevayler.demos.scalability.ScalabilityTestRun

package org.prevayler.demos.scalability;

import org.prevayler.foundation.Cool;
import org.prevayler.foundation.StopWatch;

import java.text.DecimalFormat;
import java.util.LinkedList;
import java.util.List;

/**
* Represents a single run of a scalability test. To understand the implementation of this class, you must be familiar with Prevayler's Scalability Test (run org.prevayler.test.scalability.ScalabilityTest).
*/
abstract class ScalabilityTestRun {

  static private final long ROUND_DURATION_MILLIS = 1000 * 20;

  private final ScalabilityTestSubject subject;
  protected final int numberOfObjects;

  private double bestRoundOperationsPerSecond;
  private int bestRoundThreads;

  private final List connectionCache = new LinkedList();

  private long operationCount = 0;
  private long lastOperation = 0;
  private boolean isRoundFinished;
  private int activeRoundThreads = 0;


  /**
   * @return Example: "123.12 operations/second (12 threads)".
   */
  public String getResult() {
    return toResultString(bestRoundOperationsPerSecond, bestRoundThreads);
  }


  public double getOperationsPerSecond() {
    return bestRoundOperationsPerSecond;
  }


  protected ScalabilityTestRun(ScalabilityTestSubject subject, int numberOfObjects, int minThreads, int maxThreads) {
    if (minThreads > maxThreads)
      throw new IllegalArgumentException("The minimum number of threads cannot be greater than the maximum number.");
    if (minThreads < 1) throw new IllegalArgumentException("The minimum number of threads cannot be smaller than one.");

    this.subject = subject;
    this.numberOfObjects = numberOfObjects;

    out("\n\n========= Running " + name() + " (" + (maxThreads - minThreads + 1) + " rounds). Subject: " + subject.name() + "...");
    prepare();

    out("Each round will take approx. " + ROUND_DURATION_MILLIS / 1000 + " seconds to run...");
    performTest(minThreads, maxThreads);
    out("\n----------- BEST ROUND: " + getResult());
  }

  protected void prepare() {
    subject.replaceAllRecords(numberOfObjects);
    System.gc();
  }


  /**
   * @return The name of the test to be executed. Example: "Prevayler Query Test".
   */
  protected abstract String name();


  private void performTest(int minThreads, int maxThreads) {

    int threads = minThreads;
    while (threads <= maxThreads) {
      double operationsPerSecond = performRound(threads);

      if (operationsPerSecond > bestRoundOperationsPerSecond) {
        bestRoundOperationsPerSecond = operationsPerSecond;
        bestRoundThreads = threads;
      }

      threads++;
    }
  }


  /**
   * @return The number of operations the test managed to execute per second with the given number of threads.
   */
  private double performRound(int threads) {
    long initialOperationCount = operationCount;
    StopWatch stopWatch = StopWatch.start();

    startThreads(threads);
    sleep();
    stopThreads();

    double secondsEllapsed = stopWatch.secondsEllapsed();
    double operationsPerSecond = (operationCount - initialOperationCount) / secondsEllapsed;

    out("\nMemory used: " + Runtime.getRuntime().totalMemory());
    subject.reportResourcesUsed(System.out);
    out("Seconds ellapsed: " + secondsEllapsed);
    out("--------- Round Result: " + toResultString(operationsPerSecond, threads));

    return operationsPerSecond;
  }


  private void startThreads(int threads) {
    isRoundFinished = false;

    int i = 1;
    while (i <= threads) {
      startThread(lastOperation + i, threads);
      i++;
    }
  }


  private void startThread(final long startingOperation, final int operationIncrement) {
    (new Thread() {
      public void run() {
        try {
          Object connection = acquireConnection();

          long operation = startingOperation;
          while (!isRoundFinished) {
            executeOperation(connection, operation);
            operation += operationIncrement;
          }

          synchronized (connectionCache) {
            connectionCache.add(connection);
            operationCount += (operation - startingOperation) / operationIncrement;
            if (lastOperation < operation) lastOperation = operation;
            activeRoundThreads--;
          }

        } catch (OutOfMemoryError err) {
          outOfMemory();
        }
      }
    }).start();

    activeRoundThreads++;
  }


  protected abstract void executeOperation(Object connection, long operation);


  private Object acquireConnection() {
    synchronized (connectionCache) {
      return connectionCache.isEmpty()
          ? subject.createTestConnection()
          : connectionCache.remove(0);
    }
  }


  private void stopThreads() {
    isRoundFinished = true;
    while (activeRoundThreads != 0) {
      Thread.yield();
    }
  }


  static private String toResultString(double operationsPerSecond, int threads) {
    String operations = new DecimalFormat("0.00").format(operationsPerSecond);
    return "" + operations + " operations/second (" + threads + " threads)";
  }

  static void outOfMemory() {
    System.gc();
    out(
        "\n\nOutOfMemoryError.\n" +
            "===========================================================\n" +
            "The VM must be started with a sufficient maximum heap size.\n" +
            "Example for Linux and Windows:  java -Xmx512000000 ...\n\n"
    );
  }

  static private void sleep() {
    Cool.sleep(ROUND_DURATION_MILLIS);
  }


  static private void out(Object obj) {
    System.out.println(obj);
  }
}
TOP

Related Classes of org.prevayler.demos.scalability.ScalabilityTestRun

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.