-
Notifications
You must be signed in to change notification settings - Fork 4
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #18 from mastodon-sc/improve-kd-tree-performance
Improve kd tree performance during initialization
- Loading branch information
Showing
4 changed files
with
256 additions
and
120 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,94 @@ | ||
package org.mastodon.util; | ||
|
||
import java.util.function.IntToDoubleFunction; | ||
|
||
/** | ||
* Class for partially sorting a list. The class is used in the KDTree. | ||
*/ | ||
public class KthElement | ||
{ | ||
private KthElement() | ||
{ | ||
// prevent instantiation | ||
} | ||
|
||
/** | ||
* Partially sort a sublist such that the element that would be at postition | ||
* {@code k} in a sorted list is at position {@code k}, elements before the k-th | ||
* are smaller or equal and elements after the k-th are larger or equal. | ||
* | ||
* @param i index of first element of the sublist | ||
* @param j index of last element of the sublist | ||
* @param k index for k-th smallest value. i <= k <= j. | ||
* @param rankMethod method that returns i-th rank of the i-th value in the list | ||
* @param swapMethod method that swaps entry i and j in the list | ||
*/ | ||
public static void kthElement( int i, int j, final int k, final IntToDoubleFunction rankMethod, final Swap swapMethod ) | ||
{ | ||
while ( i < j ) | ||
{ | ||
final int pivotpos = partitionSubList( i, j, rankMethod, swapMethod ); | ||
if ( k < pivotpos ) | ||
{ | ||
// partition lower half | ||
j = pivotpos - 1; | ||
} | ||
else //if ( k >= pivotpos ) | ||
{ | ||
// partition upper half | ||
i = pivotpos; | ||
} | ||
} | ||
} | ||
|
||
/** | ||
* Partition a sublist. | ||
* | ||
* The method does not swap entries for a correctly sorted list. | ||
* | ||
* @param left index of first element of the sublist | ||
* @param right index of last element of the sublist | ||
* @param rankMethod method that returns i-th rank of the i-th value in the list | ||
* @param swapMethod method that swaps entry i and j in the list | ||
* @return the index of the first element of the right partition | ||
*/ | ||
static int partitionSubList( final int left, final int right, final IntToDoubleFunction rankMethod, final Swap swapMethod ) | ||
{ | ||
final double pivot = rankMethod.applyAsDouble( ( left + right ) / 2 ); | ||
int i = left; | ||
int j = right; | ||
|
||
while ( true ) | ||
{ | ||
double ivalue = rankMethod.applyAsDouble( i ); | ||
while ( ivalue < pivot ) | ||
{ | ||
++i; | ||
ivalue = rankMethod.applyAsDouble( i ); | ||
} | ||
|
||
double jvalue = rankMethod.applyAsDouble( j ); | ||
while ( pivot < jvalue ) | ||
{ | ||
--j; | ||
jvalue = rankMethod.applyAsDouble( j ); | ||
} | ||
|
||
if ( i <= j ) | ||
{ | ||
if ( ivalue > jvalue ) // this avoids unnecessary swaps in case of ivalue = jvalue = pivot | ||
swapMethod.swap( i, j ); | ||
++i; | ||
--j; | ||
} | ||
|
||
if ( i > j ) | ||
return i; | ||
} | ||
} | ||
|
||
public interface Swap | ||
{ | ||
void swap( int i, int j ); | ||
} | ||
} |
40 changes: 40 additions & 0 deletions
40
src/test/java/org/mastodon/kdtree/KDTreeInitializationBenchmark.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,40 @@ | ||
package org.mastodon.kdtree; | ||
|
||
import net.imglib2.util.StopWatch; | ||
|
||
import org.mastodon.collection.RefList; | ||
import org.mastodon.collection.ref.RefArrayList; | ||
|
||
/** | ||
* Measure how long it takes to initialize a KDTree with 1_000_000 points | ||
* if the points are not randomly distributed, but lie on a circle. This | ||
* is a difficult case scenario for KDTree initialization. | ||
*/ | ||
public class KDTreeInitializationBenchmark | ||
{ | ||
public static void main( final String... args ) | ||
{ | ||
final int count = 1_000_000; | ||
|
||
final RealPointPool vertexPool = new RealPointPool( 3, count ); | ||
final RefList< RealPoint > positions = pointsInACircle( vertexPool, count ); | ||
|
||
final StopWatch watch = StopWatch.createAndStart(); | ||
for ( int i = 0; i < 10; i++ ) | ||
KDTree.kdtree( positions, vertexPool ); | ||
System.out.println( watch ); | ||
} | ||
|
||
private static RefList< RealPoint > pointsInACircle( final RealPointPool vertexPool, final int count ) | ||
{ | ||
final RefList< RealPoint > positions = new RefArrayList<>( vertexPool ); | ||
final RealPoint point = positions.createRef(); | ||
for ( int i = 0; i < count; i++ ) | ||
{ | ||
final double angle = 2 * Math.PI * i / count; | ||
point.init( Math.sin( angle ), Math.cos( angle ), 1 ); | ||
positions.add( point ); | ||
} | ||
return positions; | ||
} | ||
} |
Oops, something went wrong.