views:

70

answers:

3

I'm working on a data structure which subdivides items into quadrants, and one of the bottlenecks I've identified is my method to select the quadrant of the point. Admittedly, it's fairly simple, but it's called so many times that it adds up. I imagine there's got to be an efficient way to bit twiddle this into what I want, but I can't think of it.

private int Quadrant(Point p)
{
    if (p.X >= Center.X)
        return p.Y >= Center.Y ? 0 : 3;
    return p.Y >= Center.Y ? 1 : 2;
}

Center is of type Point, coordinates are ints. Yes, I've run a code profile, and no, this isn't premature optimization.


Because this is only used internally, I suppose my quadrants don't have to be in Cartesian order, as long as they range from 0-3.

A: 

My first try would be to get rid of the nested conditional.

int xi = p.X >= Center.X ? 1 : 0;
int yi = p.Y >= Center.Y ? 2 : 0;
int quadrants[4] = { ... };
return quadrants[xi+yi];

The array lookup in quadrants is optional if the quadrants are allowed to be renumbered. My code still needs two comparisons but they can be done in parallel.

I apologise in advance for any C# errors as I usually code C++.


Perhaps something more efficient is possible when two unsigned 31 bit coordinates are stored in a 64 bit unsigned long variable.

// The following two lines are unnecessary
// if you store your coordinated in unsigned longs right away
unsigned long Pxy = (((unsigned long)P.x) << 32) + P.y;
unsigned long Centerxy = (((unsigned long)Center.x) << 32) + Center.y;

// This is the actual calculation, only 1 subtraction is needed.
// The or-ing with ones hast only to be done once for a repeated use of Centerxy.
unsigned long diff = (Centerxy|(1<<63)|(1<<31))-Pxy;
int quadrant = ((diff >> 62)&2) | ((diff >> 31)&1);

Taking a step back, a different solution is possible. Do not arrange your data structure split into quadrants right away but alternately in both directions. This is also done in the related Kd-tree

Peter G.
I doubt this would be much faster. You doing the same number of conditional checks, but you're also adding an array lookup. The code may be more concise, but you'd have to profile it to prove it's faster.
LBushkin
int xi = (int)(p.X >= Center.X);int yi = (int)(p.Y >= Center.Y) * 2; dirty way?
arul
@arul: You cannot cast a `bool` to any numeric type in C#. This won't compile.
LBushkin
@arul That shouldn't make any difference with enabled optimization if changed to a compiling version.
Peter G.
+1  A: 

The fastest way in C/C++ it would be

(((unsigned int)x >> 30) & 2) | ((unsigned int)y >> 31)

(30/31 or 62/63, depending on size of int). This will give the quadrants in order 0, 2, 3, 1.

Edit for LBushkin:

(((unsigned int)(x - center.x) >> 30) & 2) | ((unsigned int)(y-center.y) >> 31)
ruslik
This doesn't take into account an arbitrary centerpoint around which to construct quadrants.
LBushkin
I've finished the structure I was working on. Thanks for your comment, but I wasn't able to get the time down too significantly, so I stuck with my original implementation. If you'd like to comment on my class, you can find it on this answer: http://stackoverflow.com/questions/3142919/optimal-high-density-binary-space-partition-for-grids/3379594#3379594
Daniel Rasmussen
A: 

I don't know that you can make this code dramatically faster in C#. What you may be able to do, however, it look at how you're processing points, and see if you can avoid making unecessary calls to this method. Perhaps you could create a QuadPoint structure that stores which quadrant a point is in (after you compute it once), so that you don't have to do so again.

But, admittedly, this depends on what your algorithm is doing, and whether it's possible to store/memoize the quadrant information. If every point is completely unique, this obviously won't help.

LBushkin