I will be present as a speaker at PRIO.Conference 2009 in Munich, Germany, 28th of October 2009. My session is at 17h30. It will be the first presentation of PostSharp 2.0 ever and I will show some concrete aspects useful in GUI applications.

I will be in Munich from the 27th evening to 29th afternoon, so feel free to take a date if you want to meet me there.

Many thanks to Ralf Westphal for inviting me!

See you there!

-gael

 

In my previous post, I have given a quick overview of some new features of the aspect weaver.

Equally important to features: runtime performance.

PostSharp 1.5 already did a great job compared to other aspect frameworks. However, this could still be greatly improved. And this had been done in PostSharp 2.0.

Take, for instance, a rather simple aspect: a performance counter. Say we want to increase a counter every time a method is executed. The easiest and most efficient way with PostSharp 1.5 is to create an aspect of type OnMethodBoundaryAspect and to implement the OnEntry:

[Serializable]
public sealed class MethodInvocationCounterAttribute : OnMethodBoundaryAspect
{
    [NonSerialized]
    private PerformanceCounter performanceCounter;

    public MethodInvocationCounterAttribute(string category, string name)
    {
        this.Category = category;
        this.Name = name;
    }

    public string Category { get; private set; }
    public string Name { get; private set; }

    public override void RuntimeInitialize(System.Reflection.MethodBase method)
    {
        this.performanceCounter = new PerformanceCounter(this.Category, 
                                                         this.Name, false);
    }

    public override void OnEntry(MethodExecutionEventArgs eventArgs)
    {
        this.performanceCounter.Increment();
    }

}

Now let's apply this aspect on an empty method (and forbid inlining), and put it on the test bench (here on my AMD Phenom II X4 940 3.00 GHz):

Implementation CPU Time (ns) Overhead (ns)
Manual implementation 26 0
PostSharp 1.5 108 82

As you can see, in PostSharp 1.5, the overhead of the using an aspect, compared to hand coding, is 108 - 26 = 82 ns (remember, a nanosecond is a billionth of a second). What's the problem? The aspect overhead may is more than 3 times the aspect effect itself! If the aspect has to be invoked thousands of times per second, its cost can surely not be ignored.

Now look at the benchmark with PostSharp 2.0:

Implementation CPU Time (ns) Overhead (ns)
Manual implementation 26 0
PostSharp 2.0 29 3

This time, it's much better. For this specific aspect, PostSharp 2.0 is more than 25 times faster at runtime than PostSharp 1.5! Most importantly, the overhead of the aspect is now only a small fraction of the cost of the effect itself. So it now makes perfectly sense to use an aspect for lightweight instrumentation.

How does PostSharp 2.0 achieves this performance gain? In short, by being smarter about the code it generates. PostSharp 1.5 did not look into the code of your aspects, it had to generate code for any offered feature, even if your aspect did not use them. Therefore, PostSharp 1.5 generated a lot of useless instructions. This is clear if we looked at the output assembly using Reflector:

Code generated by PostSharp 1.5

As you can see, there are instructions to pass the parameters of the method to the aspect, even if the aspect never uses them. Handlers OnSuccess, OnException and OnExit are invoked, even if the aspect does not implement them. That's bad overhead: it does not translate into anything useful.

PostSharp 2.0 is way smarter: it analyzes the code of your aspect, figures out which feature you actually use, and generate instructions only for them. Watch the difference:

Code generated by PostSharp 2.0

No wonder it's faster. Since the aspect does not even use the eventArgs object, why should we pass it? PostSharp 2.0 is smarter than you could think: it also looks at which member of MethodExecutionEventArgs you are using. So if you read the Instance property and not the Arguments property, you will get Instance, not Arguments.

Sure, there is a catch in this benchmark: I have intentionally chosen an example where the improvement is dramatic. But, from today, you know that you can achieve amazing performance with OnMethodBoundaryAspect, and that it's up to you to design an aspect that is really lightweight at runtime. The learning curve of PostSharp has always been pay-as-you-consume. Now runtime performance is also pay-as-you-consume.

What with other aspects? Take OnMethodInvocationAspect. It has been reimplemented from scratch, is now named MethodInterceptionAspect, and is "just" 77% faster in PostSharp 2.0 than in PostSharp 1.5.

PostSharp 2.0 delivers better runtime performance by playing on 3 factors:

  • Adaptive Code Generation, as demonstrated above (major benefits in OnMethodBoundaryAspect, minor benefits elsewhere).
  • Use of generic tuples instead of untyped arrays to store arguments (no boxing-unboxing, no casting).
  • Use of binding classes instead of delegates.
  • Aggressively optimized design (we do consider a virtual call or a boxing/unboxing cycle as an expensive operation).
  • Cross-aspect optimizations, delivering great benefits when many aspects are applied to the same method (artifacts used by an aspect can be reused by next aspects in chain).

Now a last piece of code. What if a 3 ns overhead is still too much for your case? Look at the following piece of code, it has zero overhead.

[Serializable]
public sealed class MethodInvocationCounter3Attribute : MethodLevelAspect
{
    [NonSerialized]
    private readonly static PerformanceCounter performanceCounter;

    static MethodInvocationCounter3Attribute()
    {
        if ( !PostSharpEnvironment.IsPostSharpRunning )
        {
            performanceCounter = new PerformanceCounter("Custom", "NumberOfItems64", false);
        }
    }

    [OnMethodEntryHandler, SelfSelector]
    public static void OnEntry(MethodExecutionArgs eventArgs)
    {
        performanceCounter.Increment();
    }
}

The OnEntry handler is a static method. So, when applied on a target method, it gives the following:

Code generated by PostSharp 2.0 - Static Method

Since the OnEntry handler is inlined by the JIT compiler, this method is strictly equivalent to invoking performanceCounter.Increment manually from the instrumented method. Sure, your possibilities are very limited when you use a static method (you can't access instance fields of the aspect instance, so the counter name has to be the same for all methods using this aspect). But the promise holds: you use nothing, you pay nothing. Here, the aspect is free of any overhead.

Happy PostSharping!

-gael

PS. Kicking ass?

Be seated safely before reading on. This kicks ass.

I have already written enough abstract words in previous posts; let's now introduce PostSharp 2.0 on a real example: the implementation of  NotifyPropertyChanged, one of the most frequently used patterns for all adepts of MVC designs.

It's not just about implementing the INotifyPropertyChanged interface; it's also about modifying every property setter. It's deadly simple and deadly boring. And gets you more than one step away from the idea you have of aesthetical code.

Enough words. See the implementation using PostSharp 2.0. There's a lot of new concepts out there, so let's start easily:

[Serializable]
[IntroduceInterface( typeof(INotifyPropertyChanged))]
public sealed class NotifyPropertyChangedAttribute : 
   InstanceLevelAspect, INotifyPropertyChanged
{
    public void OnPropertyChanged( string propertyName )
    {
        if ( this.PropertyChanged != null )
        {
            this.PropertyChanged( this.Instance, 
               new PropertyChangedEventArgs( propertyName ) );
        }
    }

    [IntroduceMember]
    public event PropertyChangedEventHandler PropertyChanged;

    [OnLocationSetHandler, 
     MulticastSelector( Targets = MulticastTargets.Property )]
    public void OnPropertySet( LocationInterceptionArgs args )
    {
        if ( args.Value == args.GetCurrentValue() ) return;

        this.OnPropertyChanged( args.Location.PropertyInfo.Name );
    }
}

The aspect is used this way:

[NotifyPropertyChanged]
public class TestBaseClass
{
    public string PropertyA { get; set; }
}

Some words of explanation.

First, look at introductions: IntroduceInterface tells that the interface INotifyPropertyChanged must be introduced into the target type. And where is the interface implemented? Well, by the aspect itself! Note that PropertyChanged is itself introduced to the target type as a public member thanks to the IntroduceMember custom attribute (without this attribute, the interface would be implemented explicitely).

The aspect actually becomes an extension of the class; aspect instances have the same lifetime as objects of the class to which the aspect is applied... just because the aspect inherits from InstanceLevelAspect.

Got it? So let's continue to the OnPropertySet method. It is marked by custom attribute OnLocationSetHandler: it makes this method a handler that intercepts all calls to the setter of all target properties. And which are target properties? This is told by custom attribute MulticastSelector: all properties in this case, but we could filter on name, visibility and all features you are used to. We could have used the same handler to handle access to properties (it would have turned the field to a property as a side effect).

Did you get it? You can now catch accesses to properties or fields using the same aspects. Same semantics, same aspect. Simple, powerful.

Now look at the body of method OnPropertySet and see how easy it is to read the current value of the property.

The code above works on an isolated class, but a class is rarely isolated, right? More of the time, it derives from an ancestor and have descendants. What if the ancestor already implements interface INotifyPropertyChanged? The code above would trigger a build time error. But we can improve it. What do we want, by the way? Well, if the class above already implements INotifyPropertyChanged, it must also have implemented a protected method OnPropertyChanged(string), and we have to invoke this method. If not, we define this method ourselves. In both cases, we need invoke this method from all property setters.

Let's turn it into code:

[Serializable]
[IntroduceInterface( typeof(INotifyPropertyChanged), 
                     OverrideAction = InterfaceOverrideAction.Ignore )]
[MulticastAttributeUsage( MulticastTargets.Class, 
                          Inheritance = MulticastInheritance.Strict )]
public sealed class NotifyPropertyChangedAttribute : 
   InstanceLevelAspect, INotifyPropertyChanged
{
   
    [ImportMember( "OnPropertyChanged", IsRequired = false )] 
    public Action<string> BaseOnPropertyChanged;

    [IntroduceMember( Visibility = Visibility.Family, 
                      IsVirtual = true, 
                      OverrideAction = MemberOverrideAction.Ignore )]
    public void OnPropertyChanged( string propertyName )
    {
        if ( this.PropertyChanged != null )
        {
            this.PropertyChanged( this.Instance, 
               new PropertyChangedEventArgs( propertyName ) );
        }
    }

    [IntroduceMember( OverrideAction = MemberOverrideAction.Ignore )]
    public event PropertyChangedEventHandler PropertyChanged;

    [OnLocationSetHandler, 
     MulticastSelector( Targets = MulticastTargets.Property )]
    public void OnPropertySet( LocationInterceptionArgs args )
    {
        if ( args.Value == args.GetCurrentValue() ) return;

        if ( this.BaseOnPropertyChanged != null )
        {
            this.BaseOnPropertyChanged( args.Location.PropertyInfo.Name );
        }
        else
        {
            this.OnPropertyChanged( args.Location.PropertyInfo.Name );
        }
    }
}

This time, the code is complete. No pedagogical simplification. Look at custom attributes IntroduceInterface and IntroduceMember: I have added an OverrideAction; it tells that interface and member introductions will be silently ignored if already implemented above.

Now look at field BasePropertyChanged: its type is Action<string> (a delegate with signature (string):void) and is annotated with custom attribute ImportMember. At runtime, this field with be bound to method OnPropertyChanged of the base type. If there is no such method in the base type, the field will simply be null. So, in method OnPropertySet, we can now choose: if there was already a method OnPropertyChanged, we invoke the existing one. Otherwise, we invoke the one we are introducing.

Thanks to ImportMember, we know how to extend a class that already implement the pattern. But how to make our own implementation extensible by derived classes? We have to introduce the method OnPropertyChanged and make it virtual. It's done, again, by custom attribute IntroduceMember.

That's all. You can now use the code on class hierarchies, like here:

[NotifyPropertyChanged]
public class TestBaseClass
{
    public string PropertyA { get; set; }
}
public class TestDerivedClass : TestBaseClass
{
    public int PropertyB { get; set; }
}
class Program
{
    static void Main(string[] args)
    {
        TestDerivedClass c = new TestDerivedClass();
        Post.Cast<TestDerivedClass, INotifyPropertyChanged>( c ).PropertyChanged += OnPropertyChanged;
        c.PropertyA = "Hello";
        c.PropertyB = 5;
    }

    private static void OnPropertyChanged( object sender, PropertyChangedEventArgs e )
    {
        Console.WriteLine("Property changed: {0}", e.PropertyName);
    }
}

With PostSharp 1.5, you could do implement easy aspects easily. With PostSharp 2.0, you can implement more complex design patterns, and it's still easy.

Happy PostSharping!

-gael

P.S. Kicking?