blob: e0bafaacadd807a67f4bf26c0abb7b2173256dd0 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 1998, 2012 Oracle and/or its affiliates. All rights reserved.
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License v1.0 and Eclipse Distribution License v. 1.0
* which accompanies this distribution.
* The Eclipse Public License is available at http://www.eclipse.org/legal/epl-v10.html
* and the Eclipse Distribution License is available at
* http://www.eclipse.org/org/documents/edl-v10.php.
*
* Contributors:
* Oracle - initial impl
******************************************************************************/
package example;
import java.util.ArrayList;
import java.util.List;
/**
* Generic performance test.
* This allows executing a timed run and measures and logs the performance.
* Logs the executions per run (minute), logs average and % stdev.
*/
public class PerformanceTest {
public static int REPEATS = 5;
public static long RUN_TIME = 10 * 10000L * 1000000L; //10 seconds (in nanos).
public static int THREADS = 1;
static WorkerThread[] threads;
/**
* Defines the work thread.
* A worker thread calls the run method in a loop,
* until it is suspended.
*/
protected static class WorkerThread extends Thread {
volatile boolean isSuspended = true;
volatile boolean isDead = false;
Runnable runnable;
long min;
long max;
long total;
int count;
public void stopExecution() {
isDead = true;
}
/**
* After the next completion of the run method, suspend execution.
* The thread is still alive, but waiting to be signaled to resumed.
*/
public void suspendExecution() {
isSuspended = true;
}
/**
* Wait for the thread to suspend.
* Synchronize will wait for wait to release synchronization.
*/
public synchronized void joinExecution() {
if (!isSuspended) {
throw new RuntimeException("Must suspend first");
}
}
/**
* Continue running the run method.
*/
public synchronized void resumeExecution() {
isSuspended = false;
min = Long.MAX_VALUE;
max = 0;
total = 0;
count = 0;
try {
notify();
} catch (Exception exception) {
throw new RuntimeException(exception.getMessage());
}
}
/**
* Run the test run method in a loop until killed.
*/
public synchronized void run() {
try {
while (!isDead) {
// Allows the thread to suspend itself when the current test is done.
if (isSuspended) {
wait();
}
long start = System.nanoTime();
this.runnable.run();
long end = System.nanoTime();
long time = end - start;
total = total + time;
if (time > max) {
max = time;
}
if (time < min) {
min = time;
}
count++;
}
} catch (Exception exception) {
exception.printStackTrace();
throw new RuntimeException(exception.getMessage());
}
}
}
/**
* Measure the performance of the run.
* Repeat the run REPEATS (5) times,
* and measure the number of execution in RUN_TIME (60s).
*/
public PerformanceResult executeRun(String name, Runnable runnable) {
if (THREADS > 1) {
return executeMultiThreadedRun(name, runnable);
}
System.out.println("Starting run: " + name);
List<Integer> results = new ArrayList<Integer>();
long max = 0;
long min = Long.MAX_VALUE;
// Repeat the test and baseline for the number of repeats.
for (int index = 0; index < REPEATS; index++) {
int executions = 0;
System.gc();
try {
Thread.sleep(1000);
} catch (InterruptedException ignore) {}
long startTime = System.nanoTime();
long endTime = startTime;
long lastEndTime = endTime;
// Count how many times the test can be invoked in the run time.
// This allows for the test run time to be easily changed.
while ((startTime + (RUN_TIME)) >= endTime) {
runnable.run();
executions++;
endTime = System.nanoTime();
long time = endTime - lastEndTime;
lastEndTime = endTime;
if (time > max) {
max = time;
}
if (time < min) {
min = time;
}
}
results.add(executions);
System.out.println("Done run: " + index + " for: " + name + " - " + executions);
reset();
}
System.out.println("Completed run: " + name);
PerformanceResult result = new PerformanceResult();
result.testName = name;
result.runRepeats = REPEATS;
result.runTime = RUN_TIME;
result.average = averageResults(results);
result.max = maxResults(results);
result.min = minResults(results);
result.standardDeviation = standardDeviationResults(results);
result.maxOperationTime = max;
result.minOperationTime = min;
result.averageOperationTime = (long) (RUN_TIME / result.average);
System.out.println("");
System.out.println(result);
System.out.println("");
System.out.println("");
return result;
}
/**
* Allow the test to reset after a run.
*/
public void reset() {
return;
}
/**
* Measure the performance of the run.
* Repeat the run REPEATS (5) times,
* and measure the number of execution in RUN_TIME (60s).
*/
public static PerformanceResult executeMultiThreadedRun(String name, Runnable runnable) {
System.out.println("Starting run: " + name);
threads = new WorkerThread[THREADS];
for (int index = 0; index < THREADS; index++) {
threads[index] = new WorkerThread();
}
for (int index = 0; index < THREADS; index++) {
threads[index].runnable = runnable;
}
for (int index = 0; index < THREADS; index++) {
threads[index].start();
}
long min = Long.MAX_VALUE;
long max = 0;
long totalTime = 0;
long totalExecutions = 0;
List<Integer> results = new ArrayList<Integer>();
// Repeat the test and baseline for the number of repeats.
for (int repeat = 0; repeat < REPEATS; repeat++) {
System.gc();
try {
Thread.sleep(1000);
} catch (InterruptedException ignore) {}
long startTime = System.nanoTime();
long endTime = startTime;
for (int index = 0; index < THREADS; index++) {
threads[index].resumeExecution();
}
while ((startTime + RUN_TIME) >= endTime) {
try {
Thread.sleep(1);
} catch (Exception ignore) {}
endTime = System.nanoTime();
}
for (int index = 0; index < THREADS; index++) {
threads[index].suspendExecution();
}
Thread.yield();
for (int index = 0; index < THREADS; index++) {
threads[index].joinExecution();
}
int executions = 0;
for (int index = 0; index < THREADS; index++) {
WorkerThread thread = threads[index];
executions = executions + thread.count;
totalTime = totalTime + thread.total;
if (thread.max > max) {
max = thread.max;
}
if (thread.min < min) {
min = thread.min;
}
}
totalExecutions = totalExecutions + executions;
results.add(executions);
System.out.println("Done run: " + repeat + " for: " + name);
}
for (int index = 0; index < THREADS; index++) {
threads[index].stopExecution();
}
Thread.yield();
threads = null;
System.out.println("Completed run: " + name);
PerformanceResult result = new PerformanceResult();
result.testName = name;
result.threads = THREADS;
result.runRepeats = REPEATS;
result.runTime = RUN_TIME;
result.average = averageResults(results);
result.max = maxResults(results);
result.min = minResults(results);
result.standardDeviation = standardDeviationResults(results);
result.maxOperationTime = max;
result.minOperationTime = min;
result.averageOperationTime = (totalTime / totalExecutions);
System.out.println("");
System.out.println(result);
System.out.println("");
System.out.println("");
return result;
}
/**
* Compute the max of the results.
*/
public static int maxResults(List<Integer> times) {
int testMax = 0;
for (int index = 0; index < times.size(); index++) {
int time = (int)times.get(index);
if (time > testMax) {
testMax = time;
}
}
return testMax;
}
/**
* Compute the min of the results.
*/
public static int minResults(List<Integer> times) {
int testMin = 0;
for (int index = 0; index < times.size(); index++) {
int time = (int)times.get(index);
if ((testMin == 0) || (time < testMin)) {
testMin = time;
}
}
return testMin;
}
/**
* Filter max and min from results.
*/
public static List<Integer> filterMaxMinResults(List<Integer> times) {
List filteredTimes = new ArrayList(times);
if (filteredTimes.size() > 3) {
filteredTimes.remove((Integer)maxResults(times));
filteredTimes.remove((Integer)minResults(times));
}
return filteredTimes;
}
/**
* Compute the average of the results rejecting the min and max.
*/
public static double averageResults(List<Integer> allTimes) {
// Compute the average reject the min and max to improve consistency.
List<Integer> times = filterMaxMinResults(allTimes);
double testAverage = 0;
for (int index = 0; index < times.size(); index++) {
int time = (int)times.get(index);
testAverage = testAverage + time;
}
testAverage = testAverage / times.size();
return testAverage;
}
/**
* Compute the standard deviation of the results rejecting the min and max.
*/
public static double standardDeviationResults(List<Integer> allTimes) {
// Compute the average reject the min and max to improve consistency.
double testAverage = averageResults(allTimes);
// Compute the standard deviation reject the min and max to improve consistency.
List<Integer> times = filterMaxMinResults(allTimes);
double testStandardDeviation = 0;
for (int index = 0; index < times.size(); index++) {
int time = (int)times.get(index);
testStandardDeviation = testStandardDeviation + Math.pow(time - testAverage, 2);
}
testStandardDeviation = testStandardDeviation / times.size();
testStandardDeviation = Math.sqrt(testStandardDeviation);
// As percent of average
testStandardDeviation = (testStandardDeviation / testAverage) * 100;
return testStandardDeviation;
}
}