Archive for the ‘.Net 4’ category

Time To Throw Out Reflection

May 21, 2010

I was recently faced with the need to invoke all public property getters on tens of thousands of objects who’s types were unknown at compile time.

The traditional way of doing this would have been to use the System.Reflection namespace to find the PropertyInfo’s and then call invoke but since Microsoft introduced LINQ and Expression Tree’s into the .net framework there is another way to perform the invocations.

The reflection method would look something like this (for brevity I am making the assumption that all objects in the collection are of the same type and the operation I am performing on the properties value is to assume its not null, ToString it, and add it to a list):

public List<string> ExtractUsingReflectionPropertyInfo(IEnumerable<object> objects)
{
    var values = new List<string>();
    Type type = objects.GetType().GetGenericArguments()[0];
    var propertyInfos = type.GetProperties().ToList();

    var timer = new Stopwatch();
    timer.Start();

    foreach (var currentObject in objects)
    {
        foreach (var propertyInfo in propertyInfos)
        {
            object propertyValue = propertyInfo.GetValue(currentObject, null);
            values.Add(propertyValue.ToString());
        }
    }

    timer.Stop();
    Console.Write("PropertyInfo: ");
    Console.WriteLine(timer.ElapsedMilliseconds);
    return values;
}

Or we could try and help out the runtime a bit further by gathering up the MethodInfo’s for the property getters:

public List<string> ExtractUsingReflectionMethodInfo(IEnumerable<object> objects)
{
    var values = new List<string>();
    Type type = objects.GetType().GetGenericArguments()[0];
    var methodInfos = type.GetProperties().Select(pi => pi.GetGetMethod()).ToList();

    var timer = new Stopwatch();
    timer.Start();

    foreach (var currentObject in objects)
    {
        foreach (var methodInfo in methodInfos)
        {
            object propertyValue = methodInfo.Invoke(currentObject, null);
            values.Add(propertyValue.ToString());
        }
    }

    timer.Stop();
    Console.Write("MethodInfo: ");
    Console.WriteLine(timer.ElapsedMilliseconds);
    return values;
}

However instead of invoking the properties getter through the PropertyInfo another way to approach this is to build and compile an expression, at runtime, that does this for us. You can build an expression to do this as shown below:

private static Func<object, object> GetPropertyGetter(Type type, PropertyInfo property)
{
    ParameterExpression param = Expression.Parameter(typeof(Object), "param");
    Expression convertedParam = Expression.Convert(param, type);
    // the specific conversion is needed to cover off both primitive types that need to be boxed and reference types
    var lambda = Expression.Lambda(Expression.Convert(Expression.Property(convertedParam, property.Name), typeof(object)), param);
    return ((Expression<object, object>)lambda).Compile();
}

Its important to note that you need to explicitly force the boxing of primitive types if you want to treat all things as objects.

Once we’ve got our property getters we can write a different extraction method:

public List<string> ExtractUsingExpression(IEnumerable<object> objects)
{
    var values = new List<string>();
    var type = objects.GetType().GetGenericArguments()[0];
    var propertyInfos = type.GetProperties().ToList();
    var expressions = propertyInfos.Select(pi => GetPropertyGetter(type, pi)).ToList();

    var timer = new Stopwatch();
    timer.Start();

    foreach (var currentObject in objects)
    {
        foreach (var expression in expressions)
        {
            object propertyValue = expression(currentObject);
            values.Add(propertyValue.ToString());
        }
    }

    timer.Stop();
    Console.Write("Expression: ");
    Console.WriteLine(timer.ElapsedMilliseconds);
    return values;
}

Not much difference but whats really interesting is that the expression tree version is considerably quicker than the reflection version. Timing the extraction loops I get the following figures when processing 100000 objects:

Reflection with PropertyInfo = 461ms
Reflection with MethodInfo = 388ms
Compiled Expressions = 132ms
Control = 62ms

The control timings are for doing the same operations using straight compiled code against the well known type. My timings varied a little and sometimes the MethodInfo version took a little longer than the PropertyInfo version but the results were always in this ballpark with the significant result being the compiled expression version being over twice as quick as its nearest rival. In fact if you take the addition of the string to the values list out then the results look even more striking:

Reflection with PropertyInfo = 303
Reflection with MethodInfo = 273
Compiled Expressions = 19
Control = 6ms

Compiled expressions are 3 times slower than the control version and the closest reflection version is itself another 14 times slower than the compiled expression.

A final important point to note about the timings is that I took them after gathering the MethodInfo’s, PropertyInfo’s and compiling the expressions. If you include this in the timings then the compiled expression took slightly longer than the PropertyInfo version due to the  expense of compiling the expressions.

In my own solution I simply cache the compiled expressions the first time I encounter a given type thus only incurring the expense of compilation once – as my application is long running and this code called frequently called this works well for me. If you can predetermine the types you are working with in some fashion then another approach might be to arrange to compile the expressions in a non performance critical section of code or as your application starts.

While you can’t throw reflection completely out the window, you still needs its interrogative abilities, its fairly clear however that if you’ve been using reflection to invoke properties and methods on objects then switching to a compiled expression based approach could give you big performance gains.

The full code for the above can be found here.

The Ping Master

May 16, 2010

I’ve begun work on integrating exercise logging into Activate Your Glutes (more on that in a separate post) but this weekend I took a slight diversion to help out a friend who was looking for a basic network tool.

Basically he wants to run a command to restart a software VPN connection when and if it fails to connect to a specified IP address along with a GUI for configuring it and an installer for, errr, installing it.

As I’ve been waiting for a good opportunity to learn some WPF best practice (up until now I’ve skirted around that and used it much like Windows Forms) and write my first Wix installer this seemed like a win for all.

The result is The Ping Master which you can find on CodePlex.

I still have a little work to do, mainly some unit tests and beautification, but hopefully its useful already.

Activate Your Glutes and Visual Studio 2010

February 3, 2010

If you’ve downloaded the source code for the site you’ve probably noticed that it’s still using MVC 1.0, Entity Framework 1.0 and Visual Studio 2008.

I’ve been using Visual Studio 2010 and .Net 4 for a few weeks now on a commercial project and, since ReSharper 5.0 was released in beta (losing ReSharper is a painful experience), have found it to be a fairly stable and usable experience though I hope they sort out the performance issues in the IDE by the time it is fully released – I don’t recall ever waiting for my 8-bit BBC Micro with its mighty 6502 processor and 32kb of RAM to catch up with my typing.

But back on topic, I do plan on moving the site over to MVC 2.0 and Entity Framework early next month. I’m particularly keen to see how the much touted Entity Framework improvements work out as version 1.0 is frequently horrible and I keep wishing I’d used NHibernate.

A prerequisite for me doing this however is getting integration tests in place for my repository implementation for which I’m planning on using ndbunit for the first time. If anyone has any experience with this, good or bad, then I’d be interested in hearing any comments you might have and experiences you think are worth sharing.