Aspect Oriented Programming for Benchmarking (转与 The Code Project )

来源:百度文库 编辑:神马文学网 时间:2024/04/29 16:51:24
Aspect Oriented Programming for Benchmarking
ByMarcelo Ricardo de Oliveira.
This article explains how you can use AOP for benchmarking purposes
C# (C# 2.0)
.NET (.NET 2.0), Windows
Win32, VS (VS2005), WinForms
Arch, Dev, Design
Posted: 13 Jun 2007
Views: 788
Note:This is an unedited reader contributionModify |Delete
Announcements
Vista Mobile comp:Win a Samsung UMPC
VB6 Interop CompWin an Xbox Elite!
Monthly Competition

Search   Articles Authors   Advanced Search
Sitemap
PrintBroken Article?BookmarkDiscussSend to a friend
3 votes for this article.
Popularity: 1.81. Rating: 3.8 out of 5.
Note: This is an unedited contribution. If this article is inappropriate, needs attention or copies someone else‘s work without reference then pleaseReport this article.
Download AOP.zip - 300.0 KB
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 acalculator which implements the Command Pattern. Hence, it can performs undo and redo operations. MyApplication.CommandClient: aconsole application which references 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 theSpring.NET Framework to develop the sample code, I‘ll use 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 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 inenterprise 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 advice 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:

Okay, now let‘s say you are interested in measuring 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 the 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 exposes a 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/propety 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 the Billy McCafferty‘s excellent articleDependency 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 timeSheet = new List();
The core of the BenchmarkFactory class is the GetProxy() method. It has a single line and returns an object created via 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 before and after the invocation. The interceptors are the BenchmarkBeforeAdvice and BenchmarkAfterAdvice classes 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 Before 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. The After 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.Collapse
* *
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 before and after each method call. Once the pointcut specifications are matched, the corresponding before/after advice classes are triggered automatically:

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

Visual Benchmarking
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 allow the user to copy the timesheet data to the clipboard. Thus, the copied data can be pasted directly to a 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:

The new interface is cool, now we can perform the 4-operations, plus undo/functionality. But the great leap here is the ability to show benckmarking 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 allow 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 both the beforeAdvisor and afteradvisor configurations.
Collapse
Compute Compute
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 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 .NET Framework has a lot of objects with sorting funcionalities, 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:
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 before/after advisors will intercept only the Sort method of these classes:
Collapse
Sort Sort
The Sorting Strategy project has just one form, intended to measure 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:

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.
About Marcelo Ricardo de Oliveira

Marcelo Ricardo de Oliveira is product developer atSpring Wireless Brasil (São Paulo - Brazil), a leading end-to-end mobile business solutions provider. Clickhere to view Marcelo Ricardo de Oliveira‘s online profile.