Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

Removed threads and added object pools in bbTree. 5% gain

  • Loading branch information...
commit 9eb49d38643a4ba220565b153e49f710103ad1b4 1 parent fe01221
@josephg authored
Showing with 177 additions and 181 deletions.
  1. +2 −0  benchmark/bench.js
  2. +86 −89 cp.js
  3. +3 −3 cp.min.js
  4. +86 −89 lib/cpBBTree.js
View
2  benchmark/bench.js
@@ -513,6 +513,7 @@ print('contacts: ' + numContacts);
print('node: ' + numNodes);
print('leaf: ' + numLeaves);
print('bb: ' + numBB);
+print('pairs: ' + numPairs);
print('applyImpulse: ' + numApplyImpulse);
print('applyContact: ' + numApplyContact);
@@ -521,6 +522,7 @@ print(numContacts);
print(numNodes);
print(numLeaves);
print(numBB);
+print(numPairs);
print(numApplyImpulse);
print(numApplyContact);
View
175 cp.js
@@ -1789,9 +1789,9 @@ var BBTree = cp.BBTree = function(staticIndex)
this.root = null;
- // An object pool of tree nodes and pairs.
- //this.pooledNodes = [];
- //this.pooledPairs = [];
+ // A linked list containing an object pool of tree nodes and pairs.
+ this.pooledNodes = null;
+ this.pooledPairs = null;
this.stamp = 0;
};
@@ -1802,9 +1802,7 @@ var numNodes = 0;
var Node = function(tree, a, b)
{
- //Node *node = NodeFromPool(tree);
this.obj = null;
- //this.bb = bbMerge(a.bb, b.bb);
this.bb_l = min(a.bb_l, b.bb_l);
this.bb_b = min(a.bb_b, b.bb_b);
this.bb_r = max(a.bb_r, b.bb_r);
@@ -1813,16 +1811,25 @@ var Node = function(tree, a, b)
this.setA(a);
this.setB(b);
- numNodes++;
+};
+
+BBTree.prototype.makeNode = function(a, b)
+{
+ var node = this.pooledNodes;
+ if(node){
+ this.pooledNodes = node.parent;
+ node.constructor(this, a, b);
+ return node;
+ } else {
+ numNodes++;
+ return new Node(this, a, b);
+ }
};
var numLeaves = 0;
var Leaf = function(tree, obj)
{
- //Node *node = NodeFromPool(tree);
-
this.obj = obj;
- //this.bb = tree.getBB(obj);
tree.getBB(obj, this);
this.parent = null;
@@ -1873,45 +1880,59 @@ BBTree.prototype.incrementStamp = function()
// **** Pair/Thread Functions
+var numPairs = 0;
// Objects created with constructors are faster than object literals. :(
-var Pair = function(a, b)
+var Pair = function(leafA, nextA, leafB, nextB)
{
- this.a = a; this.b = b;
-};
+ this.prevA = null;
+ this.leafA = leafA;
+ this.nextA = nextA;
-var Thread = function(leaf, next)
-{
- this.prev = null;
- this.next = next;
- this.leaf = leaf;
+ this.prevB = null;
+ this.leafB = leafB;
+ this.nextB = nextB;
};
-// Benchmark this code with object pools on & off.
-/*
-BBTree.prototype.pairRecycle = function(pair)
+BBTree.prototype.makePair = function(leafA, nextA, leafB, nextB)
{
- this.pooledPairs.push(pair);
+ //return new Pair(leafA, nextA, leafB, nextB);
+ var pair = this.pooledPairs;
+ if (pair)
+ {
+ this.pooledPairs = pair.prevA;
+
+ pair.prevA = null;
+ pair.leafA = leafA;
+ pair.nextA = nextA;
+
+ pair.prevB = null;
+ pair.leafB = leafB;
+ pair.nextB = nextB;
+
+ //pair.constructor(leafA, nextA, leafB, nextB);
+ return pair;
+ } else {
+ numPairs++;
+ return new Pair(leafA, nextA, leafB, nextB);
+ }
};
-BBTree.prototype.pairFromPool = function()
+Pair.prototype.recycle = function(tree)
{
- return this.pooledPairs.pop() || new Pair(null, null);
+ this.prevA = tree.pooledPairs;
+ tree.pooledPairs = this;
};
-*/
-Thread.prototype.unlink = function()
+var unlinkThread = function(prev, leaf, next)
{
- var next = this.next;
- var prev = this.prev;
-
if(next){
- if(next.a.leaf == this.leaf) next.a.prev = prev; else next.b.prev = prev;
+ if(next.leafA === leaf) next.prevA = prev; else next.prevB = prev;
}
if(prev){
- if(prev.a.leaf == this.leaf) prev.a.next = next; else prev.b.next = next;
+ if(prev.leafA === leaf) prev.nextA = next; else prev.nextB = next;
} else {
- this.leaf.pairs = next;
+ leaf.pairs = next;
}
};
@@ -1923,61 +1944,45 @@ Leaf.prototype.clearPairs = function(tree)
this.pairs = null;
while(pair){
- if(pair.a.leaf == this){
- next = pair.a.next;
- pair.b.unlink();
- //tree.pairRecycle(pair);
- pair = next;
+ if(pair.leafA === this){
+ next = pair.nextA;
+ unlinkThread(pair.prevB, pair.leafB, pair.nextB);
} else {
- next = pair.b.next;
- pair.a.unlink();
- //tree.pairRecycle(pair);
- pair = next;
+ next = pair.nextB;
+ unlinkThread(pair.prevA, pair.leafA, pair.nextA);
}
+ pair.recycle(tree);
+ pair = next;
}
}
var pairInsert = function(a, b, tree)
{
var nextA = a.pairs, nextB = b.pairs;
- var pair = new Pair(new Thread(a, nextA), new Thread(b, nextB));
+ var pair = tree.makePair(a, nextA, b, nextB);
a.pairs = b.pairs = pair;
- //var pair = tree.pairFromPool();
- //Pair temp = {{null, a, nextA},{null, b, nextB}};
- //*pair = temp;
-
if(nextA){
- if(nextA.a.leaf == a) nextA.a.prev = pair; else nextA.b.prev = pair;
+ if(nextA.leafA === a) nextA.prevA = pair; else nextA.prevB = pair;
}
if(nextB){
- if(nextB.a.leaf == b) nextB.a.prev = pair; else nextB.b.prev = pair;
+ if(nextB.leafA === b) nextB.prevA = pair; else nextB.prevB = pair;
}
};
// **** Node Functions
-/*
-static void
-NodeRecycle(bbTree *tree, Node *node)
+Node.prototype.recycle = function(tree)
{
- node.parent = tree.pooledNodes;
- tree.pooledNodes = node;
-}
+ this.parent = tree.pooledNodes;
+ tree.pooledNodes = this;
+};
-static Node *
-NodeFromPool(bbTree *tree)
+Leaf.prototype.recycle = function(tree)
{
- Node *node = tree.pooledNodes;
-
- if(node){
- tree.pooledNodes = node.parent;
- return node;
- } else {
- // Pool is exhausted, make more
- }
-}*/
+ // Its not worth the overhead to recycle leaves.
+};
Node.prototype.setA = function(value)
{
@@ -1991,12 +1996,6 @@ Node.prototype.setB = function(value)
value.parent = this;
};
-/*
-static inline cpBool
-NodeIsLeaf(Node *node)
-{
- return (node.obj != null);
-}*/
Leaf.prototype.isLeaf = true;
Node.prototype.isLeaf = false;
@@ -2010,10 +2009,10 @@ Node.prototype.replaceChild = function(child, value, tree)
assertSoft(child == this.A || child == this.B, "Node is not a child of parent.");
if(this.A == child){
- //NodeRecycle(tree, parent.A);
+ this.A.recycle(tree);
this.setA(value);
} else {
- //NodeRecycle(tree, parent.B);
+ this.B.recycle(tree);
this.setB(value);
}
@@ -2055,7 +2054,7 @@ var subtreeInsert = function(subtree, leaf, tree)
if(subtree == null){
return leaf;
} else if(subtree.isLeaf){
- return new Node(tree, leaf, subtree);
+ return tree.makeNode(leaf, subtree);
} else {
var cost_a = subtree.B.bbArea() + bbTreeMergedArea(subtree.A, leaf);
var cost_b = subtree.A.bbArea() + bbTreeMergedArea(subtree.B, leaf);
@@ -2144,16 +2143,14 @@ var subtreeSegmentQuery = function(subtree, a, b, t_exit, func)
}
};
-/*
-static void
-SubtreeRecycle(bbTree *tree, Node *node)
+BBTree.prototype.subtreeRecycle = function(node)
{
- if(!NodeIsLeaf(node)){
- SubtreeRecycle(tree, node.A);
- SubtreeRecycle(tree, node.B);
- NodeRecycle(tree, node);
+ if(node.isLeaf){
+ this.subtreeRecycle(node.A);
+ this.subtreeRecycle(node.B);
+ node.recycle(this);
}
-}*/
+};
var subtreeRemove = function(subtree, leaf, tree)
{
@@ -2164,7 +2161,7 @@ var subtreeRemove = function(subtree, leaf, tree)
if(parent == subtree){
var other = subtree.otherChild(leaf);
other.parent = subtree.parent;
- //NodeRecycle(tree, subtree);
+ subtree.recycle(tree);
return other;
} else {
parent.parent.replaceChild(parent, parent.otherChild(leaf), tree);
@@ -2220,11 +2217,11 @@ var markLeaf = function(leaf, tree, staticRoot, func)
} else {
var pair = leaf.pairs;
while(pair){
- if(leaf == pair.b.leaf){
- if(func) func(pair.a.leaf.obj, leaf.obj);
- pair = pair.b.next;
+ if(leaf === pair.leafB){
+ if(func) func(pair.leafA.obj, leaf.obj);
+ pair = pair.nextB;
} else {
- pair = pair.a.next;
+ pair = pair.nextA;
}
}
}
@@ -2306,7 +2303,7 @@ BBTree.prototype.remove = function(obj, hashid)
this.count--;
leaf.clearPairs(this);
- //NodeRecycle(tree, leaf);
+ leaf.recycle(this);
};
BBTree.prototype.contains = function(obj, hashid)
@@ -2398,7 +2395,7 @@ var partitionNodes = function(tree, nodes, offset, count)
if(count == 1){
return nodes[offset];
} else if(count == 2) {
- return new Node(tree, nodes[offset], nodes[offset + 1]);
+ return tree.makeNode(nodes[offset], nodes[offset + 1]);
}
// Find the AABB for these nodes
@@ -2505,7 +2502,7 @@ BBTree.prototype.optimize = function()
nodes[i++] = this.nodes[hashid];
}
- //SubtreeRecycle(tree, root);
+ tree.subtreeRecycle(root);
this.root = partitionNodes(tree, nodes, nodes.length);
};
View
6 cp.min.js
3 additions, 3 deletions not shown
View
175 lib/cpBBTree.js
@@ -34,9 +34,9 @@ var BBTree = cp.BBTree = function(staticIndex)
this.root = null;
- // An object pool of tree nodes and pairs.
- //this.pooledNodes = [];
- //this.pooledPairs = [];
+ // A linked list containing an object pool of tree nodes and pairs.
+ this.pooledNodes = null;
+ this.pooledPairs = null;
this.stamp = 0;
};
@@ -47,9 +47,7 @@ var numNodes = 0;
var Node = function(tree, a, b)
{
- //Node *node = NodeFromPool(tree);
this.obj = null;
- //this.bb = bbMerge(a.bb, b.bb);
this.bb_l = min(a.bb_l, b.bb_l);
this.bb_b = min(a.bb_b, b.bb_b);
this.bb_r = max(a.bb_r, b.bb_r);
@@ -58,16 +56,25 @@ var Node = function(tree, a, b)
this.setA(a);
this.setB(b);
- numNodes++;
+};
+
+BBTree.prototype.makeNode = function(a, b)
+{
+ var node = this.pooledNodes;
+ if(node){
+ this.pooledNodes = node.parent;
+ node.constructor(this, a, b);
+ return node;
+ } else {
+ numNodes++;
+ return new Node(this, a, b);
+ }
};
var numLeaves = 0;
var Leaf = function(tree, obj)
{
- //Node *node = NodeFromPool(tree);
-
this.obj = obj;
- //this.bb = tree.getBB(obj);
tree.getBB(obj, this);
this.parent = null;
@@ -118,45 +125,59 @@ BBTree.prototype.incrementStamp = function()
// **** Pair/Thread Functions
+var numPairs = 0;
// Objects created with constructors are faster than object literals. :(
-var Pair = function(a, b)
+var Pair = function(leafA, nextA, leafB, nextB)
{
- this.a = a; this.b = b;
-};
+ this.prevA = null;
+ this.leafA = leafA;
+ this.nextA = nextA;
-var Thread = function(leaf, next)
-{
- this.prev = null;
- this.next = next;
- this.leaf = leaf;
+ this.prevB = null;
+ this.leafB = leafB;
+ this.nextB = nextB;
};
-// Benchmark this code with object pools on & off.
-/*
-BBTree.prototype.pairRecycle = function(pair)
+BBTree.prototype.makePair = function(leafA, nextA, leafB, nextB)
{
- this.pooledPairs.push(pair);
+ //return new Pair(leafA, nextA, leafB, nextB);
+ var pair = this.pooledPairs;
+ if (pair)
+ {
+ this.pooledPairs = pair.prevA;
+
+ pair.prevA = null;
+ pair.leafA = leafA;
+ pair.nextA = nextA;
+
+ pair.prevB = null;
+ pair.leafB = leafB;
+ pair.nextB = nextB;
+
+ //pair.constructor(leafA, nextA, leafB, nextB);
+ return pair;
+ } else {
+ numPairs++;
+ return new Pair(leafA, nextA, leafB, nextB);
+ }
};
-BBTree.prototype.pairFromPool = function()
+Pair.prototype.recycle = function(tree)
{
- return this.pooledPairs.pop() || new Pair(null, null);
+ this.prevA = tree.pooledPairs;
+ tree.pooledPairs = this;
};
-*/
-Thread.prototype.unlink = function()
+var unlinkThread = function(prev, leaf, next)
{
- var next = this.next;
- var prev = this.prev;
-
if(next){
- if(next.a.leaf == this.leaf) next.a.prev = prev; else next.b.prev = prev;
+ if(next.leafA === leaf) next.prevA = prev; else next.prevB = prev;
}
if(prev){
- if(prev.a.leaf == this.leaf) prev.a.next = next; else prev.b.next = next;
+ if(prev.leafA === leaf) prev.nextA = next; else prev.nextB = next;
} else {
- this.leaf.pairs = next;
+ leaf.pairs = next;
}
};
@@ -168,61 +189,45 @@ Leaf.prototype.clearPairs = function(tree)
this.pairs = null;
while(pair){
- if(pair.a.leaf == this){
- next = pair.a.next;
- pair.b.unlink();
- //tree.pairRecycle(pair);
- pair = next;
+ if(pair.leafA === this){
+ next = pair.nextA;
+ unlinkThread(pair.prevB, pair.leafB, pair.nextB);
} else {
- next = pair.b.next;
- pair.a.unlink();
- //tree.pairRecycle(pair);
- pair = next;
+ next = pair.nextB;
+ unlinkThread(pair.prevA, pair.leafA, pair.nextA);
}
+ pair.recycle(tree);
+ pair = next;
}
}
var pairInsert = function(a, b, tree)
{
var nextA = a.pairs, nextB = b.pairs;
- var pair = new Pair(new Thread(a, nextA), new Thread(b, nextB));
+ var pair = tree.makePair(a, nextA, b, nextB);
a.pairs = b.pairs = pair;
- //var pair = tree.pairFromPool();
- //Pair temp = {{null, a, nextA},{null, b, nextB}};
- //*pair = temp;
-
if(nextA){
- if(nextA.a.leaf == a) nextA.a.prev = pair; else nextA.b.prev = pair;
+ if(nextA.leafA === a) nextA.prevA = pair; else nextA.prevB = pair;
}
if(nextB){
- if(nextB.a.leaf == b) nextB.a.prev = pair; else nextB.b.prev = pair;
+ if(nextB.leafA === b) nextB.prevA = pair; else nextB.prevB = pair;
}
};
// **** Node Functions
-/*
-static void
-NodeRecycle(bbTree *tree, Node *node)
+Node.prototype.recycle = function(tree)
{
- node.parent = tree.pooledNodes;
- tree.pooledNodes = node;
-}
+ this.parent = tree.pooledNodes;
+ tree.pooledNodes = this;
+};
-static Node *
-NodeFromPool(bbTree *tree)
+Leaf.prototype.recycle = function(tree)
{
- Node *node = tree.pooledNodes;
-
- if(node){
- tree.pooledNodes = node.parent;
- return node;
- } else {
- // Pool is exhausted, make more
- }
-}*/
+ // Its not worth the overhead to recycle leaves.
+};
Node.prototype.setA = function(value)
{
@@ -236,12 +241,6 @@ Node.prototype.setB = function(value)
value.parent = this;
};
-/*
-static inline cpBool
-NodeIsLeaf(Node *node)
-{
- return (node.obj != null);
-}*/
Leaf.prototype.isLeaf = true;
Node.prototype.isLeaf = false;
@@ -255,10 +254,10 @@ Node.prototype.replaceChild = function(child, value, tree)
assertSoft(child == this.A || child == this.B, "Node is not a child of parent.");
if(this.A == child){
- //NodeRecycle(tree, parent.A);
+ this.A.recycle(tree);
this.setA(value);
} else {
- //NodeRecycle(tree, parent.B);
+ this.B.recycle(tree);
this.setB(value);
}
@@ -300,7 +299,7 @@ var subtreeInsert = function(subtree, leaf, tree)
if(subtree == null){
return leaf;
} else if(subtree.isLeaf){
- return new Node(tree, leaf, subtree);
+ return tree.makeNode(leaf, subtree);
} else {
var cost_a = subtree.B.bbArea() + bbTreeMergedArea(subtree.A, leaf);
var cost_b = subtree.A.bbArea() + bbTreeMergedArea(subtree.B, leaf);
@@ -389,16 +388,14 @@ var subtreeSegmentQuery = function(subtree, a, b, t_exit, func)
}
};
-/*
-static void
-SubtreeRecycle(bbTree *tree, Node *node)
+BBTree.prototype.subtreeRecycle = function(node)
{
- if(!NodeIsLeaf(node)){
- SubtreeRecycle(tree, node.A);
- SubtreeRecycle(tree, node.B);
- NodeRecycle(tree, node);
+ if(node.isLeaf){
+ this.subtreeRecycle(node.A);
+ this.subtreeRecycle(node.B);
+ node.recycle(this);
}
-}*/
+};
var subtreeRemove = function(subtree, leaf, tree)
{
@@ -409,7 +406,7 @@ var subtreeRemove = function(subtree, leaf, tree)
if(parent == subtree){
var other = subtree.otherChild(leaf);
other.parent = subtree.parent;
- //NodeRecycle(tree, subtree);
+ subtree.recycle(tree);
return other;
} else {
parent.parent.replaceChild(parent, parent.otherChild(leaf), tree);
@@ -465,11 +462,11 @@ var markLeaf = function(leaf, tree, staticRoot, func)
} else {
var pair = leaf.pairs;
while(pair){
- if(leaf == pair.b.leaf){
- if(func) func(pair.a.leaf.obj, leaf.obj);
- pair = pair.b.next;
+ if(leaf === pair.leafB){
+ if(func) func(pair.leafA.obj, leaf.obj);
+ pair = pair.nextB;
} else {
- pair = pair.a.next;
+ pair = pair.nextA;
}
}
}
@@ -551,7 +548,7 @@ BBTree.prototype.remove = function(obj, hashid)
this.count--;
leaf.clearPairs(this);
- //NodeRecycle(tree, leaf);
+ leaf.recycle(this);
};
BBTree.prototype.contains = function(obj, hashid)
@@ -643,7 +640,7 @@ var partitionNodes = function(tree, nodes, offset, count)
if(count == 1){
return nodes[offset];
} else if(count == 2) {
- return new Node(tree, nodes[offset], nodes[offset + 1]);
+ return tree.makeNode(nodes[offset], nodes[offset + 1]);
}
// Find the AABB for these nodes
@@ -750,7 +747,7 @@ BBTree.prototype.optimize = function()
nodes[i++] = this.nodes[hashid];
}
- //SubtreeRecycle(tree, root);
+ tree.subtreeRecycle(root);
this.root = partitionNodes(tree, nodes, nodes.length);
};
Please sign in to comment.
Something went wrong with that request. Please try again.