CodeThinked

codethinked (kōdthĭngked) adj. To be consumed by or obsessed with code.

C# 4.0 New Features Part 1 - dynamic keyword

UPDATE: I have posted some more performance numbers on the dynamic keyword in a second post

One of the coolest new features in C# 4.0 that has been announced at PDC is the new dynamic keyword. This keyword allows the developer to declare an object whose method calls will be resolved at runtime. The interesting part about it is that the class doesn't need to be declared in any special way to use this keyword, it is all up to the consumer.

So, lets just declare a normal class like this:

public class TestClass
{
    public void TestMethod1()
    {
        Console.WriteLine("test method 1");
    }

    public void TestMethod2()
    {
        Console.WriteLine("test method 2");
    }        
}

And now we could instantiate and call a few methods on this class just like this:

var test = new TestClass();
test.TestMethod1();
test.TestMethod2();

At this point everything is working exactly as you expect it, and it all compiles just fine. But now, lets throw in the dynamic keyword:

dynamic test = new TestClass();
test.TestMethod1();
test.TestMethod2();

Okay, so nothing changed, right? Wrong. Everything builds just as before, but now those method calls on our test method are not being resolved at compile time, they are being resolved at runtime! So, say we did this:

dynamic test = new TestClass();
test.TestMethod1();
test.TestMethod2();
test.TestMethod3();

This would still compile! But at runtime we would see something like this:

Runtime Error

Pretty big implications, huh? So, what is happening here? This is where our friend Reflector comes in:

private static void Main(string[] args)
{
    object test = new TestClass();
    if (<Main>o__SiteContainer0.<>p__Site1 == null)
    {
        <Main>o__SiteContainer0.<>p__Site1 = CallSite<Action<CallSite, object>>.Create(new CSharpCallPayload(Microsoft.CSharp.RuntimeBinder.RuntimeBinder.GetInstance(), false, false, "TestMethod1", typeof(object), null));
    }
    <Main>o__SiteContainer0.<>p__Site1.Target(<Main>o__SiteContainer0.<>p__Site1, test);
    if (<Main>o__SiteContainer0.<>p__Site2 == null)
    {
        <Main>o__SiteContainer0.<>p__Site2 = CallSite<Action<CallSite, object>>.Create(new CSharpCallPayload(Microsoft.CSharp.RuntimeBinder.RuntimeBinder.GetInstance(), false, false, "TestMethod2", typeof(object), null));
    }
    <Main>o__SiteContainer0.<>p__Site2.Target(<Main>o__SiteContainer0.<>p__Site2, test);
}

So this is what our main method looks like when we reflect it. It may be hard to follow, but make sure you look at the line numbers on the side to see where the wrapping occurs. First the compiler generated the "__SiteContainer0" local field in order to hold our callsite info. Next you will see that our "test" variable is just of class "object" now! There really isn't a dynamic type, it is just a helper.

Next you see that we are checking if these callsites are null, and if they are, then we are using "CallSite.Create" and passing in a "CSharpCallPayload" object which has all the info about the method that we are trying to call. Once the callsite is defined, then we just invoke that callsite on our "test" instance by passing the callsite data and the object. The compiler has done all of this for us, we just need to sit back and let it happen.

So, in this instance what we are doing is pretty useless, but the power of this feature comes in when we are using a type whose methods we do not necessarily know at compile time. This could be because it is coming from some dynamic code (like IronRuby!), or it is a generated class that we don't have compile time type info for, or anywhere that you are currently using heavy reflection for.

So, the first thing that popped into my mind when I saw this was, "what are the performance implications of this?" I know that this is a super early CTP, but I figured that I would run a few unscientific tests anyways.

So, here is the highly scientific process I used:

I changed to Release build and I put the two method calls in a loop surrounded by a stopwatch. The loop ran 1 million times, and so invoked a total of 2 million method calls. I then wrote out the number of milliseconds it took to execute to the screen. I also modified the TestClass to not write to the screen, since that would take significantly more time than the method calls. Instead, I changed the class to just add numbers, like this:

public class TestClass
{
    public int i;
    public void TestMethod1()
    {
        i += 1;
    }

    public void TestMethod2()
    {
        i += 2;
    }        
}

I ran each of them once, and then threw away the first result. I then ran them each 7 times and threw away the highest and the lowest. Then averaged the 5 numbers left.

Compile Time Bound: 6 ms - yep, 2 million calls, 6 ms. Fast.

Dynamically Bound: 2106ms - so, roughly 351 times slower than the strongly typed calls

Now, this is a super early CTP and they will obviously optimize this, but they still are probably going to be many times slower than the compile time calls. The thing to realize here is that while these numbers are very different, we are still talking about 2 million calls in about 2 seconds. So, about one thousandth of a millisecond per call.

In most applications we are using reflection because we have to, or because it solves a problem that would be much harder to solve with strongly typed code. The overhead of making these kinds of calls is small, and unless they are being called with extremely high frequency would likely not make a noticeable impact on your application.

I hope that you enjoyed this quick little spin around the dynamic keyword, and I'll be sure to come back shortly with more C# 4.0 goodies!

Comments

trackback

Trackback from DotNetKicks.com

C# 4.0 New Features Part 1 - dynamic keyword

DotNetKicks.com

October 28. 2008 07:27

Muhammad Mosa

When I started to read this post one question where in my mind is he going to talk about performance? Then just before you finish your post you hit my question. Thank you Justin. Really nice post

Muhammad Mosa

October 28. 2008 10:43

Egypt
NinjaCross

Thankyou for the pretty clear and nice post.
The best and most important part is (IMHO) the one related to the performance comparision, a thing that unfortunately too many bloggers leave uncovered in their coding-posts.
I hope to see more examples like this in the future Smile

NinjaCross

October 28. 2008 11:52

Italy
Erik

I think a more relevant comparison would be late-bound vs manual reflection, rather than late-bound vs early-bound. Of course the late-bound calls are going to be slower than early-bound.

That said, thank you for the writeup - it was very clear and easy to understand. =) I can't wait!

Erik

October 28. 2008 12:58

United States
Pete

Wow!
<Main>o__SiteContainer0.<>p__Site2

Very maintainable and innovative code... NOT! Keep that dynamic mumbojumbo out of my strongly typed code and create a dynamic library based on IronWhatever.

Otherwise nice article, thanks.

Pete

October 28. 2008 14:11

ozone

how is this different from var?

ozone

October 28. 2008 15:09

United States
Curt Hagenlocher

"var" represents a statically typed variable whose actual type has been inferred by the compiler.  "dynamic" represents a dynamically typed variable whose actual type is completely unknown to the compiler, so all the calls it generates against the variable use late-bound dispatch.

Curt Hagenlocher

October 28. 2008 15:18

configurator

I wonder what would happen if I do this:

dynamic test = new TestClass();
test.TestMethod1();
test = new TestClass2();
test.TestMethod1();

where both classes have TestMethod1 defined.
Then, <Main>o__SiteContainer0.<>p__Site1 would be defined after the first call, and would be calling a wrong function after setting the dynamic variable to a different value.
Of course, I haven't seen or installed the CTP so I wouldn't know the exact implications.

ozone, the difference is that var is still bound at compile time. the line
var test = new TestClass1();
is translated into
TestClass1 test = new TestClass1();
You can see that using reflector.
However, dynamic is translated to object, and changes every usage of that variable, as seen in the example given by Justin.

configurator

October 28. 2008 15:20

Israel
Paco

Where can I download the compiler and language spec?

Paco

October 28. 2008 15:54

Netherlands
Duncan Smart

So, how does it compare performance-wise to MethodInfo.Invoke?

Duncan Smart

October 28. 2008 18:35

United Kingdom
trackback

Trackback from CodeThinked

C# 4.0 New Features Part 1.1 - dynamic keyword second look

CodeThinked

October 28. 2008 19:11

Justin Etheredge

@Duncan I am writing up a post on performance versus MethodInfo.Invoke because I have found out some more interesting information.

Justin Etheredge

October 28. 2008 20:27

United States
trackback

Trackback from new 维生素C.net()

c#4.0新特性之一: Dynamic Lookup (2)

new 维生素C.net()

October 29. 2008 09:13

pingback

Pingback from blog.cwa.me.uk

Reflective Perspective - Chris Alcock  » The Morning Brew #212

blog.cwa.me.uk

October 30. 2008 01:32

trackback

Trackback from Net Art Development

Новые возможноси C# 4.0. Часть 1: dynamic

Net Art Development

November 1. 2008 05:41

trackback

Trackback from Guy     kolbis

C# 4.0 New Features

Guy kolbis

November 11. 2008 04:09

Chandra

Nice one.I have not even heard people started talking about c# 4.0.Its really impressive.

Chandra

March 4. 2009 10:49

United Kingdom
pingback

Pingback from bogdanbrinzarea.wordpress.com

Learning .NET 4.0 new features «  Bogdan Brinzarea’s blog

bogdanbrinzarea.wordpress.com

April 23. 2009 23:13

Justicle

Newsflash! - making something more indirect makes it slower.

Still, its nice to have actual numbers.

Justicle

May 25. 2009 19:29

Australia
Comments are closed