DCSIMG
<-- +1 script --> January 2011 - Posts - .Net && Beyond

.Net && Beyond

Taking about .NET and much more

January 2011 - Posts

Using expression trees to optimize your code

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