Dynamic objects & page compilation time

Using many method calls on your dynamic objects in your ASP.NET pages can drastically slow down the compilation time.

How it begun

We needed to add i18n support to our web app. The translations were collected from different sources and they were used in our app as ViewBag.Resources.Get("Go back"). We wanted to make it shorter so decided to try to use a dynamic object so the call would look like ViewBag.R.Go_back. With a similar implementation as this one:

public class DynamicResource : DynamicObject  
    {
        public override bool TryGetMember(
            GetMemberBinder binder, out object result)
        {
            // here should be the code loading appropriate resources
            result = binder.Name.Replace('_', ' ');
            return true;
        }
    }

In the beginning everything looked good but as the app grew we noticed that the page compilation time became slower and slower. We didn't have any idea what was causing this problem. One day, the waiting exceeded my limits and I started investigating. This could be done thanks to JetBrains dotTrace profiler.

The investigation phase

I started the profiler and run through a compilation phase. Then after some time spent in All Calls it pointed me to WebPageBase.ExecutePageHierarchy method.

When I spotted the System.Dynamic.DynamicMetaObjectBinder it was clear who the culprit was. My ingeniously invented way of ducktyping resources.

How bad is it?

In short, it is really bad. I created an example including two pages, with each one calling the similar methods. In one case it is using standard call and the second using duck typing on the dynamic object. The first requests, the ones that compiles the pages, are underlined.

PathTime [ms]
/Home/With_100_normal_method_calls48
/Home/With_100_normal_method_calls17
/Home/With_100_dynamic_object_calls6692
/Home/With_100_dynamic_object_calls18

If you watched carefully you will have noticed that the profiler shows only ~1 second needed to compile the page but these results are much slower ~6x times slower. I suppose it is due to the attached debugger but that is only a guess.

Why is this happaning

Because we are using a dynamic object the compiler generates code which handles the use of our dynamic object at runtime. This generated code uses CallSite and CallSiteBinder to get the expression that represent the particular call. Then it's compiled, cached and run.

You can find more about details in this Aleksander Heintz's blog post or a shorter version of dynamic objects in action inside StormySpike's article.

Final words

As you can see from the results, the execution time is almost unaffected, but the compilation time is another story. If you have only a few tens of calls on the dynamic object it can be a nightmare to wait so long for the page to load the first time.

I prefer not to waste anyones time. We tried to avoid calling a dynamic object too often and switched to a “normal” object for our translations.

PS: If you are interested in the example you can find it here: https://github.com/pvasek/RazorCompilationOfDynamicObjects