One of the tasty new features of the new “Mango” update of the Windows Phone developer tools is the presence of System.Reflection.Emit. This allows us, developers, to do some nice tricks to boost the overall app performance in corner cases. As some of you may be familiar with our RadChart for Windows Phone API, there are two types of DataPoint bindings, allowing you to MVVM-bind the chart: 

  • PropertyNameDataPointBinding
  • GenericDataPointBinding<T, TResult> 

As the name hints, the first type of binding uses the name of a property in order to retrieve the corresponding value, while the second one uses a generic Func<T, TResult> delegate to get the value for an object instance. Obviously the first binding may be specified through XAML and that is its purpose. One may wonder why we are not using the default Binding object that comes with the SL framework. Well, this is due to some limitations in the framework’s class hierarchy - the smallest unit that may establish one or more bindings is FrameworkElement, which on its hand is UIElement. So, our simple and light-weight DataPoint type should derive from FrameworkElement in order to get the binding system work properly. We actually tried this approach and it was an enormous overhead, especially when working with large amount of data. That is why we have our own binding implementation, which of course has some limitations compared to the framework one but still, it does its job pretty well and so far we have not faced a feature that we were unable to implement. 

Ok, back to our PropertyNameDataPointBinding. Having only a property name hints that internally we use reflection or dynamic method emit to get the value of that property for each bound object. Prior to Mango emitting was not available, so we were using reflection. As explained in this excellent article, reflection should be avoided by all possible means. Nevertheless we decided to use it because this was the only possible way to have declarative creation of bindings.

Luckily reflection is no more a performance pitfall and we have replaced all PropertyInfo.GetValue calls with a dynamically created delegate:

/// <summary>
/// Retrieves the value for the specified object instance.
/// </summary>
/// <param name="instance"></param>
/// <returns></returns>
public override object GetValue(object instance)
{
    if (instance == null)
    {
        throw new ArgumentNullException("instance");
    }
 
    if (string.IsNullOrEmpty(this.propertyName))
    {
        throw new ArgumentException("PropertyName not specified.");
    }
             
    if (this.getter == null)
    {
        this.getter = DynamicHelper.CreatePropertyValueGetter(instance.GetType(), this.propertyName);
    }
 
    return this.getter(instance);
}

And here is how the actual emitting is performed:

/// <summary>
/// Generates a <see cref="GetPropertyValueDelegate"/> instance to allow for retrieving property values for instances of the specified type without using reflection.
/// </summary>
/// <param name="type"></param>
/// <param name="propertyName"></param>
/// <returns></returns>
public static GetPropertyValueDelegate CreatePropertyValueGetter(Type type, string propertyName)
{
    PropertyInfo propInfo = type.GetProperty(propertyName);
    if (propInfo == null || !propInfo.CanRead)
    {
        throw new ArgumentException("Could not find property '" + propertyName + "' for type '" + type.FullName + "'");
    }
 
    if (propInfo.DeclaringType.IsValueType)
    {
        throw new ArgumentException("Dynamic getter is not supported for value types.");
    }
 
    DynamicMethod dm = new DynamicMethod("Telerik_DynamicGetter_" + propInfo.Name, typeof(object), new Type[] { typeof(object) });
    ILGenerator iL = dm.GetILGenerator();
 
    // load the first argument (object instance)
    iL.Emit(OpCodes.Ldarg_0);
 
    // cast the object instance to the declaring type
    iL.Emit(OpCodes.Castclass, propInfo.DeclaringType);
 
    // call the GetMethod of the property info
    iL.Emit(OpCodes.Callvirt, propInfo.GetGetMethod());
 
    if (propInfo.PropertyType.IsValueType)
    {
        // box it from the known value type
        iL.Emit(OpCodes.Box, propInfo.PropertyType);
    }
 
    // return
    iL.Emit(OpCodes.Ret);
 
    return dm.CreateDelegate(typeof(GetPropertyValueDelegate)) as GetPropertyValueDelegate;
}

Needless to say this approach is much faster. But how much exactly? Well, we did some measurements and it turned out that using dynamically generated delegates is about 50 times faster. The good news is that the same optimization is made for all the property name based data descriptors in our RadJumpList control thus improving the overall time of data processing many times.

These exciting new optimizations will be available for our Mango Update in a Service Pack release we are planning somewhere around 21st of September (that is by the middle of the next week) so stay tuned  :)


About the Author

Georgi Atanasov


Related Posts

Comments