16

I have measured the execution time for two ways of calculating the power of 2:

1) Inline

result = b * b;

2) With a simple function call

result = Power(b);

When running in Debug mode, everything is as expected: Calling a function is considerably more expensive than doing the calculation in line (385 ms in line vs. 570 ms function call).

In release mode, I'd expect the compiler to speed up execution time of the function call considerably because the compiler would inline internally the very small Power() function. But I'd NOT expect the function call to be FASTER than the manual inlined calculation.

Most astonishingly this is the case: In the release build, the first run needs 109 ms and the second run with the call to Power() needs only 62 ms.

How can a function call be faster than manual inlining?

Here is the program for your reproduction:

class Program
{
    static void Main(string[] args)
    {
        Console.WriteLine("Starting Test");

        // 1. Calculating inline without function call
        Stopwatch sw = Stopwatch.StartNew();

        for (double d = 0; d < 100000000; d++)
        {
            double res = d * d;
        }

        sw.Stop();
        Console.WriteLine("Checked: " + sw.ElapsedMilliseconds);

        // 2. Calulating power with function call
        Stopwatch sw2 = Stopwatch.StartNew();

        for (int d = 0; d < 100000000; d++)
        {
            double res = Power(d);
        }

        sw2.Stop();
        Console.WriteLine("Function: " + sw2.ElapsedMilliseconds);

        Console.ReadKey();
    }

    static double Power(double d)
    {
        return d * d;
    }
}
Smi
  • 13,850
  • 9
  • 56
  • 64
Knasterbax
  • 7,895
  • 1
  • 29
  • 23
  • Did you start the program under the debugger (F5)? In that case optimizations were suppressed. – usr Mar 25 '13 at 10:11
  • 1
    Did you run the generated .exe or within VS in release mode? Also, have you tried calling them in different orders? I've found this makes a subtle difference – DGibbs Mar 25 '13 at 10:12
  • 1
    Well, why is using an int **as a double** faster than just using a double? – Vercas Mar 25 '13 at 10:14
  • 2
    @Vercas: Most likely because incrementing and comparing a double is more expensive. – Daniel Hilgarth Mar 25 '13 at 10:15
  • You need to specify the CLR version (2 or 4) as well as the bitness. All 4 different combinations will produce different results. – leppie Mar 25 '13 at 10:17

3 Answers3

51

Your test is wrong. In the second part you use a int d instead of a double. Maybe it explains the time difference.

Xavier Delamotte
  • 3,519
  • 19
  • 30
22

As Xavier correctly spotted, you are using double in one loop and int in the other. Changing both to the same type will make the results the same - I tested it.

Furthermore: What you are really measuring here is the duration of additions and comparisons. You are not measuring the duration of the squaring of d, because it simply is not happening: In a release build, the optimizer completely removes the body of the loop, because the result is not used. You can confirm this by commenting out the body of the loop. The duration will be the same.

Daniel Hilgarth
  • 171,043
  • 40
  • 335
  • 443
3

Daniel Hilgarth is right, calculation is not happening at all as the result of is not used (which is probably not the case in Debug mode). Try the following example and you'll get correct results:

static void Main(string[] args)
    {
        Console.WriteLine("Starting Test");
        var list = new List<int>();
        // 1. Calculating inline without function call
        Stopwatch sw = Stopwatch.StartNew();

        for (int d = 0; d < 100000000; d++)
        {
            int res = d * d;
            list.Add(res);
        }

        sw.Stop();
        Console.WriteLine("Checked: " + sw.ElapsedMilliseconds);
        // 2. Calulating power with function call

        list = new List<int>();
        Stopwatch sw2 = Stopwatch.StartNew();

        for (int d = 0; d < 100000000; d++)
        {
            int res = Power(d);
            list.Add(res);
        }

        sw2.Stop();
        Console.WriteLine("Function: " + sw2.ElapsedMilliseconds);

        Console.ReadKey();
    }
realnero
  • 994
  • 6
  • 12
  • 2
    Wont this code throw an `OverflowException` since `res` will eventually be way over `Int32.MaxValue` in those loops ? – Drewman Mar 25 '13 at 12:24