2

I have a WCF service that uses LINQ to SQL for its data layer. Only stored procedures are in use, no dynamic table access. When I target x64, I am getting half the throughput of an x86 build. I have traced the hot path to Reflection.Emit.DynamicMethod.CreateDelegate. I created a simple test project to demonstrate the difference in performance between the two platforms.

What is the specific explanation for DynamicMethod being so much slower on x64? My vague understanding is that there may be an additional thunk involved in DynamicInvoke on x64.


Here are the results when performed on Windows 7 Enterprise x64, Core i7 Q720 @ 1.60 GHz, single-threaded:

Build Target      Average milliseconds to execute 100,000 iterations
x86               5504
x64               14699
Any CPU           14789

And the test code:

class Program
{
    private delegate string XInvoker(string arg);

    private const int OUTER_ITERATIONS = 4;
    private const int INNER_ITERATIONS = 100000;

    static void Main(string[] args)
    {
        Console.WriteLine("Timing {0} iterations, repeat {1} times...", INNER_ITERATIONS, OUTER_ITERATIONS);

        var watch = new Stopwatch();
        long totalMs = 0;

        for (int outer = 0; outer < OUTER_ITERATIONS; outer++)
        {
            watch.Restart();

            for (int inner = 0; inner < INNER_ITERATIONS; inner++)
            {
                var method = new DynamicMethod("X", typeof(string), new[] { typeof(string) });

                var ilGen = method.GetILGenerator();
                ilGen.Emit(OpCodes.Ldarg_0);
                ilGen.Emit(OpCodes.Ret);

                var del = method.CreateDelegate(typeof(XInvoker));
                var blah = del.DynamicInvoke("blah");
            }

            watch.Stop();
            totalMs += watch.ElapsedMilliseconds;

            Console.WriteLine("Took {0} ms to iterate {1} times", watch.ElapsedMilliseconds, INNER_ITERATIONS);
        }

        Console.WriteLine();
        Console.WriteLine("Overall average: {0} ms to iterate {1} times", totalMs / OUTER_ITERATIONS, INNER_ITERATIONS);
    }
}
Aidan Ryan
  • 11,389
  • 13
  • 54
  • 86
  • Normally, your CreateDelegate wouldn't be in an inner loop, though, right? – antlersoft Mar 13 '12 at 20:39
  • It is effectively in an inner loop when I am performing load testing of the WCF service. – Aidan Ryan Mar 13 '12 at 20:40
  • @AidanRyan, Can you try to call your Main method more than once? ( rename your Main to Main2 and write a new Main calling Main2 in a loop). – L.B Mar 13 '12 at 21:35
  • Since I guess your 2nd run will be much faster without JITting – L.B Mar 13 '12 at 21:52
  • Just for kicks, I tried it - no change. The main JIT overhead of actually emitting and invoking the dynamic method can't be avoided. – Aidan Ryan Mar 13 '12 at 22:01
  • Your code is incredibly synthetic. You are basically measuring how well the level 1 cpu cache works in 64-bit code. Sure, not so well, everything is double the size. You won't get ahead until you write *real* code, the kind of code that can take advantage of better x64 instructions and 8 more cpu registers. And no, it still won't be faster. Most real world problems don't need to count past 2 billion. They just need more memory. Plenty of that. – Hans Passant Mar 14 '12 at 00:27
  • Of course it is synthetic - it is a demonstration of the specific bottleneck of running LINQ to SQL in x64. I am calling out the code path within LINQ to SQL that is responsible for its worse performance on x64. I am not expecting x64 to be faster, not a magic bullet, just wondering what the specific reason for the difference is. – Aidan Ryan Mar 14 '12 at 04:03

1 Answers1

1

I would guess it's to do with the speed of compilation. There are lots of threads that seem to indicate JIT compiling for x64 is significanltly slower than x86.

In this one case someone saw significant performance increase in their x64 JIT just because other dependent assemblies weren't NGEN'd. Although I doubt it would help in this scenario, you never know what other things it is trying to load that might be slowing it down. Perhaps try running the command in the answer and see if that changes your performance. WPF slow to start on x64 in .NET Framework 4.0

Community
  • 1
  • 1
AaronLS
  • 37,329
  • 20
  • 143
  • 202