blob: 02b2f2cf48b5e63d99c719f7cbd894cdcf240bf4 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2000, 2018 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;
import java.io.PrintStream;
import java.text.MessageFormat;
import java.text.NumberFormat;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Comparator;
import java.util.Iterator;
import java.util.List;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.test.internal.performance.data.DataPoint;
import org.eclipse.test.internal.performance.data.Dim;
import org.eclipse.test.internal.performance.data.Sample;
import org.eclipse.test.internal.performance.db.DB;
import org.eclipse.test.internal.performance.db.Variations;
import org.eclipse.test.internal.performance.eval.StatisticsSession;
import org.eclipse.test.internal.performance.eval.StatisticsUtil;
import org.eclipse.test.internal.performance.eval.StatisticsUtil.Percentile;
import org.eclipse.test.performance.Dimension;
import org.eclipse.test.performance.PerformanceMeter;
public abstract class InternalPerformanceMeter extends PerformanceMeter {
private static class DimensionComparator implements Comparator<Dim> {
@Override
public int compare(Dim o1, Dim o2) {
return o1.getId() - o2.getId();
}
}
public static final int AVERAGE = -3;
public static final int SIZE = -4;
public static final int STDEV = -5;
public static final int BEFORE = 0;
public static final int AFTER = 1;
protected static final String VERBOSE_PERFORMANCE_METER_PROPERTY = "InternalPrintPerformanceResults"; //$NON-NLS-1$
private String fScenarioId;
private String fShortName;
private Dimension[] fSummaryDimensions;
private boolean fSummaryIsGlobal;
private int fCommentType;
private String fComment;
public InternalPerformanceMeter(String scenarioId) {
fScenarioId = scenarioId;
}
@Override
public void dispose() {
fScenarioId = null;
}
public abstract Sample getSample();
/**
* Answer the scenario ID.
*
* @return the scenario ID
*/
public String getScenarioName() {
return fScenarioId;
}
@Override
public void commit() {
Sample sample = getSample();
if (sample != null) {
if (fSummaryDimensions != null) {
sample.tagAsSummary(fSummaryIsGlobal, fShortName, fSummaryDimensions, fCommentType, fComment);
} else if (this.fComment != null) {
sample.setComment(this.fCommentType, this.fComment);
}
Variations variations = PerformanceTestPlugin.getVariations();
if (variations != null)
DB.store(variations, sample);
if (!DB.isActive() || System.getProperty(VERBOSE_PERFORMANCE_METER_PROPERTY) != null) {
printSample(System.out, sample);
// printSampleCSV(System.out, sample);
}
}
}
private void printSample(PrintStream ps, Sample sample) {
ps.print("Scenario '" + getScenarioName() + "' "); //$NON-NLS-1$ //$NON-NLS-2$
DataPoint[] dataPoints = sample.getDataPoints();
if (dataPoints.length > 0) {
StatisticsSession s = new StatisticsSession(dataPoints);
Dim[] dimensions = dataPoints[0].getDimensions();
Arrays.sort(dimensions, new DimensionComparator());
if (dimensions.length > 0) {
List<Dim> badDimensions = new ArrayList<>();
long n = s.getCount(dimensions[0]);
MessageFormat format = new MessageFormat("({0,number,percent} in [{1}, {2}])"); //$NON-NLS-1$
String spaces = " "; //$NON-NLS-1$
ps.println("(average over " + n + " samples):"); //$NON-NLS-1$ //$NON-NLS-2$
for (Dim dimension : dimensions) {
double mean = s.getAverage(dimension);
String nameString = " " + dimension.getName() + ":"; //$NON-NLS-1$ //$NON-NLS-2$
String meanString = dimension.getDisplayValue(mean);
int align = firstNonDigit(meanString);
int endIndex = 30 - align - nameString.length();
if (endIndex > 0)
meanString = spaces.substring(0, endIndex) + meanString;
align = nameString.length() + meanString.length();
Percentile percentile = StatisticsUtil.T95;
double[] confidenceInterval = s.getConfidenceInterval(dimension, percentile);
StringBuffer printBuffer;
if (n <= 2) {
printBuffer = new StringBuffer(" (no confidence)"); //$NON-NLS-1$
} else {
printBuffer = new StringBuffer();
int ns = align;
while (ns++ < 40)
printBuffer.append(' ');
printBuffer
.append(format.format(new Object[] { Double.valueOf(percentile.inside()),
dimension.getDisplayValue(confidenceInterval[0]),
dimension.getDisplayValue(confidenceInterval[1]) }));
}
align += printBuffer.length();
try {
while (align++ < 70)
printBuffer.append(' ');
printBuffer.append(checkSampleSize(s, sample, dimension));
}
catch (CoreException x) {
badDimensions.add(dimension);
continue;
}
ps.print(nameString);
ps.print(meanString);
ps.println(printBuffer);
}
if (!badDimensions.isEmpty()) {
ps.print(" Dimensions with unusable statistical properties: "); //$NON-NLS-1$
for (Iterator<Dim> iter = badDimensions.iterator(); iter.hasNext();) {
Dim dimension = iter.next();
ps.print(dimension.getName());
if (iter.hasNext())
ps.print(", "); //$NON-NLS-1$
}
ps.println();
}
}
}
ps.println();
}
private String checkSampleSize(StatisticsSession s, Sample sample, Dim dimension) throws CoreException {
long sampleSize = s.getCount(dimension);
double stdev = s.getStddev(dimension);
double mean = s.getAverage(dimension);
if (stdev == 0)
return ""; //$NON-NLS-1$
// measurable effect size
// sampleSize= 16 * stdev^2 / effect^2
double effectSize = 4 * Math.sqrt(stdev * stdev / sampleSize);
double base;
String baseName;
if (stdev > Math.abs(mean)) {
base = stdev;
baseName = "stdev"; //$NON-NLS-1$
} else {
base = Math.abs(mean);
baseName = "mean"; //$NON-NLS-1$
}
double fivePercentEffect = 0.05 * base;
long requiredSampleSizeForFivePercentEffect = Math.round(16 * stdev * stdev / fivePercentEffect / fivePercentEffect + 0.5);
// if (requiredSampleSizeForFivePercentEffect > 1000 || Double.isNaN(stdev))
// throw new CoreException(new Status(IStatus.OK, "org.eclipse.text.performance", IStatus.OK, "no message", null)); //$NON-NLS-1$ //$NON-NLS-2$
NumberFormat numberInstance = NumberFormat.getNumberInstance();
numberInstance.setMaximumFractionDigits(1);
numberInstance.setMinimumFractionDigits(1);
String measurableMsg = " Measurable effect: " + dimension.getDisplayValue(effectSize) + " (" + numberInstance.format(effectSize / stdev) + " SDs)"; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
if (fivePercentEffect < effectSize)
measurableMsg += " (required sample size for an effect of 5% of " + baseName + ": " + requiredSampleSizeForFivePercentEffect + ")"; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
return measurableMsg;
}
private int firstNonDigit(String string) {
int length = string.length();
for (int i = 0; i < length; i++)
if (!Character.isDigit(string.charAt(i)) && string.charAt(i) != '-' && string.charAt(i) != '.')
return i;
return length;
}
void printSampleCSV(PrintStream ps, Sample sample) {
final char SEPARATOR = '\t';
DataPoint[] dataPoints = sample.getDataPoints();
if (dataPoints.length > 0) {
Dim[] dimensions = dataPoints[0].getDimensions();
Arrays.sort(dimensions, new DimensionComparator());
if (dimensions.length > 0) {
/* print dimensions */
for (Dim dimension : dimensions) {
ps.print(dimension.getName());
ps.print(SEPARATOR);
}
ps.println("scenario"); //$NON-NLS-1$
for (int p = 0; p < dataPoints.length - 1; p += 2) {
DataPoint before = dataPoints[p];
DataPoint after = dataPoints[p + 1];
for (Dim dimension : dimensions) {
long valBefore = before.getScalar(dimension).getMagnitude();
long valAfter = after.getScalar(dimension).getMagnitude();
ps.print(valAfter - valBefore);
ps.print(SEPARATOR);
}
ps.print(sample.getShortname() != null ? sample.getShortname() : sample.getScenarioID());
ps.println();
}
ps.println();
}
}
}
public void tagAsSummary(boolean global, String shortName, Dimension[] dims) {
fSummaryIsGlobal = global;
fShortName = shortName;
fSummaryDimensions = dims;
}
public void setComment(int commentType, String comment) {
fCommentType = commentType;
fComment = comment;
}
}