/*******************************************************************************
* Copyright (c) 2000, 2006 IBM Corporation and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* IBM Corporation - initial API and implementation
*******************************************************************************/
package org.eclipse.test.internal.performance.eval;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import org.eclipse.test.internal.performance.InternalPerformanceMeter;
import org.eclipse.test.internal.performance.data.DataPoint;
import org.eclipse.test.internal.performance.data.Dim;
import org.eclipse.test.internal.performance.data.Scalar;
import org.eclipse.test.internal.performance.eval.StatisticsUtil.Percentile;
import junit.framework.Assert;
/**
* @since 3.1
*/
public class StatisticsSession {
static final class Statistics {
public long count;
public long sum;
public double average;
public double stddev;
}
private final DataPoint[] fDataPoints;
private final Map fStatistics= new HashMap();
public StatisticsSession(DataPoint[] datapoints) {
fDataPoints= datapoints;
}
public double getAverage(Dim dimension) {
return getStats(dimension).average;
}
public long getSum(Dim dimension) {
return getStats(dimension).sum;
}
public long getCount(Dim dimension) {
return getStats(dimension).count;
}
public double getStddev(Dim dimension) {
return getStats(dimension).stddev;
}
double getStderr_mean(Dim dimension) {
return getStats(dimension).stddev / Math.sqrt(getStats(dimension).count);
}
double getStudentsT(Dim dimension, Percentile percentile) {
int df= (int) getStats(dimension).count - 1;
return StatisticsUtil.getStudentsT(df, percentile);
}
/**
* Returns the confidence interval for the given dimension and the percentile.
*
* @param dimension the dimension
* @param percentile the percentile
* @return an array of length two, with the lower and upper bounds of the confidence interval
*/
public double[] getConfidenceInterval(Dim dimension, Percentile percentile) {
double mean= getAverage(dimension);
double stat_err= getStderr_mean(dimension);
double t= getStudentsT(dimension, percentile);
double[] interval= {mean - t * stat_err, mean + t * stat_err};
return interval;
}
private Statistics getStats(Dim dimension) {
Statistics stats= (Statistics) fStatistics.get(dimension);
if (stats == null) {
stats= computeStats(dimension);
fStatistics.put(dimension, stats);
}
return stats;
}
private Statistics computeStats(Dim dimension) {
Statistics stats;
Set steps= new HashSet();
for (int j= 0; j < fDataPoints.length; j++) {
DataPoint dp= fDataPoints[j];
steps.add(new Integer(dp.getStep()));
}
if (steps.contains(new Integer(InternalPerformanceMeter.AVERAGE))) {
// an already aggregated set of data points from the DB
stats= computeStatsFromAggregates(dimension);
} else if (steps.contains(new Integer(InternalPerformanceMeter.AFTER))) {
// raw values from measurement
stats= computeStatsFromMeasurements(dimension, steps);
} else {
Assert.fail("illegal data set: contains neither AVERAGE nor AFTER values."); //$NON-NLS-1$
stats= null; // dummy
}
return stats;
}
private Statistics computeStatsFromAggregates(Dim dimension) {
Statistics stats= new Statistics();
long aggregateCount= 0;
double averageSum= 0;
long countSum= 0;
double stdevSum= 0;
// Set acquiredAggregates= new HashSet();
for (int i= 0; i < fDataPoints.length; i++) {
DataPoint point= fDataPoints[i];
Scalar scalar= point.getScalar(dimension);
if (scalar == null)
continue;
Integer aggregate= new Integer(point.getStep());
// allow for multiple measurements that were each stored with their own
// aggregate values
// Assert.assertTrue(acquiredAggregates.add(aggregate));
long magnitude= scalar.getMagnitude();
switch (aggregate.intValue()) {
case InternalPerformanceMeter.AVERAGE:
averageSum += magnitude;
aggregateCount++;
break;
case InternalPerformanceMeter.STDEV:
// see DB.internalStore
stdevSum += Double.longBitsToDouble(magnitude);
break;
case InternalPerformanceMeter.SIZE:
countSum += magnitude;
break;
default:
Assert.fail("only average, stdev and size are supported in aggregate mode"); //$NON-NLS-1$
break;
}
}
stats.average= averageSum / aggregateCount;
stats.stddev= stdevSum / aggregateCount; // XXX this does not work! have to treat multiple runs like normal measurement data
stats.count= countSum;
stats.sum= Math.round(stats.count * stats.average);
return stats;
}
private Statistics computeStatsFromMeasurements(Dim dimension, Set steps) {
Statistics stats= new Statistics();
long mags[];
switch (steps.size()) {
case 1:
// if there is only one Step, we don't calculate the delta. happens for startup tests
mags= new long[fDataPoints.length];
for (int i= 0; i < fDataPoints.length; i++) {
Scalar sc= fDataPoints[i].getScalar(dimension);
mags[i]= sc == null ? 0 : sc.getMagnitude();
}
break;
case 2:
int count= fDataPoints.length / 2;
mags= new long[count];
for (int i= 0; i < count; i ++) {
DataPoint before= fDataPoints[2 * i];
Assert.assertTrue("wrong order of steps", before.getStep() == InternalPerformanceMeter.BEFORE); //$NON-NLS-1$
DataPoint after= fDataPoints[2 * i + 1];
Assert.assertTrue("wrong order of steps", after.getStep() == InternalPerformanceMeter.AFTER); //$NON-NLS-1$
Scalar delta= getDelta(before, after, dimension);
long magnitude= delta.getMagnitude();
mags[i]= magnitude;
}
break;
default:
Assert.fail("cannot handle more than two steps in measurement mode"); //$NON-NLS-1$
return null; // dummy
}
for (int i= 0; i < mags.length; i++) {
stats.sum += mags[i];
stats.count++;
}
if (stats.count > 0) {
stats.average= (double) stats.sum / stats.count;
if (stats.count == 1) {
stats.stddev= 0;
} else {
double squaredDeviations= 0;
for (int i= 0; i < mags.length; i++) {
double deviation= stats.average - mags[i];
squaredDeviations += deviation * deviation;
}
stats.stddev= Math.sqrt(squaredDeviations / (stats.count - 1)); // unbiased sample stdev
}
} else {
stats.average= 0;
stats.stddev= 0;
}
return stats;
}
private Scalar getDelta(DataPoint before, DataPoint after, Dim dimension) {
Scalar one= before.getScalar(dimension);
Assert.assertTrue("reference has no value for dimension " + dimension, one != null); //$NON-NLS-1$
Scalar two= after.getScalar(dimension);
Assert.assertTrue("reference has no value for dimension " + dimension, two != null); //$NON-NLS-1$
return new Scalar(one.getDimension(), two.getMagnitude() - one.getMagnitude());
}
public boolean contains(Dim dimension) {
if (fDataPoints.length > 0)
return fDataPoints[0].contains(dimension);
return false;
}
}