Retrieving property value by name using dynamic method

In the previous post we compared some alternatives of the dynamic keyword. One important and very interesting alternative is based on reflection emit. Reflection emit enables us to generate code using IL at runtime, compile it and execute it straightaway.
In this post we’ll see how to extract a string property named ‘Name’ from an unknown type using a dynamic method.

The code

public static string GetNameByDynamicMethod(object arg)
{
Type type = arg.GetType();

Func<object, string> getterDelegate;
if (!typeToEmitDelegateMap.TryGetValue(type, out getterDelegate))
{
string typeName = type.Name;

PropertyInfo nameProperty = type.GetProperty("Name");
Type returnType = typeof (string);

// Define a new dynamic method
// The method returns a string type
// The method expects single parameter
var method = new DynamicMethod("GetNameFrom" + typeName,
returnType,
new[] {typeof(object)});

ILGenerator ilGenerator = method.GetILGenerator();

// Load to the stack the first method argument.
//In our case, this is an object whose type we already know
ilGenerator.Emit(OpCodes.Ldarg_0);

// Cast the object to the type we already know
ilGenerator.Emit(OpCodes.Castclass, type);

// Call the getter method on the casted instance
ilGenerator.EmitCall(OpCodes.Call, nameProperty.GetGetMethod(), null);

// Return the value from Name property
ilGenerator.Emit(OpCodes.Ret);

// Compile the method and create a delegate to the new method
getterDelegate = (Func<object, string>)method.CreateDelegate(typeof(Func<object, string>));

typeToEmitDelegateMap.Add(type, getterDelegate);
}

return getterDelegate(arg);
}

What we did here was to define a new method, generate its code with IL, compile it and execute it. This new method is equivalent in many ways to a method we had generated in the original program. This new method will be hosted in a dynamic module in the memory.

The advantage of this kind of method over reflection is that it compiles the code once and doesn’t need to explore the type again whenever we need to get the property value.

Performance

A quick comparison for calling these alternatives 10,000,000 times each:

Seconds Ratio to directly
Directly 0.0131 1
Dynamic 0.4609 35
Expression 0.9154 70
Reflection emit 0.9832 75

As can be seen, using the dynamic keyword works much faster than compiling an expression or a dynamic method at runtime.

Another interesting data set shows the time that each alternative takes to set up (The time to perform the first call):

Seconds
Directly 0.00003
Dynamic 0.08047
Expression 0.00114
Reflection emit 0.02169

Performance of the dynamic keyword

In .NET 4.0 a new keyword was introduced: the dynamic keyword. One of the things it allows is calling methods on an instance and bypassing the compile time type checks. It can be useful in many scenarios, for example duck typing.
In this post, we’ll see that in some cases the keyword might have an unnecessary performance hit. Another thing we’ll see is how to save some of that time.

Simple performance measure

Let’s compare the performance of 3 ways of getting a property value – directly, using dynamic and using reflection:

public static string GetName(Student arg)
{
return arg.Name;
}
public static string GetNameByDynamic(dynamic arg)
{
return arg.Name;
}
public static string GetNameByReflection(object arg)
{
Type type = arg.GetType();

MethodInfo getter;
if (!typeToMethodMap.TryGetValue(type, out getter))
{
PropertyInfo property = type.GetProperty("Name");
getter = property.GetGetMethod();
typeToMethodMap.Add(type, getter);
}

return (string) getter.Invoke(arg, null);
}

Calling each method 10,000,000 times sums to: GetName=0.02 seconds, GetNameByDynamic=0.47 seconds, GetNameByReflection=15.41. Meaning, dynamic compared to strong type call is ~20 times slower.

Improving performance using interface

One way to deal with this performance hit is to extract an interface from all possible objects, through using it we can get back to work with strong type:

public interface INameProvider{
string Name { get; set; }
}

And change our method to:

public static string GetNameByInterface(INameProvider arg)
{
return arg.Name;
}

Luckily this code runs at 0.07 seconds, which is ~7 times faster than the dynamic version. The conclusion from this is that if our code is in a critical performance area, we better extract an interface (as long as it makes sense – don’t abuse the interface if the types have no logical commonality!).

Improving reflection version using expressions

What should we do if our code is written in pre-.NET 4.0 version and our solution is based on reflection? In this case, our code runs ~750 times slower than the strong type version. Since we can’t use dynamic, which was introduced first at .NET 4.0, we should find some other solution. A simple one is generating a method using expressions. The main advantage of expressions here is that they can be compiled into a new method which we can reuse.

public static string GetNameByExpression(object arg)
{
Type type = arg.GetType();

Func<object, string> getterDelegate;
if (!typeToDelegateMap.TryGetValue(type, out getterDelegate))
{
var parameterExpression = Expression.Parameter(typeof (object), "arg");
var castExpression = Expression.TypeAs(parameterExpression, type);
MemberExpression memberExpression = Expression.Property(castExpression, "Name");
var lambda = Expression.Lambda<Func<object, string>>(memberExpression, parameterExpression);

getterDelegate = lambda.Compile();
typeToDelegateMap.Add(type, getterDelegate);
}

return getterDelegate(arg);
}

This code here is basically equivalent to generating a lambda which looks like:

(object arg) => ((Student)arg).Name;

After we compile the code once we can skip the reflection invocation each time and end with much faster code. Running this method times 10,000,000 takes 0.86 seconds, which is ~18 times faster than the reflection solution.

Conclusion

If you are writing code which must run as fast as possible, this is the performance summary:

Seconds Ratio to directly
Directly 0.02 1
Through interface 0.07 3.5
Using dynamic 0.47 23.5
Using expression 0.86 43
Reflection 15.41 770