Tuesday, February 10, 2015

How Slow is Repeatedly Parsing an Expression for INotifyPropertyChanged

Introduced in .NET Framework v4.5 (as a C# 5.0 compiler feature), the CallerMemberNameAttribute allows you to determine the method or property name of the caller of a method.

This comes in very handy if you’re adding logging code, or in the case of with WPF you’re using models based on the INotifyPropertyChanged interface, where you need to fire the PropertyChanged event passing along the name of the property that’s changed.

Prior to the availability of CallerMemberName, you had a couple of choices when calling the PropertyChanged event:

  1. Pass a hard-coded string to identify the property name.
    This has clear drawbacks – what if the property name changes/identifying code dependent on the property?
     
  2. Parsing an expression tree that represents a concrete class property
    Handy, because you’re referring to a class's property  


There are plenty of examples available showing how to parse an expression tree (see below), but be very careful, parsing an expression tree is very, very slow compared to using the CallerMemberName or passing in a string to identify the property. 


Results


If your application can target v4.5 then, in terms of performance, it’s strongly recommended that you replace any expression tree parsing with the CallerMemberNameAttribute.


The chart below shows how slow repeatedly parsing an Expression in order to determine the bound property, can be, when compared to CallerMemberNameAttribute or a string value:



In order to determine these numbers, I created a suite of tests, that all performed the same task of firing a property change notification change for a collection of 100 objects (each object having two properties changed 200 times). 

The statistical 95th percentile number of milliseconds for each test was calculated (rather than using a less accurate average) and plotted above.


Implementation
No. Of Objects
Properties to Change
No. of Times Each Property Changed
Time Taken Milliseconds
Expression Parsing
100
2
200
238
CallerMemberNameAttribute
100
2
200
4
Hard Coded String
100
2
200
4

 You can see that repeatedly parsing an Expression was more than 50 times slower than the other two methods. 

Clearly, if you only have a few objects to track or the number of property change is equally low then this will not be such a problem, but as the volumes increase you might notice a difference – and this is before you factor in the time it takes for your observer action to complete.  In my case I was simply incrementing a counter to keep the action work to a minimum.



Code Snippets

Before delving into the test code, I’ll summarise a few basic snippets for the three standard INotifyPropertyChanged implementations.

1. CallerMemberName

Using the CallerMemberName attribute to notify a change in the Customer’s Name (in addition you’d usually check that the old property value is different to the new incoming value before notifying the change)

public class Customer : INotifyPropertyChanged
{
    public event PropertyChangedEventHandler PropertyChanged;
 
    protected virtual void OnPropertyChanged([CallerMemberName] string propName = null)
    {
        var handler = PropertyChanged;
        if (handler != null)
        {
            handler(this, new PropertyChangedEventArgs(propName));
        }
    }
 
    private string _name;
    public string Name
    {
        get { return _name; }
        set
        {
            _name = value;
            OnPropertyChanged();
        }
    }
} 

2. Hard Coded Property Name Strings

Contrast this with the older style of passing along a string to identify the property. 
It’s simple, it works, but you have no easy way of checking if the name of bound property changes or the impact of changing that name or how it’s really used – a potential maintenance nightmare.
public class Customer : INotifyPropertyChanged
{
    public event PropertyChangedEventHandler PropertyChanged;
 
    protected virtual void OnPropertyChanged(string propName)
    {
        var handler = PropertyChanged;
        if (handler != null)
        {
            handler(this, new PropertyChangedEventArgs(propName));
        }
    }
 
    private string _name;
    public string Name
    {
        get { return _name; }
        set
        {
            _name = value;
            OnPropertyChanged("Name");
        }
    }
}

3.  Expression Parsing

You get the benefits of compile-time checking and it’s easy to determine property usage in your code…but it can be very slow.
public class Customer : INotifyPropertyChanged
{
    public event PropertyChangedEventHandler PropertyChanged;
 
    private string _name;
 
    public string Name
    {
        get { return _name; }
        set
        {
            _name = value;
            OnPropertyChanged(() => Name);
        }
    }
 
    protected void OnPropertyChanged<T>(Expression<Func<T>> expression)
    {
        var handler = PropertyChanged;
        if (handler != null)
        {
            var propName = GetPropertyNameFromExpression(expression);
            handler(this, new PropertyChangedEventArgs(propName));
        }
    }
 
   private static string GetPropertyNameFromExpression<T>(Expression<Func<T>> exp)
   {
        var memberExpression = exp.Body as MemberExpression
            ?? ((UnaryExpression)exp.Body).Operand as MemberExpression;
 
        if (memberExpression == null)
            throw new ArgumentException(
                String.Format("[{0}] is not a member expression", exp.Body));
 
        return memberExpression.Member.Name;
   }
} 

Test Cases

In order to run the tests, I prefer to use the NUnit framework to create the tests and then run them using Resharper (in a handy single click).

Using the Package Manager console, you’ll need to run Install-Package NUnit to get NUnit downloaded from NuGet and referenced in your project.

I have a general purpose base class that I often use when creating timed test code, TimedActionBase.cs. 

using System;
using System.Collections.Generic;
using System.Diagnostics;
using NUnit.Framework;
 
[TestFixture]
public abstract class TimedActionBase
{
  protected abstract void TimedAction();
…


There is an abstract void method, TimedAction(), which contains the code that I want to measure and two public methods that NUnit can see:

[Test]
public void SinglePassRunner()
{
    TimedActionRunner();
}
 
[Test]
public void MultiPassRunner()
{
    const int NoOfPasses = 180;
 
    for (var i = 0; i < NoOfPasses; i ++)
    {
        TimedActionRunner();
    }
}


TimedActionRunner is the main controlling method, responsible for timing the method under test and generating the output statistics via - CalculateStatistics()

private void TimedActionRunner()
{
    const int DefaultTestCycles = 100;
 
    // Prerun TimedAction method in case there's any JIT overhead
    TimedAction();
 
    _durations.Clear();
 
    for (var cycle = 1; cycle <= DefaultTestCycles; cycle++)
    {
        _timer.Start();
        TimedAction();
        _timer.Stop();
 
        var duration = _timer.Elapsed;
        _durations.Add(duration);
 
        _timer.Reset();
    }
 
    CalculateStatistics();
}


In each of the performance test, I’m testing a specific implementation of a Car class (CarBase)
public abstract class CarBase : INotifyPropertyChanged
{
    public event PropertyChangedEventHandler PropertyChanged;
 
    protected CarBase(int id)
    {
        Id = id;
    }
 
    public int Id { get; private set; }
    public abstract string Name { get; set; }
    public abstract int Speed { get; set; }
    public abstract int Mileage { get; set; }
 
    protected virtual void OnPropertyChanged(string propertyName)
    {
        var handler = PropertyChanged;
        if (handler != null)
        {
            handler(this, new PropertyChangedEventArgs(propertyName));
        }
    }
}


The only difference between each of the tests is how OnPropertyChanged is called (it needs a string property in all cases).

To create the actual tests I extend the TimedActionBase class further with a NotifyTestBase class.  This serves as the core for each notification test. 

public abstract class NotifyTestBase : TimedActionBase
{
    static class TestParams
    {
        public const int NumberOfCars = 100;
        public const int NumberOfLaps = 200;
        public const int MileageIncrement = 2;
        public const int SpeedIncrement = 3;
    }
 
    private readonly IEnumerable<CarBase> _cars;
 
    private int _propertyChangedCount;
    protected NotifyTestBase()
    {
        _cars = Enumerable
          .Range(1, TestParams.NumberOfCars)
          .Select(i =>
          {
              var car = CreateCar(i);
              car.PropertyChanged += (sender, args) => _propertyChangedCount ++;
              return car;
          })
          .ToList();
    }
 
    protected override void TimedAction()
    {
        for (var i = 0; i < TestParams.NumberOfLaps; i++)
        {
            foreach (var car in _cars)
            {
                car.Mileage += TestParams.MileageIncrement;
                car.Speed += TestParams.SpeedIncrement;
            }
        }
    }
 
    protected abstract CarBase CreateCar(int id);
}

You’ll see that NotifyTestBase overrides the base class’s TimedAction() method. It ensures that for each test, the same number of objects are modified and the same number of times between each CarBase implementation by overriding the factory method CarBase CreateCar().

All that’s required for each scenario is to create a concrete implementation of CarBase and have a new instance of that type returned in the test by overriding CreateCar(int i).

Taking CallerMemberName as the first example, I’ll extend a CarBase with a new class NotifyByCallerMemberNameCar:

public class NotifyByCallerMemberNameCar : CarBase
{
    public NotifyByCallerMemberNameCar(int id) : base(id)
    {}
 
    private string _name;
    public override string Name
    {
        get { return _name; }
        set
        {
            SetAndRaiseIfChanged(ref _name, value);
        }
    }
 
    private int _speed;
    public override int Speed
    {
        get { return _speed; }
        set
        {
            SetAndRaiseIfChanged(ref _speed, value);
        }
    }
 
    private int _mileage;
    public override int Mileage
    {
        get { return _mileage; }
        set
        {
            SetAndRaiseIfChanged(ref _mileage, value);
        }
    }
 
    private void SetAndRaiseIfChanged<T>(ref T backingField, T newValue, [CallerMemberName]string propName = null)
    {
        if (EqualityComparer<T>.Default.Equals(backingField, newValue))
        {
            return;
        }
        backingField = newValue;
        OnPropertyChanged(propName);
    }
}


You can see that each of the property sets calls SetAndRaiseIfChanged – no need to pass in the property name as the propName string is decorated with the [CallerMemberName] attribute.

In order to test this I need to create test class that extends NotifyTestBase, with a method that overrides CreateCar to return the concrete NotifyByCallerMemberNameCar class:

public class NotifyByCallerMemberNameTest : NotifyTestBase
{
    protected override CarBase CreateCar(int i)
    {
        return new NotifyByCallerMemberNameCar(i);
    }
} 

The NotifyByExpressionTest looks similar, we return a NotifyByExpressionCar instance, with each property set making use of an Expression:

public class NotifyByExpressionTest : NotifyTestBase
{
    protected override CarBase CreateCar(int i)
    {
        return new NotifyByExpressionCar(i);
    }
}
 
public class NotifyByExpressionCar : CarBase
{
    public NotifyByExpressionCar(int id) : base(id)
    {}
 
    private string _name;
    public override string Name
    {
        get { return _name; }
        set
        {
            SetAndRaiseIfChanged(ref _name, value, () => Name);
        }
    }
 
    private int _speed;
    public override int Speed
    {
        get { return _speed; }
        set
        {
            SetAndRaiseIfChanged(ref _speed, value, () => Speed);
        }
    }
 
    private int _mileage;
    public override int Mileage
    {
        get { return _mileage; }
        set
        {
            SetAndRaiseIfChanged(ref _mileage, value, () => Mileage);
        }
    }
 
    protected void SetAndRaiseIfChanged<T>(ref T backingField, T newValue, 
                                           Expression<Func<T>> expression)
    {
        if (EqualityComparer<T>.Default.Equals(backingField, newValue))
        {
            return;
        }
 
        var propName = GetPropertyNameFromExpression(expression);
        backingField = newValue;
        OnPropertyChanged(propName);
    }
 
    private static string GetPropertyNameFromExpression<T>(
       Expression<Func<T>> expression)
    {
        var memberExpression = expression.Body as MemberExpression 
            ?? ((UnaryExpression)expression.Body).Operand as MemberExpression;
 
        if (memberExpression == null)
            throw new ArgumentException(String.Format("[{0}] is not a member expression", expression.Body));
 
        return memberExpression.Member.Name;
    }
        
}

and finally NotifyByStringTest - for the hard-coded string property

public class NotifyByStringTest : NotifyTestBase
{
    protected override CarBase CreateCar(int i)
    {
        return new NotifyByStringCar(i);
    }
}
 
public class NotifyByStringCar : CarBase
{
    public NotifyByStringCar(int id) : base(id)
    {}
 
    private string _name;
    public override string Name
    {
        get { return _name; }
        set
        {
            SetAndRaiseIfChanged(ref _name, value, "Name");
        }
    }
 
    private int _speed;
    public override int Speed
    {
        get { return _speed; }
        set
        {
            SetAndRaiseIfChanged(ref _speed, value, "Speed");
        }
    }
 
    private int _mileage;
    public override int Mileage
    {
        get { return _mileage; }
        set
        {
            SetAndRaiseIfChanged(ref _mileage, value, "Mileage");
        }
    }
 
    protected void SetAndRaiseIfChanged<T>(ref T backingField, T newValue, string propName)
    {
        if (EqualityComparer<T>.Default.Equals(backingField, newValue))
        {
            return;
        }
        backingField = newValue;
        OnPropertyChanged(propName);
    }
        
}



You can download the full test solution from here.


Further Information


I was interested in finding out how the use of CallerMemberNameAttribute affects my assemblies.  It turns out CallerMemberNameAttribute is part of the System.Runtime.CompilerServices namespace:

The System.Runtime.CompilerServices namespace provides functionality for compiler writers who use managed code to specify attributes in metadata that affect the run-time behavior of the common language runtime.  This namespace is primarily for compiler writers

Normally you use Reflection to get hold of types decorated with an attribute at runtime, but the C# 5.0 compiler looks for this attribute when compiling your code.

I used ILDASM, Microsoft’s MSIL dissembler, to examine assembly that was created.  It’s interesting to note that the property sets created using CallerMemberName AND a string constant are identical:


No comments:

Post a Comment