Instanciating a Type at Runtime – C# Benchmarks

Assuming you are familiar with C#;
If I give you a Type and tell you to create an object with it, you would automatically think of Activator.CreateInstance right?
What if I tell you that instanciating a Type using Expression Trees is much faster?

The code for the benchmarks is in this repository.


I am currently in an internship and the project I’m working on needs to be as extensible as possible.
Meaning that users/developers should have the possiblity to add new functionalities (or at least a variant of a functionality) easily.

For instance, I have a Manager class and a couple of default classes that inherit implement, each of them does something specific.
Now the idea is to make it possible for people to plug-in their SomethingManager class.

I ended up with a list of Type that inherit from Manager. Now all I have to do is instanciate and execute these types when I need them. Like most C# programmers, I immediately thought of Activator.CreateInstance!

However, there must be a faster way for instanciating a type right?

Instaciating a type - some of the ways

Let’s imagine that our base class is TestClass.


public TestClass Activator()
    return (TestClass)System.Activator.CreateInstance(_type);

ConstructorInfo (Reflection)

// This line should only be ran once
var _constructor = _type.GetConstructor(Type.EmptyTypes);

public TestClass Constructor()
    return (TestClass)_constructor.Invoke(null);

Expression Trees

// This line should only be ran once
var _delegate = Expression.Lambda(Expression.New(_type)).Compile();

public TestClass Delegate()
    return (TestClass)_delegate.DynamicInvoke();

Func<object> (Expression Trees)

// This line should only be ran once
var _func = Expression.Lambda<Func<object>>(Expression.New(_type)).Compile();

public TestClass Func()
    return (TestClass)_func();

Func<TestClass> (Expression Trees)

// This line should only be ran once
var _typedFunc = Expression.Lambda<Func<TestClass>>(Expression.New(_type)).Compile();

public TestClass TypedFunc()
    return _typedFunc();


Benchmark results

Benchmark results

I added the new TestClass() benchmark as a baseline.
As you can see, Expression Trees compiled Func<object> or Func<TestClass> are so close to being as fast as the baseline.

On the other hand, Expression Trees compiled to a plain Delegate and called using DynamicInvoke() are extremely slow.
This is due to the fact that a Delegate is dynamically invoked, .net has to use Reflection to figure out the Type and other informations, and this happens everytime.

For more informations, check this Stackoverflow question.

Raw results

Method Job Mean Error StdDev Ratio RatioSD Rank
New Clr 3.722 ns 0.0581 ns 0.0544 ns 1.00 0.00 2
Activator Clr 48.439 ns 0.1852 ns 0.1733 ns 13.02 0.20 9
Constructor Clr 137.333 ns 0.3617 ns 0.3383 ns 36.91 0.54 11
Delegate Clr 727.431 ns 1.6431 ns 1.2829 ns 195.50 3.15 17
Func Clr 11.439 ns 0.0742 ns 0.0658 ns 3.07 0.04 7
TypedFunc Clr 10.749 ns 0.0752 ns 0.0703 ns 2.89 0.04 6
New Core 3.973 ns 0.0417 ns 0.0370 ns 1.07 0.02 3
Activator Core 43.673 ns 0.1425 ns 0.1333 ns 11.74 0.16 8
Constructor Core 96.602 ns 0.3215 ns 0.3007 ns 25.96 0.39 10
Delegate Core 524.034 ns 1.1478 ns 1.0736 ns 140.84 2.10 16
Func Core 6.282 ns 0.2092 ns 0.3195 ns 1.72 0.09 5
TypedFunc Core 4.538 ns 0.0891 ns 0.0833 ns 1.22 0.02 4
New CoreRT 3.187 ns 0.0759 ns 0.0710 ns 0.86 0.02 1
Activator CoreRT 328.310 ns 1.3142 ns 1.2293 ns 88.23 1.24 15
Constructor CoreRT 142.282 ns 0.7171 ns 0.6708 ns 38.24 0.55 12
Delegate CoreRT 298.431 ns 2.0301 ns 1.8990 ns 80.21 1.36 14
Func CoreRT 220.747 ns 0.5149 ns 0.4816 ns 59.33 0.84 13
TypedFunc CoreRT 219.806 ns 1.5278 ns 1.4291 ns 59.08 1.02 13

To sum up, instanciating a type at runtime can be done in multiple ways.
The fasted way (in these benchmarks) is using Expression Trees to generate a typed Func<X> and invoke it when needed.

For the future, I should also benchmark running IL Code directly.

Zanid Haytam Written by:

Zanid Haytam is an enthusiastic programmer that enjoys coding, reading code, hunting bugs and writing blog posts.

comments powered by Disqus