All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.lazerycode.jmeter.analyzer.statistics.Samples Maven / Gradle / Ivy

Go to download

Parses JMeter result files and computes performance indicators such as average request duration

There is a newer version: 1.0.6
Show newest version
package com.lazerycode.jmeter.analyzer.statistics;

import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;

/**
 * Collects samples (as a sequence of values of type "long") and provides the following values:
 *
 * 
    *
  • min: Minimum value
  • *
  • max: Maximum value
  • *
  • average: Average value
  • *
  • standard deviation
  • *
  • samples per second
  • *
  • quantiles
  • *
* * @author Dennis Homann, Arne Franken, Peter Kaul */ public class Samples { private static final float SECOND = 1000f; // number or error samples private long errors = 0; // number of success samples private long success = 0; // collected samples private List samples = new ArrayList(); // timestamps corresponding to samples private List timestamps = new ArrayList(); // minimum sample timestamp private long minTimestamp = Long.MAX_VALUE; // maximum sample timestamp private long maxTimestamp = Long.MIN_VALUE; // minimum sample value private long min = Long.MAX_VALUE; // maximum sample value private long max = Long.MIN_VALUE; private volatile boolean finished = false; // current number of samples which are aggregated into a single sample private int compression = 1; // maximum number of samples to store private final int maxSamplesCount; private List samplesBuffer = new ArrayList(); private List timestampsBuffer = new ArrayList(); private double total = 0; // sum of all values each powered by 2 private double totalPowered2 = 0; private long standardDeviation; // The value histogram private Map histogram; // ---------------------- /** * Creates a new instance where a limited number of samples is stored internally. * If more samples are added, existing samples will be compressed internally. * * @param maxSamples The maximum number of samples. 0=store no samples * @param histogram If set to true the a value histogram should be counted as well. */ public Samples(int maxSamples, boolean histogram) { this.maxSamplesCount = maxSamples; if( histogram ) { this.histogram = new HashMap(); } } /** * Adds an "error" sample. An error sample isn't used for statistics values such as average, ... * * @param timestamp The timestamp of the sample. It's assumed that a timestamp is greater or equal than the previous one */ public void addError(long timestamp) { assertNotFinished(); errors++; setTimestamp(timestamp); } /** * Adds a "success" sample * * @param timestamp The timestamp of the sample. It's assumed that a timestamp is greater or equal than the previous one * @param value The sample value, e.g. the response duration or response bytes * * @see #addError(long) */ public void addSample(long timestamp, long value) { assertNotFinished(); // handle counters/statistics success++; total += value; totalPowered2 += Math.pow(value, 2); //set min / max value if( value > max ) { max = value; } if( value < min ) { min = value; } //set min / max timestamp setTimestamp(timestamp); //collect the value if( histogram != null ) { ValueCount count = histogram.get(value); if( count == null ) { //there was no other request with the same response value, collect count = new ValueCount(value); histogram.put(value, count); } //there already was another request with the same response value, increment count.increment(); } // store sample add(timestamp, value); } /** * Marks collecting samples as "finished" */ public void finish() { finished = true; // flush buffer by adding remaining items if( samplesBuffer.size() > 0 ) { addAggregated(samplesBuffer, timestampsBuffer); samplesBuffer.clear(); timestampsBuffer.clear(); } // adjust some statistics // Standard Deviation: http://en.wikipedia.org/wiki/Standard_deviation#Rapid_calculation_methods double totalPowered0 = success; double totalPowered1 = total; standardDeviation = (long) (Math.sqrt(totalPowered0 * totalPowered2 - Math.pow(totalPowered1, 2)) / totalPowered0); // protect collected data against modification samples = Collections.unmodifiableList(samples); timestamps = Collections.unmodifiableList(timestamps); } /** * @return Determines whether a relevant number of samples have been provided so that statistics can be computed */ public boolean hasSamples() { return getSuccessCount() > 0; } public List getSamples() { assertFinished(); return samples; } /** * @return The sample's timestamp. */ public List getTimestamps() { assertFinished(); return timestamps; } /** * @return Total number of stored samples * @see #getSuccessCount() */ public long getStoredSamplesCount() { assertFinished(); return samples.size(); } /** * @return Total number of successful samples */ public long getSuccessCount() { assertFinished(); return success; } /** * @return Total number of samples having an error */ public long getErrorsCount() { assertFinished(); return errors; } public long getMin() { assertFinished(); if( !hasSamples() ) { throw new IllegalStateException("No samples"); } return min; } public long getMax() { assertFinished(); if( !hasSamples() ) { throw new IllegalStateException("No samples"); } return max; } /** * @return The average for all samples */ public long getAverage() { assertFinished(); long count = getSuccessCount(); if( count == 0 ) { throw new IllegalStateException("No samples"); } return (long) total / count; } public long getTotal() { assertFinished(); return (long)total; } public long getStandardDeviation() { return standardDeviation; } public long getMaxTimestamp() { assertFinished(); return maxTimestamp; } public long getMinTimestamp() { assertFinished(); return minTimestamp; } /** * @return Number of successful samples per second */ public long getSuccessPerSecond() { assertFinished(); long duration = getDuration(); if( duration == 0 ) { return 0; // shouldn't happen } return getSuccessCount() / duration; } /** * @return The duration in s */ public long getDuration() { assertFinished(); return Math.round((getMaxTimestamp()-getMinTimestamp()) / SECOND); } /** * Returns a Quantile with the grade/resolution q using counts as values * * @param q the grade * * @return the q-quantile. */ public Quantile getQuantiles(int q) { assertFinished(); if( histogram == null ) { throw new IllegalStateException("No histogram available"); } return new Quantile(q, histogram.values()); } //==================================================================================================================== private void assertNotFinished() { if( finished ) { throw new IllegalStateException("Already finished"); } } private void assertFinished() { if( !finished ) { throw new IllegalStateException("Not finished"); } } /** * set min / max timestamp * @param timestamp the timestamp */ private void setTimestamp(long timestamp) { if( timestamp < minTimestamp ) { minTimestamp = timestamp; } if( timestamp > maxTimestamp ) { maxTimestamp = timestamp; } } /** * Collect timestamp and value */ private void add(long timestamp, long value) { if( maxSamplesCount == 0 ) { return; } // check whether the maximum of samples is reached and reduce number of samples if necessary if( samples.size() >= maxSamplesCount ) { // compress halve(); compression *= 2; } // add current sample if( compression == 1 ) { // store samples samples.add(value); timestamps.add(timestamp); } else { // buffer samples for aggregation samplesBuffer.add(value); timestampsBuffer.add(timestamp); if( samplesBuffer.size() >= compression ) { // we have collected enough items addAggregated(samplesBuffer, timestampsBuffer); samplesBuffer.clear(); timestampsBuffer.clear(); } } } /** * Aggregates samples and timestamps and add them as a single item to samples/timestamp * * @param samplesBuffer samples to be aggregated. will be cleared * @param timestampsBuffer timestamps to be aggregated */ private void addAggregated(List samplesBuffer, List timestampsBuffer) { long firstTimestamp = timestampsBuffer.get(0); long lastTimestamp = timestampsBuffer.get(timestampsBuffer.size()-1); long aggregatedTimestamp = firstTimestamp + (lastTimestamp-firstTimestamp) / 2; long aggregatedSample = 0; for( long sample : samplesBuffer ) { aggregatedSample += sample; } aggregatedSample = aggregatedSample / samplesBuffer.size(); samples.add(aggregatedSample); timestamps.add(aggregatedTimestamp); } /** * Cuts a list of samples in half by aggregating pairs a samples */ private void halve() { List newSamples = new ArrayList(); List newTimestamps = new ArrayList(); Iterator si = samples.iterator(); Iterator ti = timestamps.iterator(); while( si.hasNext() ) { long sample = si.next(); long timestamp = ti.next(); if( !si.hasNext() ) { // there is no second sample. thus, don't aggregate this last element newSamples.add(sample); newTimestamps.add(timestamp); } else { long secondTimestamp = ti.next(); long secondSample = si.next(); long aggregatedSample = (sample+secondSample) / 2; long aggregatedTimestamp = (timestamp+(secondTimestamp-timestamp) / 2); newSamples.add(aggregatedSample); newTimestamps.add(aggregatedTimestamp); } } samples = newSamples; timestamps = newTimestamps; } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy