package io.dropwizard.metrics; import java.util.ArrayList; import java.util.List; import java.util.concurrent.ThreadLocalRandom; import java.util.concurrent.atomic.AtomicLong; import java.util.concurrent.atomic.AtomicLongArray; /** * A random sampling reservoir of a stream of {@code long}s. Uses Vitter's Algorithm R to produce a * statistically representative sample. * * @see <a href="http://www.cs.umd.edu/~samir/498/vitter.pdf">Random Sampling with a Reservoir</a> */ public class UniformReservoir implements Reservoir { private static final int DEFAULT_SIZE = 1028; private static final int BITS_PER_LONG = 63; private final AtomicLong count = new AtomicLong(); private final AtomicLongArray values; /** * Creates a new {@link UniformReservoir} of 1028 elements, which offers a 99.9% confidence level * with a 5% margin of error assuming a normal distribution. */ public UniformReservoir() { this(DEFAULT_SIZE); } /** * Creates a new {@link UniformReservoir}. * * @param size the number of samples to keep in the sampling reservoir */ public UniformReservoir(int size) { this.values = new AtomicLongArray(size); for (int i = 0; i < values.length(); i++) { values.set(i, 0); } count.set(0); } @Override public int size() { final long c = count.get(); if (c > values.length()) { return values.length(); } return (int) c; } @Override public void update(long value) { final long c = count.incrementAndGet(); if (c <= values.length()) { values.set((int) c - 1, value); } else { final long r = nextLong(c); if (r < values.length()) { values.set((int) r, value); } } } /** * Get a pseudo-random long uniformly between 0 and n-1. Stolen from * {@link java.util.Random#nextInt()}. * * @param n the bound * @return a value select randomly from the range {@code [0..n)}. */ private static long nextLong(long n) { long bits, val; do { bits = ThreadLocalRandom.current().nextLong() & (~(1L << BITS_PER_LONG)); val = bits % n; } while (bits - val + (n - 1) < 0L); return val; } @Override public Snapshot getSnapshot() { final int s = size(); long[] copy = new long[s]; for (int i = 0; i < s; i++) { copy[i] = values.get(i); } return new UniformSnapshot(copy, false); } }