/*
* Copyright 2010 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.benchmark;
import java.io.PrintStream;
import java.text.NumberFormat;
import java.util.HashMap;
class Results {
public int operations;
public long totalLatency, minLatency, maxLatency, q99Latency, q95Latency, medianLatency;
public Results(int ops, long minL, long maxL, long totalLat, long medL, long q95, long q99) {
this.operations = ops;
this.minLatency = minL;
this.maxLatency = maxL;
this.totalLatency = totalLat;
this.medianLatency = medL;
this.q99Latency = q99;
this.q95Latency = q95;
}
@Override
public String toString() {
StringBuffer buffer = new StringBuffer();
buffer.append("Operations = " + operations + "\n");
buffer.append("Min Latency = " + minLatency + "\n");
buffer.append("Max latency = " + maxLatency + "\n");
buffer.append("Median Latency = " + medianLatency + "\n");
buffer.append("95th percentile Latency = " + q95Latency + "\n");
buffer.append("99th percentile Latency = " + q99Latency + "\n");
return buffer.toString();
}
}
public class Measurement {
private String name;
public String getName() {
return name;
}
private int buckets;
private int[] histogram;
private int histogramOverflow;
private int windowOperations;
private long windowTotalLatency;
private int operations = 0;
private int minLatency = -1;
private int maxLatency = -1;
private long totalLatency = 0;
private HashMap<Integer, int[]> returnCodes;
private HashMap<Integer, int[]> warningCodes;
private boolean summaryOnly = false;
public Measurement(String name, boolean summaryOnly) {
this.name = name;
this.buckets = 3000; // Default bucket size of 3000 milliseconds
this.histogram = new int[buckets];
this.histogramOverflow = 0;
this.operations = 0;
this.totalLatency = 0;
this.windowOperations = 0;
this.windowTotalLatency = 0;
this.minLatency = -1;
this.maxLatency = -1;
this.returnCodes = new HashMap<Integer, int[]>();
this.warningCodes = new HashMap<Integer, int[]>();
this.summaryOnly = summaryOnly;
}
public synchronized void recordReturnCode(int code) {
Integer key = code;
if(!returnCodes.containsKey(key)) {
int[] val = new int[1];
val[0] = 0;
returnCodes.put(key, val);
}
returnCodes.get(key)[0]++;
}
public synchronized void recordWarningCode(int code) {
Integer key = code;
if(!warningCodes.containsKey(key)) {
int[] val = new int[1];
val[0] = 0;
warningCodes.put(key, val);
}
warningCodes.get(key)[0]++;
}
public synchronized void recordLatency(int latency) {
if(latency >= buckets) {
histogramOverflow++;
} else {
histogram[latency]++;
}
operations++;
totalLatency += latency;
windowOperations++;
windowTotalLatency += latency;
if((minLatency < 0) || (latency < minLatency)) {
minLatency = latency;
}
if((maxLatency < 0) || (latency > maxLatency)) {
maxLatency = latency;
}
}
public Results generateResults() {
int median = 0, q95 = 0, q99 = 0;
int opcounter = 0;
boolean done95th = false, done50th = false;
for(int i = 0; i < buckets; i++) {
opcounter += histogram[i];
double currentQuartile = ((double) opcounter) / ((double) operations);
if(!done50th && currentQuartile >= 0.50) {
median = i;
done50th = true;
}
if(!done95th && currentQuartile >= 0.95) {
q95 = i;
done95th = true;
}
if(currentQuartile >= 0.99) {
q99 = i;
break;
}
}
return new Results(operations, minLatency, maxLatency, totalLatency, median, q95, q99);
}
public void printReport(PrintStream out) {
Results result = generateResults();
NumberFormat nf = NumberFormat.getInstance();
nf.setMaximumFractionDigits(4);
nf.setGroupingUsed(false);
out.println("[" + getName() + "]\tOperations: " + operations);
out.println("[" + getName() + "]\tAverage(ms): "
+ nf.format((((double) totalLatency) / ((double) operations))));
out.println("[" + getName() + "]\tMin(ms): " + nf.format(minLatency));
out.println("[" + getName() + "]\tMax(ms): " + nf.format(maxLatency));
out.println("[" + getName() + "]\tMedian(ms): " + nf.format(result.medianLatency));
out.println("[" + getName() + "]\t95th(ms): " + nf.format(result.q95Latency));
out.println("[" + getName() + "]\t99th(ms): " + nf.format(result.q99Latency));
for(Integer i: returnCodes.keySet()) {
int[] val = returnCodes.get(i);
out.println("[" + getName() + "]\tReturn: " + VoldemortWrapper.ReturnCode.values()[i] + "\t" + val[0]);
}
for(Integer i: warningCodes.keySet()) {
int[] val = returnCodes.get(i);
out.println("[" + getName() + "]\tWarning: " + VoldemortWrapper.WarningCode.values()[i] + "\t" + val[0]);
}
if(!this.summaryOnly) {
for(int i = 0; i < buckets; i++) {
out.println("[" + getName() + "]: " + i + "\t" + histogram[i]);
}
out.println("[" + getName() + "]: >" + buckets + "\t" + histogramOverflow);
}
}
}