Using expression trees to optimize your code

January 15, 2011

4 comments

Hi,

A few days ago I wanted to use attributes to make my code easier. The problem was that It cost me in performance and I couldn’t afford it at this point of the code, so I decided to use expression to optimize my code.

Lets say we want to make a [DefaultValue] attribute that will set a default value for a property

   1: public class Person

   2:     {

   3:         [DefaultValue("Alon")]

   4:         public string Name { get; set; }

   5:         [DefaultValue("Nativ")]

   6:         public string LastName { get; set; }

   7:         [DefaultValue("http://blogs.microsoft.co.il/blogs/alon_nativ/")]

   8:         public string Blog { get; set; }

   9:  

  10:         public override string ToString()

  11:         {

  12:             return string.Format("Name: {0}, LastName: {1}, Blog: {2}", Name, LastName, Blog);

  13:         }

  14:     }

The Attribute definition:

   1: [AttributeUsage(AttributeTargets.Property,AllowMultiple = false,Inherited = true)]

   2:     class DefaultValue : Attribute

   3:     {

   4:         public DefaultValue(string key)

   5:         {

   6:             Value = key;

   7:         }

   8:  

   9:         public string Value { get; set; }

  10:     }

Now we will use it like that:

Option 1: The simple way to use the attributes is by using reflection:

   1: var person = new Person();

   2: IEnumerable<PropertyInfo> props = typeof(Person).GetProperties().Where(p => p.GetCustomAttributes(true).Where(a => a is DefaultValue).Any());

   3: foreach (var propertyInfo in props)

   4: {

   5:     DefaultValue defaultValue = propertyInfo.GetCustomAttributes(typeof(DefaultValue), true)[0] as DefaultValue;

   6:     propertyInfo.SetValue(person, defaultValue.Value, null);

   7:  

   8: }

   9: Console.WriteLine(person);

The code above will work but it is not optimized, of course we can cache the PropertyInfo for each object type to improve performance but it is not good enough.

So I decided to use expression trees to optimize the above code.

Option 2: The idea is to create a static object per type. We can do it by using generic static class this will make sure that each type will compile only once.

   1: using System;

   2: using System.Collections.Generic;

   3: using System.Linq;

   4: using System.Linq.Expressions;

   5: using System.Reflection;

   6:  

   7: namespace Alon.Samples

   8: {

   9:     /// <summary>

  10:     /// Init object with default values

  11:     /// </summary>

  12:     /// <typeparam name="T">The object that will be init</typeparam>

  13:     public static class Initializer<T> where T : new()

  14:     {

  15:         private static Action<T> _initFunction;

  16:  

  17:         static Initializer()

  18:         {

  19:             //The method that extract the value accurding to the attribute key

  20:             MethodInfo method = typeof(Initializer<T>).GetMethod("GetValue", BindingFlags.Static | BindingFlags.NonPublic);

  21:             var param = Expression.Parameter(typeof(T), "TObject");

  22:             //The expressions that we want to run

  23:             List<Expression> exps = new List<Expression>();

  24:             //Get all the properties that has DefaultValue attribute

  25:             var props = typeof(T).GetProperties().Where(p => p.GetCustomAttributes(true).Where(a => a is DefaultValue).Any());

  26:             foreach (PropertyInfo propertyInfo in props)

  27:             {

  28:                 MemberExpression prop = Expression.Property(param, propertyInfo);

  29:                 DefaultValue defaultAttribute = propertyInfo.GetCustomAttributes(typeof(DefaultValue), true)[0] as DefaultValue;

  30:                 ConstantExpression ce = Expression.Constant(defaultAttribute.Value);

  31:                 MethodCallExpression call = Expression.Call(method, ce);

  32:                 BinaryExpression assign = Expression.Assign(prop, call);

  33:                 exps.Add(assign);

  34:             }

  35:             //If we dont have any attributes we will create an empty block (otherwise we will get an exception)

  36:             BlockExpression blockExpression = exps.Count > 0 ? Expression.Block(exps) : Expression.Block(Expression.Empty());

  37:             Expression<Action<T>> lamExp = Expression.Lambda<Action<T>>(blockExpression, param);

  38:             //Compile the method :) 

  39:             _initFunction = lamExp.Compile();

  40:         }

  41:  

  42:         private static string GetValue(string key)

  43:         {

  44:             return key;

  45:         }

  46:  

  47:         public static void Init(T obj)

  48:         {

  49:             _initFunction(obj);

  50:         }

  51:     }

  52: }

Now to use it all we need to do is something is:

   1: var p = new Person();

   2: Initializer<Person>.Init(p);

   3: Console.WriteLine(p);

After I did that I wanted to make sure that I improved the performance so I tested the reflection why vs the expression way.

I run each solution 10000 times (after caching the PropertyInfo) and the results are (on my laptop)

Reflection: 2900 Milliseconds, Expression trees 30 Milliseconds – about 100 times faster

Here is the test code:

   1: static void Main(string[] args)

   2: {

   3:     List<PropertyInfo> props = typeof(Person).GetProperties().Where(p => p.GetCustomAttributes(true).Where(a => a is DefaultValue).Any()).ToList();

   4:  

   5:     var obj = new Person();

   6:     SetValues(obj, props);

   7:     Stopwatch sw1 = Stopwatch.StartNew();

   8:     for (int i = 0; i < 100000; i++)

   9:     {

  10:         obj = new Person();

  11:         SetValues(obj, props);

  12:     }

  13:     sw1.Stop();

  14:     Console.WriteLine(sw1.ElapsedMilliseconds);

  15:  

  16:     sw1 = Stopwatch.StartNew();

  17:     for (int i = 0; i < 100000; i++)

  18:     {

  19:         obj = new Person();

  20:         Initializer<Person>.Init(obj);

  21:     }

  22:     sw1.Stop();

  23:     Console.WriteLine(sw1.ElapsedMilliseconds);

  24:  

  25: }

  26:  

  27: public static void SetValues(Person myObj, IEnumerable<PropertyInfo> props)

  28: {

  29:     foreach (var propertyInfo in props)

  30:     {

  31:         DefaultValue tran = propertyInfo.GetCustomAttributes(typeof(DefaultValue), true)[0] as DefaultValue;

  32:         propertyInfo.SetValue(myObj, tran.Value, null);

  33:     }

  34: }

I think that there are many cases that we want to use attributes to make our code easier to read and write and by optimizing it with expression trees we can enjoy a clean code and not paying for it in performance.

BTW: After I did that I created a custom serializer on the same idea (just need to change the Initializer that will return values instead of set the object properties

Keep Writing, Compiling, and Debugging

Alon Nativ

Add comment
facebook linkedin twitter email

Leave a Reply

Your email address will not be published.

*

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>

4 comments

  1. Moshe KaplanJanuary 20, 2011 ב 10:47 AM

    Hi Alon,

    Have made a test with a very simple code solution w/o the fancy reflection and attributes?

    Keep Performing,
    Moshe Kaplan

    Reply
  2. ChananMarch 10, 2011 ב 5:43 PM

    Hi Alon,

    I used your example to create an object indexer in a similar style.

    Though, I have encountered one problem.

    My question is, how do you deal with inheritance when using the Initializer?

    Say you have:

    public class A
    {
    [DefaultValue("1")]
    string X { get; set;}
    }

    public class B : A
    {
    [DefaultValue("2")]
    string Y { get; set;}
    }

    If you want to initialize an object (or, in my case, implement the index) from one place in the code (e.g, FactoryMethod) – how would you do that without messing up your code with different calls for each type: Initilizer , Initilizer, etc.(if there are more types which derive A)?

    Thanks, :)

    Chanan

    Reply
  3. Alon NativMarch 11, 2011 ב 1:30 PM

    Hi Chanan,
    Good question
    there are many options to solve this issue
    for example:
    1. Visitor pattern – I think that it is the best solution for this problem.
    2. Or you can implement an interface void InitObject()
    that each one will call the Initializer on this
    for example for class A
    void InitObject() {
    Initializer.Init(this);
    }

    Hope this that this will help you with your problem.

    Thanks,
    Alon Nativ

    Reply