Division Vs Multiplication Equivalent
It’s a common belief in the world of programming that division is slower than multiplication. Not in order of magnitudes, but relatively. Granted, it depends on the underlying architecture, but generally speaking, a computer will be able to calculate the product of two numbers faster than when dividing.
Well, you guessed it, I came across quite a bit of code that had simple fractional division embedded within it:
1 2 3 4 5 6 7 8 9 10 |
for (int index = 1; index < maxLoops; index++) { //Did some stuff total = sum; half = sum / 2; third = sum / 3; quarter = sum / 4; //Did some more stuff with the half, third, quarter values. } |
That’s where this Curious Consultant started wondering… is there a C# performance impact using division vs multiplication equivalent?
That is, if the code was rewritten as follows:
1 2 3 4 5 6 7 8 9 10 |
for (int index = 1; index < maxLoops; index++) { //Did some stuff total = sum; half = sum * 0.5; third = sum * 0.3333; quarter = sum * 0.25; //Did some more stuff with the half, third, quarter values. } |
Let’s find out!
The test is pretty straight forward
The code is written in Visual Studio 2017 targeting .Net Framework version 4.8 x64, compiled in “Release” mode the build option “Optimize Code” checked. The source code is available at the end of this blog so you can benchmark it on your own system.
Assumptions:
- No exception handling as no exceptions should occur
- Only fractional variants with “1/” are used. Otherwise there are more operations involved. Eg, 2/3 means multiple x by 2 and then divide by 3.
- to make the test worth-while, several fractional variants were used: 1/2, 1/3, 1/4, 1/5, 1/8, 1/100, 1/1000
The exe file was run under Windows 10 Professional 64-bit with 32GB of memory.
The test was run with each fraction and its equivalent the following number of times:
- 10,000
- 1,000,000
- 100,000,000
Totals are summed at the end of each run to verify the same number of operations were performed.
Are you a betting person?
Before starting, I predicted that the multiplication equivalent would have a winning streak in the results. Whether it won by a little or a lot didn’t bother me so much as that it won consistently.
This test was run 3 times, but only the last set of results are being shown below as the results were equivalent for each run.
All times are indicated in minutes.seconds.milliseconds format. Lower numbers indicate faster run time performance.
Winners are highlighted in green; there are no points for second place.
|
Number of Loops |
||||
|
10,000 |
1,000,000 |
100,000,000 |
||
1 / 2 |
00:00.0000307 |
00:00.0039268 |
00:00.3399387 |
||
0.5 |
00:00.0000303 |
00:00.0033720 |
00:00.3642056 |
||
|
|||||
1 / 3 |
00:00.0000307 |
00:00.0035239 |
00:00.3522421 |
||
0.3333 |
00:00.0000348 |
00:00.0030266 |
00:00.3392868 |
||
|
|||||
1 / 4 |
00:00.0000324 |
00:00.0029968 |
00:00.3369088 |
||
0.25 |
00:00.0000319 |
00:00.0041509 |
00:00.3342529 |
||
|
|||||
1 / 5 |
00:00.0001036 |
00:00.0042574 |
00:00.3432200 |
||
0.2 |
00:00.0000371 |
00:00.0038947 |
00:00.3395073 |
||
|
|||||
1 / 8 |
00:00.0000339 |
00:00.0034149 |
00:00.3196951 |
||
0.125 |
00:00.0000472 |
00:00.0033856 |
00:00.3171413 |
||
|
|||||
1 / 100 |
00:00.0001030 |
00:00.0032920 |
00:00.3273697 |
||
0.01 |
00:00.0000300 |
00:00.0037606 |
00:00.3185416 |
||
|
|||||
1 / 1000 |
00:00.0000332 |
00:00.0034861 |
00:00.3193875 |
||
0.001 |
00:00.0000703 |
00:00.0028892 |
00:00.3158620 |
In Summary:
The prediction is on track. In C#, when deciding to use division vs multiplication equivalent, the multiplication equivalent won 15/21 times. In the other two runs, it was also 15/21 and 16/21.
There’s no distinct pattern or threshold that appears in the results of when to use one versus the other although overall multiplication is the winner.
And when it did win, with few exceptions, it wasn’t by leaps in bounds.
So in the more practical world, it comes back down to whatever is easiest for reading/writing code.
But if you have the need for speed, definitely use the multiplication equivalent where possible.
The Code:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 |
using System; using System.Collections.Generic; using System.Collections; using System.Collections.Concurrent; using System.Data; using System.Data.Sql; using System.IO; using System.Linq; using System.Text; using System.Text.RegularExpressions; using System.Threading.Tasks; using System.Threading; using System.Diagnostics; namespace TestApplication { class Program { static void Main(string[] args) { DateTime end; DateTime start = DateTime.Now; Console.WriteLine("### Overall Start Time: " + start.ToLongTimeString()); Console.WriteLine(); TestDivisionVsEquivalentMultiplication(10000, 1, 2); TestDivisionVsEquivalentMultiplication(1000000, 1, 2); TestDivisionVsEquivalentMultiplication(100000000, 1, 2); TestDivisionVsEquivalentMultiplication(10000, 1, 3); TestDivisionVsEquivalentMultiplication(1000000, 1, 3); TestDivisionVsEquivalentMultiplication(100000000, 1, 3); TestDivisionVsEquivalentMultiplication(10000, 1, 4); TestDivisionVsEquivalentMultiplication(1000000, 1, 4); TestDivisionVsEquivalentMultiplication(100000000, 1, 4); TestDivisionVsEquivalentMultiplication(10000, 1, 5); TestDivisionVsEquivalentMultiplication(1000000, 1, 5); TestDivisionVsEquivalentMultiplication(100000000, 1, 5); TestDivisionVsEquivalentMultiplication(10000, 1, 8); TestDivisionVsEquivalentMultiplication(1000000, 1, 8); TestDivisionVsEquivalentMultiplication(100000000, 1, 8); TestDivisionVsEquivalentMultiplication(10000, 1, 100); TestDivisionVsEquivalentMultiplication(1000000, 1, 100); TestDivisionVsEquivalentMultiplication(100000000, 1, 100); TestDivisionVsEquivalentMultiplication(10000, 1, 1000); TestDivisionVsEquivalentMultiplication(1000000, 1, 1000); TestDivisionVsEquivalentMultiplication(100000000, 1, 1000); end = DateTime.Now; Console.WriteLine(); Console.WriteLine("### Overall End Time: " + end.ToLongTimeString()); Console.WriteLine("### Overall Run Time: " + (end - start)); Console.WriteLine(); Console.WriteLine("Hit Enter to Exit"); Console.ReadLine(); } //Tests which is faster - division or the equivalent multiplication. Eg, (x / 2) or (x * 0.5). static void TestDivisionVsEquivalentMultiplication(int numberOfComparisons, double dividend, double divisor) { Console.WriteLine("######## " + System.Reflection.MethodBase.GetCurrentMethod().Name); Console.WriteLine("Number of comparisons: " + numberOfComparisons.ToString("#,##0")); Stopwatch sw = new Stopwatch(); DateTime end = DateTime.Now; DateTime start = DateTime.Now; double totalSum = 0; double multiplier = dividend / divisor; Console.WriteLine("###########################################################"); Console.WriteLine("Starting division " + dividend + "/" + divisor + " test with " + numberOfComparisons.ToString("#,##0") + " at: " + DateTime.Now.ToLongTimeString()); sw.Restart(); for (int x = 0; x < numberOfComparisons; x++) { totalSum += (dividend / divisor); } sw.Stop(); Console.WriteLine("Finished at: " + DateTime.Now.ToLongTimeString()); Console.WriteLine("Time to run: " + sw.Elapsed.ToString("mm\\:ss\\.fffffff")); Console.WriteLine("Sum is: " + totalSum.ToString("#,##0")); totalSum = 0; Thread.Sleep(500); Console.WriteLine("###########################################################"); Console.WriteLine("Starting equivalent multiplication " + multiplier + " test with " + numberOfComparisons.ToString("#,##0") + " at: " + DateTime.Now.ToLongTimeString()); sw.Restart(); for (int x = 0; x < numberOfComparisons; x++) { totalSum += (dividend * multiplier); } sw.Stop(); Console.WriteLine("Finished at: " + DateTime.Now.ToLongTimeString()); Console.WriteLine("Time to run: " + sw.Elapsed.ToString("mm\\:ss\\.fffffff")); Console.WriteLine("Sum is: " + totalSum.ToString("#,##0")); Thread.Sleep(500); Console.WriteLine(); sw = null; } } //class } //namespace |