Skip to content

Commit

Permalink
rolling queue as first step toward incremental stats lib for streaming.
Browse files Browse the repository at this point in the history
  • Loading branch information
bradford committed Aug 4, 2010
1 parent e8355c1 commit 4274dab
Show file tree
Hide file tree
Showing 3 changed files with 110 additions and 1 deletion.
3 changes: 2 additions & 1 deletion project.clj
Expand Up @@ -5,5 +5,6 @@
[clojure-csv/clojure-csv "1.1.0"]
[org.apache.commons/commons-math "2.0"]
[ujmp-complete "0.2.4"]]
:dev-dependencies [[swank-clojure "1.2.0"]
:dev-dependencies [[org.clojars.mmcgrana/lein-javac "0.1.0"]
[swank-clojure "1.2.0"]
[lein-clojars "0.5.0"]])
71 changes: 71 additions & 0 deletions src/jvm/infer/RollingQueue.java
@@ -0,0 +1,71 @@
package infer;

/// <summary>
/// The IndexableFixedLengthCompoundQueueOfDoubles is an optimized, indexable, fixed-length queue designed to solve
/// two problems for event driven calculations on rolling windows. First, we discovered
/// performancce issues with a normal queue. A normal queue is dynamicly resizing ad new
/// enemends are enqued and dequed. A queue that serves as the data event caching mecahnism
/// to back rolling calculations is used in a specific pattern - data events cause both an
/// enqueue and a dequeue at the same time. This pattern of useage can cause performace
/// issues for normal queues if it leads to dynamci sizing and resizing. Since we know the
/// queue is fized length and has a certian useage pattern, we can impliment a different api;
/// we can remove the Deque method and foled its behavior into the Enque method. Moreover,
/// this can be done very efficiently on a fixed length array with a very fast indexing scheme
/// that continues wrapping indexes around the array and overwriting elements that are dequed.
/// </summary>
public class RollingQueue
{
private final double[] backingArray;
private int tail;
private final int length;
public boolean isPrimed;

public RollingQueue(int i)
{
length = i;
backingArray = new double[i];
for (int j = 0; j < i; j++)
{
backingArray[j] = Double.NaN;
}
}

public int getLength()
{
return isPrimed ? length : tail;
}

public double enqueue(double element)
{
double tailValue = backingArray[tail];
backingArray[tail] = element;
tail = tail + 1;
if (tail > length - 1) tail = 0;
if (!isPrimed) isPrimed = tail == 0;
return tailValue;
}

public double getAtLookback(int n)
{
int index = wrapFromTheBack(tail - n - 1);
return backingArray[index];
}

public double getAtIndex(int i)
{
return isPrimed ? getAtLookback(length - 1 - i) : backingArray[0];
}

private int wrapFromTheBack(int index)
{
return (index < 0) ? length + index : index;
}

public double[] cloneElements()
{
if (isPrimed) return (double[]) backingArray.clone();
double[] elements = new double[tail];
System.arraycopy(backingArray, 0, elements, 0, tail);
return (double[]) elements.clone();
}
}
37 changes: 37 additions & 0 deletions test/infer/inc_stats_test.clj
@@ -0,0 +1,37 @@
(ns infer.inc-stats-test
(:use clojure.test)
(:import infer.RollingQueue))

(deftest enqueueing
(let [q (RollingQueue. 3)]
(is (Double/isNaN (.enqueue q 15)))
(is (Double/isNaN (.enqueue q 3)))
(is (Double/isNaN (.enqueue q 17)))
(is (= 15 (.enqueue q 2)))
(is (= 3 (.enqueue q 3)))
(is (= 17 (.enqueue q 4)))))

(deftest enqueueing-state
(let [q (RollingQueue. 3)
_ (.enqueue q 15)
_ (.enqueue q 3)]

(is (= 15 (.getAtLookback q 1)))
(is (= 15 (.getAtIndex q 0)))
(is (= 2 (.getLength q)))

(let [_ (.enqueue q 17)]

(is (= 3 (.getAtLookback q 1)))
(is (= 17 (.getAtLookback q 0)))
(is (= 15 (.getAtLookback q 2)))
(is (= 3 (.getAtIndex q 1)))
(is (= 3 (.getLength q)))

(let [_ (.enqueue q 1)]

(is (= 1 (.getAtLookback q 0)))
(is (= 17 (.getAtLookback q 1)))
(is (= 3 (.getAtLookback q 2)))
(is (= 1 (.getAtIndex q 2)))
(is (= 3 (.getLength q)))))))

0 comments on commit 4274dab

Please sign in to comment.