Thursday, May 28, 2009

The Dynamic Type and Runtime Overload Resolution

I've written about the upcoming dynamic keyword in C# a little bit already, but did you know that the dynamic type enables you to perform runtime resolution of method overloads?

In the past, the specific overload of a method to be called was statically defined at compile time within a generic method, and the least specific overload was selected as the method to be called at runtime. This all happened at compile time. Take the following example:


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

namespace ConsoleApplication6
{
class Program
{
static void Main(string[] args)
{
DoSomethingGeneric("Howdy!");
DoSomethingGeneric(1);
}

static void DoSomethingGeneric<T>(T value)
{
DoSomething(value);
}

static void DoSomething(object value)
{
Console.WriteLine("Object! {0}", value.GetType());
}

static void DoSomething(int value)
{
Console.WriteLine("Int!");
}

static void DoSomething(string value)
{
Console.WriteLine("String!");
}
}
}


The output of this method is the following:

Object! System.String
Object! System.Int32

Notice that regardless of the type you send in to DoSomethingGeneric, it's the object version that gets called. This is because there is no way for the C# 3.0 compiler to determine that the intention of the user is to call the integer or string versions of the overload at compile time, so the compiler opts for the most non-specific version of the overload, the object version, and compiles the call to the object version into the IL of the method.

This all changes now with the dynamic type. The above code will still work the same, but take the following code:


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

namespace ConsoleApplication6
{
class Program
{
static void Main(string[] args)
{
DoSomethingDynamic("Howdy!");
DoSomethingDynamic(1);
DoSomethingDynamic(DateTime.Now);
}

static void DoSomethingDynamic(dynamic value)
{
DoSomething(value);
}

static void DoSomething(object value)
{
Console.WriteLine("Object! {0}", value.GetType());
}

static void DoSomething(int value)
{
Console.WriteLine("Int!");
}

static void DoSomething(string value)
{
Console.WriteLine("String!");
}
}
}


This is a bit different. The output of this code is the following:

String!
Int!
Object! System.DateTime

So the dynamic type can help in runtime method overload resolution!

That being said, there is a slight performance penalty to using the dynamic object, but it wasn't quite as much as I expected. Here's the code I used to test the performance. Just for kicks, I threw in a reflection version as well, to see how fast that one returned...


using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Diagnostics;
using System.Reflection;

namespace ConsoleApplication6
{
class Program
{
static void Main(string[] args)
{
// performance tests.
Console.Write("Milliseconds to complete one million iterations of the generic method: ");
Test(() => DoSomethingGeneric("Howdy"), 10000000);
Console.Write("Milliseconds to complete one million iterations of the dynamic method: ");
Test(() => DoSomethingDynamic("Howdy"), 10000000);
Console.Write("Milliseconds to complete one million iterations of the reflection method: ");
Test(() => DoSomethingReflective("Howdy"), 10000000);
}

static void Test(Action action, int iterations)
{
Stopwatch sw = Stopwatch.StartNew();
for (int i = 0; i < iterations; i++)
action();
Console.WriteLine(sw.ElapsedMilliseconds);
}

static void DoSomethingGeneric<T>(T value)
{
DoSomething(value);
}

static void DoSomethingDynamic(dynamic value)
{
DoSomething(value);
}

static void DoSomethingReflective<T>(T value)
{
MethodInfo info = typeof(Program).GetMethod("DoSomething", BindingFlags.NonPublic | BindingFlags.Static, null, new Type[] { typeof(T) }, null);
if (info == null)
DoSomething(value);
else
info.Invoke(null, new object[] { value });
}

static void DoSomething(object value) { }

static void DoSomething(int value) { }

static void DoSomething(string value) { }
}
}


The output I got from this is the following:

Milliseconds to complete one million iterations of the generic method: 136
Milliseconds to complete one million iterations of the dynamic method: 2589
Milliseconds to complete one million iterations of the reflection method: 102552

Needless to say, dynamic is much faster than reflection, though slightly slower than the generic version... which doesn't call the proper overload anyways.

4 comments:

katman26 said...

First off, way to go Morton. Now, I'm learning things from you! :) Right on!

Second, I may be a little behind the times, but I'm just starting to play with VS2010 and .NET 4.0. So, I thought I'd give your code samples here a try. I noticed a couple of interesting things that I thought I'd throw out there.

I made a few little changes to my version of your code. I declare a "value" object, and then pass that in to each "Test" method. If you do something like this:

int iterations = 10;
object value = "Howdy";
Test(() => DoSomethingGeneric(value), iterations);
Test(() => DoSomethingDynamic(value), iterations);
Test(() => DoSomethingReflective(value), iterations);

The "Generic" AND "Reflective" versions will call the wrong method, while the "Dynamic" still works like a champ. If you change the declaration from "object value" to "var value" the "Reflective" version begins to work again. More props for dynamic vs. generic/reflective.

The other thing I found was that when using smaller iterations, the "Dynamic" version is much less performant than the other two. Here's what I got:

100 iterations:
Generic: 3ms
Dynamic: 418ms
Reflective: 3ms

1,000 iterations:
Generic: 1ms
Dynamic: 356ms
Reflective: 27ms

10,000 iterations:
Generic: 3ms
Dynamic: 330ms
Reflective: 280ms

100,000 iterations: (looks like the breaking point)
Generic: 24ms
Dynamic: 437ms
Reflective: 2929ms

So, it looks like the "Dynamic" version is fairly consistent no matter how many iterations are used. But, in a practical application, it might be useful to use a hybrid approach (similar to a HybridDictionary idea).

Seems like this is just primed and ready for a revamped version of the CSLA DataPortal fetch methods. :)

Anyways, I just thought I'd throw my $0.02 out there.

Thanks for the info!

William Garrison said...

Wow, this is very informative!

This is... dubiously good. It seems like "dynamic" is doing at run time, what the compiler should have been doing at compile time all along.

If instead, the compiler generated a method with the appropriate type information at compile time (like Microsoft Visual C++ does) it would avoid the performance penalty without requiring an additional keyword.

William Garrison said...

Upon reflection, .NET can't do it at compile-time. The best it could do is to generate the code at load-time. In C++, templates don't really work in dynamic libraries since it requires having the source code to the templated methods in the header file. So the compiler has more information, and can make some assumptions that .NET cannot. So there must be some "link-time" overhead. The keyword still seems like an unnecessary add-on though. Generics should have just done this from the start.

David Morton said...

Well, it's exactly the performance issue that is the reason that generics don't automatically resolve the most specific method at runtime, and it's fully impossible for generics to resolve the most specific method at compile time, especially for a generic public method, which could accept literally any type in some situations.

Generics does it to a certain extent, but there are some limits to it that are set, and I think that's a good thing.