Added a way to compute a "histogram" (ordered by time slices).

This allows us to see snapshots of the online statistics algorihm.
This commit is contained in:
Kristian S. Stangeland 2014-03-29 23:29:25 +01:00
parent 300d3c2475
commit 839c186609
3 changed files with 195 additions and 4 deletions

View File

@ -0,0 +1,125 @@
package com.comphenix.protocol.timing;
import java.util.ArrayList;
import java.util.List;
import com.google.common.base.Preconditions;
import com.google.common.collect.ImmutableList;
/**
* Represents an online algortihm of computing histograms over time.
* @author Kristian
*/
public class HistogramStream extends OnlineComputation {
/**
* Each bin in the histogram, indexed by time.
*/
protected List<StatisticsStream> bins;
/**
* The current statistics stream we are updating.
*/
protected StatisticsStream current;
/**
* The maximum number of observations in each bin.
*/
protected int binWidth;
/**
* The number of total observations.
*/
protected int count;
/**
* Construct a new histogram stream which splits up every observation in different bins, ordered by time.
* @param binWidth - maximum number of observations in each bin.
*/
public HistogramStream(int binWidth) {
this(new ArrayList<StatisticsStream>(), new StatisticsStream(), binWidth);
}
/**
* Construct a new copy of the given histogram.
* @param other - the histogram to copy.
*/
public HistogramStream(HistogramStream other) {
// Deep cloning
for (StatisticsStream stream : other.bins) {
StatisticsStream copy = stream.copy();
// Update current
if (stream == other.current)
this.current = copy;
this.bins.add(copy);
}
this.binWidth = other.binWidth;
}
/**
* Construct a new histogram stream.
* @param bins - list of bins.
* @param current - the current selected bin. This will be added to the list if it is not already present.
* @param binWidth - the desired number of observations in each bin.
*/
protected HistogramStream(List<StatisticsStream> bins, StatisticsStream current, int binWidth) {
if (binWidth < 1)
throw new IllegalArgumentException("binWidth cannot be less than 1");
this.bins = Preconditions.checkNotNull(bins, "bins cannot be NULL");
this.current = Preconditions.checkNotNull(current, "current cannot be NULL");
this.binWidth = binWidth;
if (!this.bins.contains(current)) {
this.bins.add(current);
}
}
@Override
public HistogramStream copy() {
return new HistogramStream(this);
}
/**
* Retrieve an immutable view of every bin in the histogram.
* @return Every bin in the histogram.
*/
public ImmutableList<StatisticsStream> getBins() {
return ImmutableList.copyOf(bins);
}
@Override
public void observe(double value) {
checkOverflow();
count++;
current.observe(value);
}
/**
* See if the current bin has overflowed. If so, construct a new bin and set it as the current.
*/
protected void checkOverflow() {
if (current.getCount() >= binWidth) {
bins.add(current = new StatisticsStream());
}
}
/**
* Retrieve the total statistics of every bin in the histogram.
* <p>
* This method is not thread safe.
* @return The total statistics.
*/
public StatisticsStream getTotal() {
StatisticsStream sum = null;
for (StatisticsStream stream : bins) {
sum = sum != null ? stream.add(sum) : stream;
}
return sum;
}
@Override
public int getCount() {
return count;
}
}

View File

@ -0,0 +1,49 @@
package com.comphenix.protocol.timing;
/**
* Represents an online computation.
* @author Kristian
*/
public abstract class OnlineComputation {
/**
* Retrieve the number of observations.
* @return Number of observations.
*/
public abstract int getCount();
/**
* Observe a value.
* @param value - the observed value.
*/
public abstract void observe(double value);
/**
* Construct a copy of the current online computation.
* @return The new copy.
*/
public abstract OnlineComputation copy();
/**
* Retrieve a wrapper for another online computation that is synchronized.
* @param computation - the computation.
* @return The synchronized wrapper.
*/
public static OnlineComputation synchronizedComputation(final OnlineComputation computation) {
return new OnlineComputation() {
@Override
public synchronized void observe(double value) {
computation.observe(value);
}
@Override
public synchronized int getCount() {
return computation.getCount();
}
@Override
public synchronized OnlineComputation copy() {
return computation.copy();
}
};
}
}

View File

@ -4,7 +4,7 @@ package com.comphenix.protocol.timing;
* Represents an online algortihm for computing the mean and standard deviation without storing every value.
* @author Kristian
*/
public class StatisticsStream {
public class StatisticsStream extends OnlineComputation {
// This algorithm is due to Donald Knuth, as described in:
// Donald E. Knuth (1998). The Art of Computer Programming, volume 2:
// Seminumerical Algorithms, 3rd edn., p. 232. Boston: Addison-Wesley.
@ -35,11 +35,17 @@ public class StatisticsStream {
this.maximum = other.maximum;
}
@Override
public StatisticsStream copy() {
return new StatisticsStream(this);
}
/**
* Observe a value.
* @param value - the observed value.
*/
public void observe(double value) {
@Override
public void observe(double value) {
double delta = value - mean;
// As per Knuth
@ -125,7 +131,8 @@ public class StatisticsStream {
* Retrieve the number of observations.
* @return Number of observations.
*/
public int getCount() {
@Override
public int getCount() {
return count;
}
@ -134,4 +141,14 @@ public class StatisticsStream {
throw new IllegalStateException("No observations in stream.");
}
}
}
@Override
public String toString() {
if (count == 0)
return "StatisticsStream [Nothing recorded]";
return String.format("StatisticsStream [Average: %.3f, SD: %.3f, Min: %.3f, Max: %.3f, Count: %s]",
getMean(), getStandardDeviation(),
getMinimum(), getMaximum(), getCount());
}
}