Thread history
Time | User | Activity | Comment |
---|---|---|---|
No results |
In the tests I did I found that the container didn't make any difference. I *did* try using the ResultHeap from your Gen2 tree, but it was quite a bit slower, the same speed as a linear-search ArrayList backed PrioQueue.
I think it isn't just a JIT issue, but also the fact that we generally only have ~50 elements in the queue. At these sizes something like an Insertion Sort is comparable in speed to a Heap Sort, so I thought I'd implement what I know first.
I'll give it another run, this time with your Gen3 heap.
Also, I tried using the PrioQueue as the stack for storing the tree nodes. I used the pointRectDist as the ordering factor, but it increased the number of points visited by ~30% so I'm not sure what I did wrong. I know you did something similar, how did you get it to work?
Regarding the stack of tree nodes, perhaps you had it sorting in the wrong direction or something? Based on my tests, when setting it up so it always visits the nearest unvisited node next, it pretty reliably decreased the number of points visisted. The overall performance gain was smaller than the reduction in points visited (due to the extra distance calculations Edit: not due to extra distance calculations, turns out I was calculating bounding box distances for unvisited paths before I implemented the stack of tree nodes), but it did did pay off slightly for me in any case.
OK, results are in. It turns out I still had some performance tucked away in my PrioQueue, like you thought. Odd that I didn't pick that up earlier.
Next, using your MaxHeap from Gen3 was faster than my old PrioQueue, but slower than the new one. Although it is really hard testing when you have shared dependencies without giving one a JIT advantage!
Using the PrioQueue (the fast one) instead of the ArrayDeque now reduces the number of nodes I visit by ~20%, but still takes longer than a stack - about the same amount longer as using the MaxHeap for the results. I skip pointRectDist checks on the chosen path when the results queue doesn't have any values, but beyond that it seems dead-end to me. It seems to me that the overhead from the PrioQueue instead of an ArrayDeque is more than I save by visiting less nodes. It also uses a few less pointRectDist calculations. I even tried trimming the stack occasionally when new data came in, thinking maybe it was growing and pushed out of the fast region. Maybe I'll try using the MinHeap, but otherwise I'm going to stick with my simple lazily-evaluate-pointRectDist ArrayDeque implementation.
So, new results without garbage-creation:
BEST RESULT: - #1 Skilgannon's Cache-hit KDTree [0.0286] - #2 Rednaxela's kd-tree (3rd gen) [0.0291] - #3 Rednaxela's kd-tree (2nd gen) [0.0319] - #4 Voidious' Bucket PR k-d tree [0.1619]
New results with garbage-creation:
BEST RESULT: - #1 Skilgannon's Cache-hit KDTree [0.0288] - #2 Rednaxela's kd-tree (3rd gen) [0.0314] - #3 Rednaxela's kd-tree (2nd gen) [0.0337] - #4 Voidious' Bucket PR k-d tree [0.1705]
Notice my Cache-hit tree isn't really affected by the garbage, while all the others are.
Ahh, nice stuff! Could you post both your current version of your tree and the code you're using for "garbage-creation"? I'm curious to do some tests with it.
Here is the garbage creation code:
public TestResult doTest(KNNImplementation algorithm, int numNeighbours, SampleData[] data, KNNPoint[][] solution) { TestResult result = new TestResult(algorithm.getName(), data.length, numNeighbours, solution); ArrayList<KNNPoint[]> neighbourLists = new ArrayList<KNNPoint[]>(); double sum = 0;//DEPENDENCY FOR GARBAGE int addCount = 0; for (SampleData sample : data) { if (sample.save) { long time = -System.nanoTime(); algorithm.addDataPoint(sample.entry); time += System.nanoTime(); result.recordAdd(time); addCount++; } if (sample.search) { long time = -System.nanoTime(); KNNPoint[] neighbourList = algorithm.getNearestNeighbors(sample.data, Math.min(numNeighbours, addCount)); time += System.nanoTime(); result.recordSearch(neighbourList, time); } //GARBAGE - vary size of array to vary effect - 0 should have no effect double[] garbageD = new double[1000]; int[] garbageI = new int[garbageD.length]; for(int i = 0; i < garbageD.length; i++){ garbageD[i] = Math.random(); garbageI[i] = (int)(Integer.MAX_VALUE*Math.random()); } for(int i = 0; i < garbageD.length; i++){ garbageD[i] *= garbageI[garbageD.length-1-i]; sum += garbageD[garbageD.length-1 - i]; garbageI[i] -= sum; } } if(sum == -1.0)//DEPENDENCY FOR GARBAGE System.out.println("Unlikely event, but cannot be eliminated."); return result; }
Hmm... I just tried running it, including your garbage-generation:
With the initially-uploaded version of your tree:
- #1 Rednaxela's kd-tree (3rd gen) [0.0281] - #2 Skilgannon's Cache-hit KDTree [0.0336] - #3 Rednaxela's kd-tree (2nd gen) [0.0347] - #4 Voidious' Linear search [0.5787]
With the newly-uploaded version of your tree:
- #1 Rednaxela's kd-tree (3rd gen) [0.0281] - #2 Skilgannon's Cache-hit KDTree [0.0292] - #3 Rednaxela's kd-tree (2nd gen) [0.0346] - #4 Voidious' Linear search [0.5756]
I'm running this version of KNN.jar with default options (all "yes", including standard data file and isolated processes), 40 nearest neighbors, 10 repetitions.
Could you maybe try running this jar file exactly as-is to see what that results in on your machine?