![]() |
Platforms, Frameworks & Libraries »
.NET Framework »
How To
Intermediate
License: The Code Project Open License (CPOL)
Aspect Oriented Programming for BenchmarkingBy Marcelo Ricardo de OliveiraThis article explains how you can use AOP for benchmarking purposes. |
C# 2.0, Windows, .NET 2.0, WinForms, VS2005, Architect, Dev, Design
|
|
Advanced Search Add to IE Search |
|
|
|
||||||||||||||||

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:
Statistics) which performs statistics calculations.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.
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:
For reference and clarification, I'll enumerate some of the AOP concepts used in the article (also according to the Spring.NET documentation):
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:

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.
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:
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.
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:
<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:

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

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

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.
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:

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:

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:

Unfortunately, the methods we are testing are too fast to be perceived by the benchmarking. What if we created a more challenging example?
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... ;-) ) 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:
BiDirectionalBubbleSortBubbleSorterComboSort11ComparableComparerDoubleStorageMergeSortFastQuickSorterHeapSortInPlaceMergeSortInsertionSortOddEvenTransportSorterQuickSorterQuickSortWithBubbleSortSelectionSortShakerSortShearSorterShellSortSince 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):

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:

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.
General
News
Question
Answer
Joke
Rant
Admin
|
PermaLink |
Privacy |
Terms of Use
Last Updated: 1 Jul 2007 Editor: Smitha Vijayan |
Copyright 2007 by Marcelo Ricardo de Oliveira Everything else Copyright © CodeProject, 1999-2009 Web17 | Advertise on the Code Project |