Permalink
Browse files

Initial pass at a bucketed histogram for percentile statistics

  • Loading branch information...
1 parent ba1eaa9 commit fc9a9b972efad338c1c59a5d6839aa653cd286c2 @afeinberg afeinberg committed Feb 15, 2012
Showing with 145 additions and 0 deletions.
  1. +103 −0 src/java/voldemort/store/stats/Histogram.java
  2. +42 −0 test/unit/voldemort/store/stats/HistogramTest.java
@@ -0,0 +1,103 @@
+package voldemort.store.stats;
+
+import voldemort.annotations.concurrency.NotThreadsafe;
+
+import java.util.Arrays;
+
+/**
+ * A class for computing percentiles based on a histogram
+ */
+@NotThreadsafe
+public class Histogram {
+
+ private final int nBuckets;
+ private final int step;
+ private final int[] buckets;
+ private final int[] bounds;
+ private int size;
+
+ /**
+ *
+ * @param nBuckets
+ * @param step
+ */
+ public Histogram(int nBuckets, int step) {
+ this.nBuckets = nBuckets;
+ this.step = step;
+ this.buckets = new int[nBuckets];
+ this.bounds = new int[nBuckets];
+ init();
+ }
+
+ protected void init() {
+ int bound = 0;
+ for(int i = 0; i < nBuckets; i++, bound += step) {
+ bounds[i] = bound;
+ }
+ reset();
+ }
+
+ /**
+ *
+ */
+ public void reset() {
+ Arrays.fill(buckets, 0);
+ size = 0;
+ }
+
+ /**
+ *
+ * @param data
+ */
+ public void insert(int data) {
+ int index = findBucket(data);
+ assert(index != -1);
+ buckets[index]++;
+ size++;
+ }
+
+ public int getQuantile(double quantile) {
+ int total = 0;
+ for(int i = 0; i < nBuckets; i++) {
+ total += buckets[i];
+ double currQuantile = ((double) total) / ((double) size);
+ if(currQuantile >= quantile) {
+ return bounds[i];
+ }
+ }
+ return 0;
+ }
+
+ private int findBucket(int needle) {
+ int max = step * nBuckets;
+ if(needle > max) {
+ return nBuckets - 1;
+ }
+ int low = 0;
+ int high = nBuckets - 1;
+ while(low <= high) {
+ int mid = (low + high) / 2;
+ int cmp = compareToBucket(mid, needle);
+ if(cmp == 0) {
+ return mid;
+ } else if(cmp > 0) {
+ high = mid - 1;
+ } else if(cmp < 0) {
+ low = mid + 1;
+ }
+ }
+ return -1;
+ }
+
+ private int compareToBucket(int bucket, int needle) {
+ int low = bounds[bucket];
+ int high = low + step;
+ if (low <= needle && high > needle) {
+ return 0;
+ } else if(low > needle) {
+ return 1;
+ } else {
+ return -1;
+ }
+ }
+}
@@ -0,0 +1,42 @@
+package voldemort.store.stats;
+
+import org.junit.Before;
+import org.junit.Test;
+
+import static org.junit.Assert.assertEquals;
+
+public class HistogramTest {
+
+ private Histogram histogram;
+
+ @Before
+ public void setUp() {
+ histogram = new Histogram(10, 5);
+ histogram.insert(1);
+ histogram.insert(6);
+ histogram.insert(11);
+ histogram.insert(16);
+ histogram.insert(21);
+ histogram.insert(26);
+ histogram.insert(31);
+ histogram.insert(36);
+ histogram.insert(41);
+ histogram.insert(46);
+ }
+
+ @Test
+ public void testAverage() {
+ assertEquals(histogram.getQuantile(0.50), 20);
+ }
+
+ @Test
+ public void test95thQuartile() {
+ assertEquals(histogram.getQuantile(0.95), 45);
+ }
+
+ @Test
+ public void test99thQuartile() {
+ assertEquals(histogram.getQuantile(0.95), 45);
+ }
+
+}

0 comments on commit fc9a9b9

Please sign in to comment.