package org.radargun.stats; import org.radargun.config.DefinitionElement; import org.radargun.stats.representation.BoxAndWhiskers; import org.radargun.stats.representation.DefaultOutcome; import org.radargun.stats.representation.MeanAndDev; import org.radargun.stats.representation.OperationThroughput; /** * Underlying statistical data gathered for single operation type. * * @author Radim Vansa <rvansa@redhat.com> */ @DefinitionElement(name = "basic", doc = "Operations statistics with fixed memory footprint.") public class BasicOperationStats implements OperationStats { private static final double INVERSE_NORMAL_95 = 1.96; private static final double INVERSE_NORMAL_50 = 0.67448; private long requests; private long responseTimeMax = Long.MIN_VALUE; private long responseTimeSum; private double responseTimeMean; // first moment private double responseTimeM2; // second moment, var = M2 / (n - 1) private long errors; @Override public BasicOperationStats newInstance() { return new BasicOperationStats(); } @Override public BasicOperationStats copy() { BasicOperationStats copy = newInstance(); copy.requests = requests; copy.responseTimeMax = responseTimeMax; copy.responseTimeSum = responseTimeSum; copy.responseTimeMean = responseTimeMean; copy.responseTimeM2 = responseTimeM2; copy.errors = errors; return copy; } @Override public void merge(OperationStats o) { if (!(o instanceof BasicOperationStats)) throw new IllegalArgumentException(o.toString()); BasicOperationStats other = (BasicOperationStats) o; responseTimeM2 = mergeM2(responseTimeMean, responseTimeM2, requests, other.responseTimeMean, other.responseTimeM2, other.requests); responseTimeMean = mergeMean(responseTimeMean, requests, other.responseTimeMean, other.requests); requests += other.requests; responseTimeMax = Math.max(responseTimeMax, other.responseTimeMax); responseTimeSum += other.responseTimeSum; errors += other.errors; } private static double mergeMean(double myMean, double myN, double otherMean, double otherN) { if (myN + otherN == 0) return .0; return (myMean * myN + otherMean * otherN) / (myN + otherN); } private static double mergeM2(double myMean, double myM2, double myN, double otherMean, double otherM2, double otherN) { if (myN + otherN == 0) return .0; double delta = myMean - otherMean; return myM2 + otherM2 + delta * delta * otherN * myN / (otherN + myN); } public String toString() { return requests == 0 ? "requests=0" : String.format("requests=%d, responseTimeMax=%d, responseTimeSum=%d, errors=%d", requests, responseTimeMax, responseTimeSum, errors); } @Override public void record(Request request) { record(request.duration()); if (!request.isSuccessful()) { errors++; } } @Override public void record(Message message) { if (message.isValid()) { record(message.totalTime()); } else { errors++; } } @Override public void record(RequestSet requestSet) { record(requestSet.sumDurations()); if (!requestSet.isSuccessful()) { errors++; } } public void record(long duration) { requests++; responseTimeMax = Math.max(responseTimeMax, duration); responseTimeSum += duration; // see http://en.wikipedia.org/wiki/Algorithms_for_calculating_variance#Online_algorithm double delta = (double) duration - responseTimeMean; responseTimeMean += delta/(double) requests; responseTimeM2 += delta * ((double) duration - responseTimeMean); } public BoxAndWhiskers getBoxAndWhiskers() { if (requests < 2) { return new BoxAndWhiskers(responseTimeMean, responseTimeMean, responseTimeMean, responseTimeMean, responseTimeMean); } double stddev = Math.sqrt(responseTimeM2 / (double) (requests - 1)); return new BoxAndWhiskers(responseTimeMean + INVERSE_NORMAL_95 * stddev, responseTimeMean + INVERSE_NORMAL_50 * stddev, responseTimeMean, responseTimeMean - INVERSE_NORMAL_50 * stddev, responseTimeMean - INVERSE_NORMAL_95 * stddev); } public MeanAndDev getMeanAndDev() { if (requests < 2) return new MeanAndDev(responseTimeMean, 0); double stddev = Math.sqrt(responseTimeM2 / (double) (requests - 1)); return new MeanAndDev(responseTimeMean, stddev); } @Override public <T> T getRepresentation(Class<T> clazz, Statistics ownerStatistics, Object... args) { if (clazz == DefaultOutcome.class) { return (T) new DefaultOutcome(requests, errors, responseTimeMean, responseTimeMax); } else if (clazz == MeanAndDev.class) { return (T) getMeanAndDev(); } else if (clazz == OperationThroughput.class) { return (T) OperationThroughput.compute(requests, errors, ownerStatistics); } else if (clazz == BoxAndWhiskers.class) { return (T) getBoxAndWhiskers(); } else { return null; } } @Override public boolean isEmpty() { return requests == 0; } }