/*
* Copyright 2008-2009 LinkedIn, 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 voldemort.performance;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
import voldemort.TestUtils;
import voldemort.utils.Time;
public abstract class PerformanceTest {
private final AtomicInteger numberOfFailures = new AtomicInteger(0);
private long elapsedTimeNs;
private long[] operationTimes;
private volatile boolean hasCompleted;
private int numberOfThreads;
public abstract void doOperation(int index) throws Exception;
public void setUp() {
// override me to do stuff
}
public void tearDown() {
// override me to do stuff
}
public void run(int numRequests, int numThreads) {
setUp();
try {
this.numberOfThreads = numThreads;
this.hasCompleted = false;
this.numberOfFailures.set(0);
this.operationTimes = new long[numRequests];
ExecutorService executor = Executors.newFixedThreadPool(numThreads);
final CountDownLatch latch = new CountDownLatch(numRequests);
final AtomicInteger index = new AtomicInteger(0);
long start = System.nanoTime();
for(int i = 0; i < numRequests; i++) {
executor.execute(new Runnable() {
public void run() {
int current = index.getAndIncrement();
long begin = System.nanoTime();
try {
doOperation(current);
} catch(Exception e) {
numberOfFailures.getAndIncrement();
e.printStackTrace();
} finally {
operationTimes[current] = System.nanoTime() - begin;
latch.countDown();
}
}
});
}
try {
latch.await();
} catch(InterruptedException e) {
e.printStackTrace();
}
this.hasCompleted = true;
this.elapsedTimeNs = System.nanoTime() - start;
executor.shutdownNow();
try {
executor.awaitTermination(3, TimeUnit.SECONDS);
} catch(InterruptedException e) {}
} finally {
tearDown();
}
}
public void printStats() {
checkComplete();
System.out.println("Total number of operations: " + this.operationTimes.length);
System.out.println("Total elapsed seconds: " + this.elapsedTimeNs
/ (double) Time.NS_PER_SECOND);
System.out.println("Number of failures: " + this.numberOfFailures.get());
System.out.println("Number of threads: " + this.numberOfThreads);
System.out.println("Avg. operations/second: " + getOperationsPerSecond());
System.out.println("Average time: " + getAverageOperationTimeMs() + " ms");
System.out.println("Std dev.: " + getStandardDeviationMs() + " ms");
System.out.println("Median time: " + getOperationTimeMsQuantile(0.5d) + " ms");
System.out.println("1st percentile: " + getOperationTimeMsQuantile(0.01d) + " ms");
System.out.println("99th percentile: " + getOperationTimeMsQuantile(0.99d) + " ms");
}
public double getOperationsPerSecond() {
checkComplete();
double elapsedSeconds = this.elapsedTimeNs / (double) Time.NS_PER_SECOND;
return this.operationTimes.length / elapsedSeconds;
}
public double getOperationTimeMsQuantile(double quantile) {
checkComplete();
return TestUtils.quantile(this.operationTimes, quantile) / (double) Time.NS_PER_MS;
}
public double getAverageOperationTimeMs() {
checkComplete();
double mean = TestUtils.mean(this.operationTimes);
return mean / Time.NS_PER_MS;
}
public double getStandardDeviationMs() {
checkComplete();
double mean = TestUtils.mean(this.operationTimes);
double sum = 0.0;
for(int i = 0; i < this.operationTimes.length; i++)
sum += (this.operationTimes[i] - mean) * (this.operationTimes[i] - mean);
return Math.sqrt(sum / this.operationTimes.length) / Time.NS_PER_MS;
}
private void checkComplete() {
if(!hasCompleted)
throw new RuntimeException("Hasn't finished running yet!");
}
}