A few days ago I posted an article about the new .NET Expression features in .NET 4.0 (Fun with .NET 4 expressions). A few people commented and wrote the this was a pretty expensive way to synchronize data. Of course it was – performance wasn’t a design consideration. I merely followed the two rules of optimization:
- Don’t do it.
- (For experts only!) Don’t do it now.
And since the synchronization process was fast enough, I didn’t care.
However, just for the fun of it, I did some performance optimization and it is pretty amazing. The thing which made the original code rather slow was the fact that the same expressions were compiled over and over again. So an obvious improvement was to cache the compiled expressions and reuse them. So I introduced two dictionaries to hold the compiled expressions:
private static readonly Dictionary<Type, object> _SetterCache = new Dictionary<Type, object>();
private static readonly Dictionary<Type, object> _GetterCache = new Dictionary<Type, object>();
You may wonder why the type of the dictionary is Dictionary<Type, object>. The reason for this is that there is no other common base class for the type Func<TSource, TProperty>. The following this method checks the cache and compiles the expression if required:
private static Func<TTarget, TProperty> GetGetterMethod<TTarget, TProperty>(Expression<Func<TTarget, TProperty>> targetSelector)
{
object getterObject;
Func<TTarget, TProperty> getterMethod;
var selectorType = targetSelector.Type;
if (!_GetterCache.TryGetValue(selectorType, out getterObject))
{
getterMethod = targetSelector.Compile();
_GetterCache[selectorType] = getterMethod;
}
else
{
getterMethod = (Func<TTarget, TProperty>) getterObject;
}
return getterMethod;
}
I added a similar method to handle the update expression accordingly.
Finally, I hacked together a small test program which used the Stopwatch class to measure the synchronization speed. The result is quite impressing. Synchronizing an object with two properties 10,000 times yielded these results:
Elapsed for 10000 rounds (Slow): 00:00:08.0471936
Elapsed for 10000 rounds (Fast): 00:00:00.2009293
That is quite an improvement.
I’ve attached the source code to this post, so you can measure yourself.
Downloads
18846f28-3912-4484-ac13-11c99ae182b8|0|.0
The other day I was tasked to write some code to synchronize data between two data stores. The entities were similar but not exactly the same. And I didn’t just want to synchronize all entries every time, but only if the source entity was different than the target entity. To simplify the comparison of each property, I wrote a small function which compared the values of one property of the source entity with the matching property on the target entity and updated it if it was different.
Let’s assume we have a simple entity class like this one:
class Contact
{
public string Name { get; set; }
public int Age { get; set; }
}
The method I write allows me to write this code to update a target entry with just a few lines of code:
var source = new Contact {Name = "Alice", Age = 42};
var target = new Contact();
if (UpdatePropertyIfChanged(source, target, contact => contact.Name, contact => contact.Name) |
UpdatePropertyIfChanged(source, target, contact => contact.Age, contact => contact.Age))
{
Console.Out.WriteLine("Contact updated.");
Console.Out.WriteLine("target.Name = {0}", target.Name);
Console.Out.WriteLine("target.Age = {0}", target.Age);
}
The important method is the UpdatePropertyIfChanged method. It takes two entities, the source and the target instance. The third parameter is a lambda method which selects the property on the source instance and the last parameter selects the destination property on the target instance. To ensure that every property is checked, I used a logical or instead of a conditional or to ensure that every property is checked. If I had used the conditional or operator instead and the first property was different on both objects, the second property would not have been checked and updated.
Here is the body of the UpdatePropertyIfChanged method:
private static bool UpdatePropertyIfChanged<TSource, TProperty, TTarget>(TSource source, TTarget target, Func<TSource, TProperty> sourceSelector, Expression<Func<TTarget, TProperty>> targetSelector)
{
var valueA = sourceSelector(source);
var valueB = targetSelector.Compile()(target);
if (EqualityComparer<TProperty>.Default.Equals(valueA, valueB)) return false;
var setterMethod = CreateSetter(targetSelector);
setterMethod(target, valueA);
return true;
}
It’s pretty straigtforward, except for one details: Instead of a Func<TSource, TProperty> like the sourceSelector, the targetSelector is of type Expression<Func<TTarget, TProperty>>. The main difference is that an expression of type Func is already compiled and ready to be executed. In contrast, an expression of type Expression<Func> can be examined and manipulated at runtime.
The method executes these steps:
- Read the value of the property from the source entity by execute the sourceSelector expression
- Read the value of the property from the target entity by compiling and executing the targetSelector expression
- Compare the two values using the EqualityComparer<T>.Default comparer.
- If the two values are equal, just return false and abort the method.
- Convert the expression from the targetSelector expression to a setter expression
- Call the newly created expression with the value from the sourceSelector expression.
The ability to compile your own lambda expression has been introduced with .NET 3.5. But they were limited to reading properties and field. This feature has been improved with .NET 4.
Here is the body of the CreateSetter method:
private static Action<TInstance, TProperty> CreateSetter<TInstance, TProperty>(Expression<Func<TInstance, TProperty>> getterMethod)
{
var memberExpression = getterMethod.Body as MemberExpression;
if (memberExpression == null) throw new InvalidOperationException("GetterMethod must be a memberexpression");
var instance = Expression.Parameter(typeof(TInstance));
var parameter = Expression.Parameter(typeof(TProperty));
var expression =
Expression.Lambda<Action<TInstance, TProperty>>(
Expression.Assign(
Expression.MakeMemberAccess(instance, memberExpression.Member), parameter), new[] { instance, parameter });
return expression.Compile();
}
This method expects an expression which maps an instance to a property of that instance. Based on this expression, a new expression is created, which sets that property to a specified value. The method returns this new expression in the form of an Action<TInstance, TProperty>.
7d7d05a4-eb1c-4a33-b15b-a39dcc1f8019|2|5.0