I saw a simple method to calculate Log2(N), where N is the power of 2:
unsigned int Log2(unsigned int N)
{
unsigned int n = 0;
while (N >>= 1)
{
++n;
}
return n;
}
It would be not likely very necessary, but I try to improve it in a simple way, the idea comes from binary search.
unsigned int Log2New(unsigned int N)
{
unsigned int bits = sizeof(N) * 4;
unsigned int n = 0;
while (N > 1)
{
if (N >> bits)
{
N >>= bits;
n += bits;
}
bits >>= 1;
}
return n;
}
I run the test in Visual Studio 2013, 32 bits release build and fully optimized. When I look into generated disassembly, both methods are inlined, and there are nothing special in both function. There is only one shr command as expected in
if (N >> bits) { N >>= bits; ... }
I test both of the method by sending 1 << n, n from 0 to 31 to them, and repeat for 1000000 times, and count the time separately.
The new method is about 1.66 times faster than the old one,
Then I make a further change, expand the while loop in the new method. (There was a word for this 'expand', but I don't remember it exactly 8-| )
To make it clear, I make a new define, and the method become:
#define CountShift(bits) if ((N)>>(bits)) { (N)>>=(bits); (n) += (bits); }
unsigned int Log2NewExpand(unsigned int N)
{
unsigned int n = 0;
CountShift(16);
CountShift(8);
CountShift(4);
CountShift(2);
CountShift(1);
return n;
}
Looking into the disassembly again, as expected, it's fully serialized, only 5 test, je shr and add commands.
Timing again, the expanded method is again about 1.66 times faster then the new one, and about 2.8 times faster than the original method.
I think there must be some more space for improvement. Any suggestion are welcome.