Archive

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?

Yesterday, C# MVP Job Skeet blogged about a  way to add generic constraints for enums and delegates. He noticed that, while the C# compiler forbids such constructions, it is perfectly possibly in MSIL. And he is fully right with this.

Jon uses a post-build step to add modify generic constraints. His process: ILDASM, then find-and-replace, then ILASM. That’s possible because the requirement is pretty easy.

But here he challenged me:

I've looked at PostSharp and CCI; both look way more complicated than the above.

Sure, PostSharp would be more complex, but Jon’s ILASM-based solution works only in best cases. There are plenty of things that you have to keep in mind while building a post-build step that modifies MSIL. For instance, how will ILASM find referenced assemblies? What with signed assemblies?  Sure, you can find a solution to everything – after all, PostSharp uses ILASM as a back-end (contrarily to rumors, it does not use ILDASM). But, believe me, addressing these side issues will prove more difficult than the core job itself.

And anyway, why would it be so complex to implement these rather simple requirements using PostSharp? PostSharp is designed so that easy jobs get easily done.

I took the challenge and implemented an add-in adding arbitrary constraints to generic parameters. I measured time. It took me 30 minutes. NDepend counts 12 lines of code.

It’s based on custom attributes. PostSharp loves custom attributes. I defined a custom attribute AddGenericConstraintAttribute that can be applied to generic parameters. Here is how it’s used:

public static class EnumHelper
{
    public static T[] GetValues<[AddGenericConstraint( typeof(Enum) )] T>() 
       where T : struct
    {
        return (T[]) Enum.GetValues( typeof(T) );
    }
}

The project can be downloaded from http://postsharp-user-samples.googlecode.com/files/AddGenericConstraint.zip. In order to use the plug-in, follow the documentation.

Here are the principal steps I followed to create this plug-in:

1.       Create a new solution AddGenericConstraint.sln.

2.       Create a new class library project AddGenericConstraint.csproj. This project will be the public interface of the plug-in.

3.       Create the class AddGenericConstraintAttribute.cs:

[AttributeUsage( AttributeTargets.GenericParameter )]
public sealed class AddGenericConstraintAttribute : Attribute
{
    public AddGenericConstraintAttribute( Type type )
    {
    }
}

4.       Create a new class library project AddGenericConstraint.Impl.csproj. This will contain the plug-in implementation.

5.       Add references to PostSharp.Public.dll, PostSharp.Core.dll, AddGenericConstraint.csproj.

6.       Create a class AddGenericConstraintTask. This is the real implementation class. This is the hard point.

public class AddGenericConstraintTask : Task
{
    public override bool Execute()
    {
        // Get the type AddGenericConstraintAttribute.
        ITypeSignature addGenericConstraintAttributeType = 
            this.Project.Module.FindType(
               typeof(AddGenericConstraintAttribute),
               BindingOptions.OnlyDefinition | BindingOptions.DontThrowException );

        if ( addGenericConstraintAttributeType == null )
        {
            // The type is not referenced in the current module. There cannot be a custom attribute
            // of this type, so we are done.
            return true;
        }

        // Enumerate custom attributes of type AddGenericConstraintAttribute.
        AnnotationRepositoryTask annotationRepository = 
            AnnotationRepositoryTask.GetTask( this.Project );

        IEnumerator customAttributesEnumerator =
            annotationRepository.GetAnnotationsOfType( 
              addGenericConstraintAttributeType.GetTypeDefinition(), false );

        while ( customAttributesEnumerator.MoveNext() )
        {
            // Get the target of the custom attribute.
            GenericParameterDeclaration target = (GenericParameterDeclaration)
                customAttributesEnumerator.Current.TargetElement;
 
            // Get the value of the parameter of the custom attribute constructor.
            ITypeSignature constraintType = (ITypeSignature)
                customAttributesEnumerator.Current.Value.
                 ConstructorArguments[0].Value.Value;

            // Add a generic constraint.
            target.Constraints.Add( constraintType );

            // Remove the custom attribute.
            ((CustomAttributeDeclaration) customAttributesEnumerator.Current).Remove();
        }

        return base.Execute();
    }
}

7.       Add an XML file AddGenericConstraint.psplugin to the project. This file will describe your plug-in. In file properties, specify “Copy to Output Directory: Copy Always”.

<?xml version="1.0" encoding="utf-8" ?>
<PlugIn xmlns="http://schemas.postsharp.org/1.0/configuration">
  <TaskType Name="AddGenericConstraint" Implementation="AddGenericConstraint.Impl.AddGenericConstraintTask, AddGenericConstraint.Impl" Phase="Transform">
    <Dependency TaskType="Remove" Position="After"/>
    <Dependency TaskType="AnnotationRepository"/>
  </TaskType>
</PlugIn>

8.       Go back to project AddGenericConstraint. Add a reference to PostSharp.Public.dll.  Add the following on the top of the class AddGenericConstraintAttribute to bind the custom attribute to its implementation:

[RequirePostSharp( "AddGenericConstraint", "AddGenericConstraint" )]

9.       Create a test project. Add references to PostSharp.Public.dll and AddGenericConstraint.csproj. In project properties, add directory “..AddGenericConstraint.ImplinDebug” to reference paths.

10.   You are done.

What’s the catch? Maybe that, if you don’t know PostSharp Core and MSIL, it will take you days to come with the 12 lines of code that form the implementation. But when you got the knowledge, you are incredibly productive. And all the issues caused by the integration in the build process are solved for you. Believe me, after 5 years of existence, there is a huge knowledge behind PostSharp integration.

Happy PostSharping!

-gael

PS. It took me longer to write this blog than the implementation itself.