|
|||||||||||||||||||||||
|
|||||||||||||||||||||||
|
Announcements
Want a new Job?
Chapters
Services
Feature Zones
|
IntroductionIn 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:
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. BackgroundDefining AOPSince 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:
The AOP JargonFor reference and clarification, I'll enumerate some of the AOP concepts used in the article (also according to the Spring.NET documentation):
The Undoable/Redoable CalculatorThe 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 The top level object in the calculator project is the 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 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 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 ProjectLet'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 // 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 public static object GetProxy(string objectName)
{
return ctx[objectName];
}
When the Back to the Client Calculator ProjectNow 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 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 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
Now, we can run the application again and see our performance results for the first time:
Visual BenchmarkingLet'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. The Windows Form Calculator ProjectNow 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 <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? The Sorting Project and the Sorting Strategy ProjectSuppose 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:
Since each sorting class implements an <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:
ConclusionI 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
|
||||||||||||||||||||||