Benchmarking KDTree


KDTree should be fast for searches, although not necessarily fast for creation/addition/deletion. 


/////////// Construction

(

// Change these two parameters if you like:

~dims = 3;

~num = 10000;

// Test either with uniformly-distributed data or quasi-gaussian data - will get different performance

~rand = {|n| n.rand };

~rand = {|n| n.sum3rand };



~halffillsize = (~num * 2).pow(1/~dims).floor.asInteger;

~array = { {~rand.value(~halffillsize)}.dup(~dims) }.dup(~num);

"KDTree construction:".postln;

bench{~tree = KDTree(~array)};

""

);


////////// Exact-match search

// Note: this exact-match finding is only faster than Array for large dataset sizes (e.g. 100000, I find).

// But the other types of search (e.g. nearest neighbour) are very fast, compared to 

// the brute-force approach that would have to be used on a flat array.

(

"100 finds using array:".postln;

bench{

100.do{

~array.indexOf({~halffillsize.rand}.dup(~dims));

};

};

"100 finds using kd-tree:".postln;

bench{

100.do{

~tree.find({~halffillsize.rand}.dup(~dims));

};

};

""

);


/////////// general nearest neighbour to arbitrary spatial point

(

"Nearest neighbour query time estimated average:".postln;

(""++(bench({

100.do{

~tree.nearest({~halffillsize.rand}.dup(~dims));

}

}, false)/100) + "seconds").postln;

""

);

//////////// nearest neighbour to a node - this test will TAKE A WHILE for large trees since it runs once on each node

(

"Nearest neighbour to each node, queried separately - query time average:".postln;

(""++(bench({

~tree.collect{ |node|

node.nearestToNode

}

}, true)/~tree.size) + "seconds per node").postln;

""

);

//////////// All Nearest Neighbours. Can theoretically re-use data to optimise re the separate query above.

(

"All Nearest Neighbours (single call, should ideally be faster due to ability to re-use data):".postln;

(""++(bench({

~tree.allNearest

}, true)/~tree.size) + "seconds per node (but note: individual queries don't exist)").postln;

""

);