Click here to Skip to main content
Click here to Skip to main content

Aspect Oriented Programming for Benchmarking

, 1 Jul 2007
Rate this:
Please Sign up or sign in to vote.
This article explains how you can use AOP for benchmarking purposes.

Screenshot - AOP.gif

Introduction

In this article, I'll try to demonstrate how you can use Aspect Oriented Programming to develop a non-intrusive, simple benchmarking application step-by-step.

The article comes with a sample C# solution (source code included) with the following projects:

  • Benchmarking: a class library project which provides general-purpose benchmarking facilities.
  • MyApplication.CommandCore: a library containing a calculator which implements the Command Pattern. Hence, it can performs undo and redo operations.
  • MyApplication.CommandClient: a console application which references the MyApplication.CommandCore project and retrieves performance information for the calculator operations.
  • Sorting: a class library project containing various implementations of sorter objects.
  • MyApplication.Statistics: a class library project containing a single class (Statistics) which performs statistics calculations.
  • MyApplication.SortingStrategy: a Windows Forms project which references MyApplication.Statistics and retrieves and displays performance information for the Sort method of the various existing sorting algorithms.

Certainly, there are many available frameworks which provide performance functionalities. So, this article doesn't aim to reinvent the wheel by providing a new benchmarking tool. Rather, the focus is on the opportunity to explore areas where AOP can be very useful.

Background

Defining AOP

Since I used the Spring.NET Framework to develop the sample code, I'll use the Spring.NET definition to describe Aspect Oriented Programming:

"Aspect-Oriented Programming (AOP) complements OOP by providing another way of thinking about program structure. Whereas OO decomposes applications into a hierarchy of objects, AOP decomposes programs into aspects or concerns. This enables the modularization of concerns such as transaction management that would otherwise cut across multiple objects (such concerns are often termed crosscutting concerns)."

The above definition mentioned transaction management as an example of common concern (or aspect) between programs, but we could also enumerate other concerns which cut across multiple objects:

  • Logging,
  • Security,
  • Mock frameworks (such as in TypeMock),
  • Exception handling, and of course...
  • Benchmarking measurements

The AOP Jargon

For reference and clarification, I'll enumerate some of the AOP concepts used in the article (also according to the Spring.NET documentation):

  • Aspect: A modularization of a concern for which the implementation might otherwise cut across multiple objects. Transaction management is a good example of a crosscutting concern in enterprise applications. Aspects are implemented using Spring.NET as advisors or interceptors.
  • Advice: Action taken by the AOP framework at a particular joinpoint. Different types of advices include "around," "before", and "throws" advice. Advice types are discussed below. Many AOP frameworks, including Spring.NET, model an advice as an interceptor, maintaining a chain of interceptors "around" the joinpoint.
  • Pointcut: A set of joinpoints specifying when an advice should fire. An AOP framework must allow developers to specify pointcuts: for example, using Regular Expressions.
  • Target object: Object containing the joinpoint. Also referred to as advised or proxied object.
  • Before advice: Advice that executes before a joinpoint, but which does not have the ability to prevent execution flow proceeding to the joinpoint (unless it throws an exception).
  • After returning advice: Advice to be executed after a joinpoint completes normally: for example, if a method returns without throwing an exception.

The Undoable/Redoable Calculator

The MyApplication.CommandCore presents an undoable, 4-operation calculator class that is used by the MyApplication.CommandClient project. You can perform operations and then call the Undo(n) method. Thus, the calculator will roll back the latest n operations.

The top level object in the calculator project is the User class. This is how the client application performs calculator operations:

class Program
{
    static void Main(string[] args)
    {
        // Create user and let her compute 

        User user = new User();
        
        user.Initialize();
        user.Compute('+', 100);
        user.Compute('-', 50);
        user.Compute('*', 10);
        user.Compute('/', 2);
        
        // Undo 4 commands 

        user.Undo(4);
        
        // Redo 3 commands 

        user.Redo(3);
        
        // Wait for user 

        Console.In.Read();
    }
}

The above code (until the sum operation line) would look like like this in a sequence diagram:

Screenshot - AOP1.jpg

Okay, now let's say you are interested in measuring the performance of your calculator. For this simple task, you could start by adding lines of code at the beginning and at the end of your User class, so that you could later compare the times and calculate the execution time:

        public void Compute(char @operator, int operand)
        {
            RegisterThisEntryTimeSomewhere();

            // Create command operation and execute it 

            Command command = new CalculatorCommand(
              calculator, @operator, operand);
            command.Execute();

            // Add command to undo list 

            commands.Add(command);
            current++;

            RegisterThisReturnTimeSomewhere();
        }

But wait, we're not done yet. The User class still has other methods, like "Undo":

        public void Undo(int levels)
        {
            RegisterThisEntryTimeSomewhere();

            Console.WriteLine("\n---- Undo {0} levels ", levels);
            // Perform undo operations 

            for (int i = 0; i < levels; i++)
            {
                if (current > 0)
                {
                    Command command = commands[--current] as Command;
                    command.UnExecute();
                }
            }
            RegisterThisReturnTimeSomewhere();
        }

Hmm, seems like we are in trouble. Are we going to change every method we're trying to measure? Obviously, changing every method of our cool objects is not a good idea. In addition, there would be cases when we couldn't even change the code of the objects we are trying to measure. This is where Aspect Oriented Programming can be of great value.

The Benchmarking Project

Let's create a new project, called Benchmarking, in order to provide AOP functionality to enable general-purpose performance measurement to our client application. Let's write down the guidelines for our new Benchmarking project:

  • It should use Spring.Net AOP functionalities.
  • It should provide the user with an easy interface and non-intrusive AOP capabilities.
  • It should expose functionality for creating new proxied objects. After a proxied object is created, it should start registering a time sheet with execution times for the object members we want to measure, according to a configuration file.
  • Each row of the time sheet will contain the following information: class name, member name (i.e., method/property name), argument list, and elapsed time.
  • We should be able to retrieve the time sheet whenever we want.
  • We should be able to reset the time sheet so that we could start over the performance measurement.

The BenchmarkFactory starts by instantiating a static ApplicationContext for IoC Container (= Inversion of Control Container, a technique which implements the Inversion of Control Pattern. See more about IoC Pattern in Billy McCafferty's excellent article Dependency Injection for Loose Coupling). Inversion of Control will provide us with the flexibility of instantiating different class just by changing the client application's XML configuration file.

        // Create AOP proxy using Spring.NET IoC container.

        private static IApplicationContext ctx = ContextRegistry.GetContext();
        private static List<timerow> timeSheet = new List<timerow>();

The core of the BenchmarkFactory class is the GetProxy() method. It has a single line, and returns an object created via the IoC container technique provided by the same Spring.NET framework:

        public static object GetProxy(string objectName)
        {
            return ctx[objectName];
        }

When the GetProxy() method is called, the AOP framework generates a dynamic proxy for the requested class, with additional code that enables the object's members to be intercepted during the invocation lifetime. The interceptor is the BenchmarkAroundAdvice class of the Benchmarking project.

Back to the Client Calculator Project

Now that we have a benchmarking layer, let's start changing our client calculator project. First of all, the client calculator project will have a new app.config file holding the configuration for the AOP framework. This configuration information comprises:

  • The information about the proxied object: the full name (namespace + class name) of the proxied class, the assembly name, and the interceptor names.
  • The Around Advice information: the full name of the advisor class, the assembly name, and a pointcut with the mapped names which defines which class members are going to be measured.
  <object id="aroundAdvisor" 
          type="Spring.Aop.Support.NameMatchMethodPointcutAdvisor, Spring.Aop">
    <property name="Advice">
      <object type="Benchmarking.Aspects.BenchmarkAroundAdvice, Benchmarking" />
    </property>
    <property name="MappedNames">
      <list>
        <value>*</value>
      </list>
    </property>
  </object>

This is how we avoid the cumbersome task of changing every class we want to measure...

Now, we should replace the instantiation method of our user object. Instead of using the new keyword, we will use the BenchmarkFactory.GetProxy() method:

IUser user = (IUser)BenchmarkFactory.GetProxy("user");

Notice that the above line is needed so that the AOP framework could create a dynamic proxy that intercepts the beginning and the end of each method/property call.

Immediately after the GetProxy() line, we must reset the time sheet for the BenchmarkFactory:

BenchmarkFactory.ResetTimeSheet();

After we have finished operations with our calculator object, we can iterate through the time sheet rows, in order to list the performance for each method call:

            Console.WriteLine("===============================");
            Console.WriteLine("BENCHMARKING RESULTS");
            Console.WriteLine("===============================");
            foreach (TimeRow tr in timeSheet)
            {
                Console.WriteLine("===============================");
                Console.WriteLine("Class Name = {0}", tr.ClassName);
                Console.WriteLine("Member Name = {0}", tr.MemberName);
                Console.WriteLine("Arguments = {0}", tr.ArgList);
                Console.WriteLine("Elapsed time = {0} milliseconds", tr.ElapsedTime);
            }
            Console.WriteLine("===============================");

After the changes, this is the new sequence diagram for the application. Notice that the proxied IUser object is now intercepted around each method call. Once the pointcut specifications are matched, the corresponding Around Advice class is triggered automatically:

Screenshot - AOP3.jpg

Now, we can run the application again and see our performance results for the first time:

Screenshot - AOP2.jpg

Visual Benchmarking

Let's open the Benchmarking project to create a Windows form representing the timesheet:

Screenshot - AOP4.jpg

This new user interface is provided by a singleton form, and is useful when you have another Windows Forms project that references the Benchmarking project, and you want your Benchmarking results to be processed in the background but to be shown only when the user explicitly asks the application to do so (e.g., by clicking a "Benchmarking" submenu under a "Help" menu).

This interface also allows the user to copy the timesheet data to the clipboard. Thus, the copied data can be pasted directly to an Excel worksheet. In addition, the user can reset timesheet data whenever he/she wants.

The Windows Form Calculator Project

Now that we can show benchmarking data in a Windows form, let's create a new project (CommandUI) and implement a new Windows Forms interface for our old console calculator:

Screenshot - AOP5.jpg

The new interface is cool. Now, we can perform the 4-operations, plus undo/redo functionality. But the great leap here is the ability to show benchmarking results. After some calculations, this is what we get when the Show Benchmark button is clicked:

Screenshot - AOP6.jpg

Notice that two members of the User class are displayed: Computed and get_CurrentValue. The Spring.NET AOP framework allows us to change the App.config so that we can specify what members we want to display. So, we open the App.config and replace the "*" value by "Compute" inside the "MappedNames" property, for the aroundAdvisor configurations.

  <object id="aroundAdvisor" 
          type="Spring.Aop.Support.NameMatchMethodPointcutAdvisor, Spring.Aop">
    <property name="Advice">
      <object type="Benchmarking.Aspects.BenchmarkAroundAdvice, Benchmarking" />
    </property>
    <property name="MappedNames">
      <list>
        <value>Compute</value>
      </list>
    </property>
  </object>

This is how the timesheet looks like after the change:

Screenshot - AOP7.jpg

Unfortunately, the methods we are testing are too fast to be perceived by the benchmarking. What if we created a more challenging example?

The Sorting Project and the Sorting Strategy Project

Suppose you were given a task to create an application to perform intense statistical calculations. You discover that you will use a sorting algorithm in many points of the application. (Okay, we know the .NET Framework has a lot of objects with sorting functionalities, but let's forget about it for a while... Wink | ;-) ) You also discover that the effectiveness of the sorting algorithm depends on the number of elements to be sorted. Thus, you create two projects: the Sorting Project, holding different sorting algorithms, and the Sorting Strategy Project, which should test the effectiveness of these algorithms.

These are the sorting algorithm classes to be tested:

  • BiDirectionalBubbleSort
  • BubbleSorter
  • ComboSort11
  • ComparableComparer
  • DoubleStorageMergeSort
  • FastQuickSorter
  • HeapSort
  • InPlaceMergeSort
  • InsertionSort
  • OddEvenTransportSorter
  • QuickSorter
  • QuickSortWithBubbleSort
  • SelectionSort
  • ShakerSort
  • ShearSorter
  • ShellSort

Since each sorting class implements an ISort interface with a public Sort method, we have to make the app.config specify that our around advisor will intercept only the Sort method of these classes:

  <object id="aroundAdvisor" 
          type="Spring.Aop.Support.NameMatchMethodPointcutAdvisor, Spring.Aop">
    <property name="Advice">
      <object type="Benchmarking.Aspects.BenchmarkAroundAdvice, Benchmarking" />
    </property>
    <property name="MappedNames">
      <list>
        <value>Sort</value>
      </list>
    </property>
  </object>

The Sorting Strategy project has just one form, intended to measure the execution time for the sorting algorithms for different element counts (marks):

Screenshot - AOP8.jpg

You can also click the Copy to Clipboard button and then paste the data directly on an Excel worksheet. After that, you can create a chart to view more clearly the evolution of effectiveness for each sorting algorithm through different element counts:

Screenshot - AOP9.jpg

Conclusion

I hope this simple example may give the readers some background regarding the practical usage of the Aspect Oriented Programming provided by the Spring.NET framework. If you reached this point of the article, thank you very much for your patience. Any comment, advice, or complaint about the article or the examples will be highly appreciated.

History

  • 2007/06/13 - Initial posting.
  • 2007/07/01 - Code and article updated (Before and After Advices replaced by Around Advice).

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

About the Author

Marcelo Ricardo de Oliveira
Software Developer
Brazil Brazil
Marcelo Ricardo de Oliveira is a senior software developer who lives with his lovely wife Luciana and his little buddy and stepson Kauê in Guarulhos, Brazil, is co-founder of the Brazilian TV Guide TV Map and currently works for ILang Educação.
 
He is often working with serious, enterprise projects, although in spare time he's trying to write fun Code Project articles involving WPF, Silverlight, XNA, HTML5 canvas, Windows Phone app development, game development and music.
 
Published Windows Phone apps:
 
 
Awards:
 
CodeProject MVP 2012
CodeProject MVP 2011
 
Best Web Dev article of March 2013
Best Web Dev article of August 2012
Best Web Dev article of May 2012
Best Mobile article of January 2012
Best Mobile article of December 2011
Best Mobile article of October 2011
Best Web Dev article of September 2011
Best Web Dev article of August 2011
HTML5 / CSS3 Competition - Second Prize
Best ASP.NET article of June 2011
Best ASP.NET article of May 2011
Best ASP.NET article of April 2011
Best C# article of November 2010
Best overall article of November 2010
Best C# article of October 2010
Best C# article of September 2010
Best overall article of September 2010
Best overall article of February 2010
Best C# article of November 2009

Comments and Discussions

 
GeneralCode speaks for itself Pinmemberi am iffi19-Jun-07 4:01 
GeneralRe: Code speaks for itself PinmemberMarcelo Ricardo de Oliveira19-Jun-07 13:14 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.

| Advertise | Privacy | Mobile
Web02 | 2.8.140721.1 | Last Updated 1 Jul 2007
Article Copyright 2007 by Marcelo Ricardo de Oliveira
Everything else Copyright © CodeProject, 1999-2014
Terms of Service
Layout: fixed | fluid