This page documents performance tests for Spring.AOP so you can compare it with other available AOP solutions and non-AOP approaches when deciding which implementation to use.
Methodology
There are several things that need to be tested when comparing performance of different AOP implementations.
First thing to test is object creation. It is important to know how much overhead is introduced to object creation process when using AOP. However, for most practical purposes this test is not critical unless you are using AOP on a large number of fine grained objects. Most aspects will probably be applied to coarse grained service classes in the middle tier, so object creation time shouldn't be an issue, especially if you are using Spring, because service instances will be cached in a container.
Second thing to test is the performance of method calls that don't have any interceptors applied. This will happen often when you are using AOP introductions on fine grained objects. If method has no interceptors there is no reason to use reflection to invoke it – proxy should be smart enough to emit direct call in that case which will significantly improve performance.
Finally, we also need to test performance of the intercepted method call.
Our tests use very simple target class that has Nop method with no parameters and no return value that does absolutely nothing, and DoSomething method that simulates method that actually performs some work by calling MethodBase.GetCurrentMethod().
Same applies to introduction class, the only difference being the interface that target and introduction implement:
public interface ITarget
{
void Nop();
void DoSomething();
}
public interface IIntroduction
{
void Nop();
void DoSomething();
}
public class Target : ITarget
{
public void Nop()
{}
public void DoSomething()
{
MethodBase.GetCurrentMethod();
}
}
public class Introduction : IAdvice, IIntroduction
{
public void Nop()
{}
public void DoSomething()
{
MethodBase.GetCurrentMethod();
}
}
public class NopInterceptor : IMethodInterceptor
{
public Object Invoke(IMethodInvocation invocation)
{
return invocation.Proceed();
}
}
Interceptor used also does nothing – it simply invokes method on a target object.
Test Hardware
All the tests were performed on Sun Java Workstation w1100, with single Opteron 144 CPU and 1GB of RAM.
Results
Object Creation
| Test Name |
Iterations |
Duration |
Per Second |
| Object Creation |
1,000,000 |
0.172 |
5,818,107 |
| Proxy Creation |
1,000,000 |
7.531 |
132,778 |
As you can see, proxy creation is much slower than direct target object creation. This is because in this particular case target object's constructor does nothing, while proxy constructor does a number of things, such as setting up target object, introductions array, etc.
Nop Method Calls
| Test Name |
Iterations |
Duration |
Per Second |
| Direct Nop Call |
10,000,000 |
0.125 |
79,998,976 |
| Target Nop Call (no interceptors) |
10,000,000 |
1.250 |
7,999,898 |
| Introduction Nop Call (no interceptors) |
10,000,000 |
1.516 |
6,597,854 |
| Target Nop Call (with interceptors) |
10,000,000 |
16.031 |
623,774 |
| Introduction Nop Call (with interceptors) |
10,000,000 |
17.375 |
575,532 |
Direct call is again an order of magnitude faster, but proxy calls without interceptors still clock fairly impressive 6.5 to 8 million calls per second. Adding interceptor slows them down to around 600K calls per second, both because of an additional call (interceptor method) and because target call has to be made using reflection.
DoSomething Method Calls
| Test Name |
Iterations |
Duration |
Per Second |
| Direct DoSomething Call |
10,000,000 |
10.172 |
983,090 |
| Target DoSomething Call (no interceptors) |
10,000,000 |
11.438 |
874,306 |
| Introduction DoSomething Call (no interceptors) |
10,000,000 |
12.500 |
799,990 |
| Target DoSomething Call (with interceptors) |
10,000,000 |
28.969 |
345,195 |
| Introduction DoSomething Call (with interceptors) |
10,000,000 |
27.094 |
369,084 |
This is where it gets interesting. As you can see from the results, if target method actually does some work, difference between direct and proxy call becomes very small, little bit over 12% in this case.
While Nop tests are the best way to compare overhead of different AOP implementations, this test is more realistic and shows what the impact of proxies might be in a real-world application.
This test shows that even when the interceptors are involved, impact on performance is not very significant, especially when target method queries a database or does something else time consuming. Even though intercepted method above is almost three times slower than the direct method, 350K calls per second is still a fairly respectable figure.