Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Slow execution under 64 bits. Possible RyuJIT bug?

I have the following C# code trying to benchmark under release mode:

using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace ConsoleApplication54
{
class Program
{
    static void Main(string[] args)
    {
        int counter = 0;
        var sw = new Stopwatch();
        unchecked
        {
            int sum = 0;
            while (true)
            {
                try
                {
                    if (counter > 20)
                        throw new Exception("exception");
                }
                catch
                {
                }

                sw.Restart();
                for (int i = 0; i < int.MaxValue; i++)
                {
                    sum += i;
                }
                counter++;
                Console.WriteLine(sw.Elapsed);
            }

        }
    }
}
}

I am on a 64-bit machine and VS 2015 installed. When I run the code under 32-bit, it runs each iteration around 0.6 seconds, printed to the console. When I run it under 64-bit then the duration for each iteration simply jumps to 4 seconds! I tried the sample code in my colleagues computer which only has VS 2013 installed. There both 32-bit and 64-bit versions run around 0.6 seconds.

In addition to that, if we just remove the try catch block, it also runs in 0.6 seconds with VS 2015 in 64-bit.

This looks like a serious RyuJIT regression when there is a try catch block. Am I correct ?

like image 421
Onur Gumus Avatar asked Nov 18 '15 07:11

Onur Gumus


1 Answers

Bench-marking is a fine art. Make a small modification to your code:

   Console.WriteLine("{0}", sw.Elapsed, sum);

And you'll now see the difference disappear. Or to put it another way, the x86 version is now just as slow as the x64 code. You can probably figure out what RyuJIT doesn't do what the legacy jitter did from this minor change, it doesn't eliminate the unnecessary

   sum += i;

Something you can see when you look at the generated machine code with Debug > Windows > Disassembly. Which is indeed a quirk in RyuJIT. Its dead code elimination isn't as thorough as the legacy jitter. Otherwise not entirely without reason, Microsoft rewrote the x64 jitter because of bugs that it could not easily fix. One of them was a fairly nasty issue with the optimizer, it had no upper-bound on the amount of time it spent on optimizing a method. Causing rather poor behavior on methods with very large bodies, it could be out in the woods for dozens of milliseconds and cause noticeable execution pauses.

Calling it a bug, meh, not really. Write sane code and the jitter won't disappoint you. Optimization does forever start at the usual place, between the programmer's ears.

like image 128
Hans Passant Avatar answered Nov 15 '22 13:11

Hans Passant