DISCLAIMER
I do not want to know when or if to use shift operators in my code, I am interested in why multiplication is faster than shifting bits to the left whereas division is not.
As I was just wandering around SO I came across this question regarding efficiency and speed of division and bit shifting. It basically states that although one might save a few seconds when performing bit shifts on powers of 2, it is not some difference one has to worry about.
Intrigued by this I decided to check how much faster bit shifting in C# actually is and realised something strange:
Bit shifting instead of dividing is faster, as I expected, but the "normal" multiplication method is faster than bit shifting.
My question is simple: Why is the multiplication of two numbers faster than bit shifting, although bit shifting is a primitive operation for the processor?
Here are the results for my test case:
Division: | Multiplication:
Bit shift: 315ms | 315ms
normal: 406ms | 261ms
The times are the averages of 100 cases with each case consisting of 10 operations per number on 10000000 random positive numbers ranging from 1 to int.MaxValue
. The operations ranged from dividing/multiplying by 2 to 1024 (in powers of 2) and bit shifting from 1 to 10 digits.
EDIT
@usr: I am using .NET version 4.5.1
I updated my results because I realised I only computed a tenth of the numbers I stated... facepalm
My Main:
static Main(string[] args)
{
Fill(); // fills the array with random numbers
Profile("division shift:", 100, BitShiftDiv);
Profile("division:", 100, Div);
Profile("multiplication shift:", 100, BitShiftMul);
Profile("multiplication:", 100, Mul);
Console.ReadKey();
}
This is my profiling method:
static void Profile(string description, int iterations, Action func)
{
GC.Collect()
GC.WaitForPendingFinalizers();
GC.Collect();
func();
Stopwatch stopWatch = Stopwatch.StartNew();
for (int i = 0; i < iterations; i++)
{
func();
}
stopWatch.Stop();
Console.WriteLine(description);
Console.WriteLine("total: {0}ms", stopWatch.Elapsed.TotalMilliseconds);
Console.WriteLine(" avg: {0}ms", stopWatch.Elapsed.TotalMilliseconds / (double)iterations);
}
The Actions containing the operations are structured like this:
static void <Name>()
{
for (int s = 1; s <= 10; s++) /* for shifts */
for (int s = 2; s <= 1024; s++) /* for others */
{
for (int i = 0; i < nums.Length; i++)
{
var useless = nums[i] <shift> s; /* shifting */
var useless = nums[i] <operator> s; /* otherwise */
}
}
}
nums
is a public array containing 10000000
int
s, which is filled by the Fill()
method.