blob: bb212c3a05a0e501c10d9314c5576107fd5ac8b4 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2000, 2006 IBM Corporation and others.
*
* This program and the accompanying materials
* are made available under the terms of the Eclipse Public License 2.0
* which accompanies this distribution, and is available at
* https://www.eclipse.org/legal/epl-2.0/
*
* SPDX-License-Identifier: EPL-2.0
*
* 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 org.junit.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<Dim, Statistics> 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 = fStatistics.get(dimension);
if (stats == null) {
stats = computeStats(dimension);
fStatistics.put(dimension, stats);
}
return stats;
}
private Statistics computeStats(Dim dimension) {
Statistics stats;
Set<Integer> steps = new HashSet<>();
for (int j = 0; j < fDataPoints.length; j++) {
DataPoint dp = fDataPoints[j];
steps.add(Integer.valueOf(dp.getStep()));
}
if (steps.contains(Integer.valueOf(InternalPerformanceMeter.AVERAGE))) {
// an already aggregated set of data points from the DB
stats = computeStatsFromAggregates(dimension);
} else if (steps.contains(Integer.valueOf(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 = Integer.valueOf(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<Integer> 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;
}
}