Skip to main content

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index] [List Home]
Re: [aspectj-dev] Performance facts and tips

Without repeating Jim's caveat about benchmarks (though I should), I'd
speculate that the performance penalty of unqualified after stems from the
overhead required by a finally block.

After is probably implemented something like:
try{
  //joinpoint
}
finally{
  //unqualified after
}

From what I have heard, finally blocks incur some performance penalty.

The important thing to note is that this "finally" behavior may differ from
author intent. It's appropriate for closing connections, for instance. It
may not be appropriate for, say, notifying another object of a state change.

Cheers,
Nick

On 8/27/03 5:59 AM, "Jeff Dalton" <jeffdalton104@xxxxxxxxxxx> wrote:

> I was wondering if you could provide more detail about this comment:
> 
> o Prefer after returning to after
> 
> "after" advice is more costly than "after returning";
> in most cases, users actually intend "after returning".
> 
> Could you explain why after is more costly?  Anyone?
> 
> - Jeff Dalton
> 
> 
>> From: Wes Isberg <wes@xxxxxxxxxxxxxx>
>> Reply-To: aspectj-dev@xxxxxxxxxxx
>> To: "aspectj-users@xxxxxxxxxxx" <aspectj-users@xxxxxxxxxxx>
>> CC: "aspectj-dev@xxxxxxxxxxx" <aspectj-dev@xxxxxxxxxxx>
>> Subject: [aspectj-dev] Performance facts and tips
>> Date: Fri, 22 Aug 2003 17:53:28 -0700
>> 
>> Below are heuristics for avoiding unnecessary performance
>> overhead in programs using our implementation of AspectJ 1.1.
>> 
>> - Are they right?
>> 
>> - Is anyone interested in writing better examples?
>>   Or in writing code to quantify the effects?
>> 
>> - Are there other heuristics?
>> 
>> Results from this discussion will go at least into the FAQ,
>> and perhaps into the programming guide or fixes for 1.1.X/1.2.
>> 
>> I also want to know what the standards are for a good AspectJ
>> program so that we can ask whether there are cases where the
>> performance of a good AspectJ program built with our
>> implementation is not on par with an equivalent solution written
>> by hand in Java or implemented with an alternative AOP framework.
>> Any experience reports on that point are also welcome!
>> 
>> Thanks -
>> Wes
>> 
>> -------- Heuristics
>> As with any performance heuristic, use these only if profiling
>> shows you have a problem and only if they don't change the
>> semantics of your program in bad ways.
>> 
>> ---- writing pointcuts
>> 
>> o Prefer static to dynamic pointcuts
>> 
>> The dynamic pointcut designators are if(), this(), target(),
>> args(), cflow() and cflowbelow().
>> 
>> The compiler/weaver can resolve static pointcuts, so there is
>> no cost at runtime to determine whether advice should run.
>> The static pointcuts do mean something different, so they should
>> only be used when they're correct.
>> 
>> Examples:
>>   - within(..) v. this(..)
>>   - withincode(..) v. cflow(..)
>>   ...
>> 
>> 
>> o Restrict the scope of dynamic pointcuts with static pointcuts
>> 
>> You can limit the cost of determining whether advice should run
>> by restricting the points at which the dynamic test is run.
>> 
>> Examples:
>>   - call(void run()) && target(Foo)
>>   - within(com.company.app..*) && printingCalls()
>> 
>> 
>> o Restrict wildcarded pointcuts
>> 
>> Some wildcarded pointcut match a great many join points,
>> e.g., "call(* *(..))".  Used alone, this is rarely what's intended.
>> 
>> 
>> o Change the code to be more susceptible to pointcuts
>> 
>> Sometimes it's possible and easier to refactor the target code
>> to write a better pointcut; this can avoid dynamic tests.
>> 
>> 
>> ---- writing advice
>> 
>> o Prefer thisJoinPointStaticPart (or neither) to thisJoinPoint
>> 
>> Using thisJoinPoint involves some runtime reflection.
>> 
>> 
>> o Prefer binding variables with target(), args() or this()
>>   to using thisJoinPoint.getArgs()
>> 
>> For the same reason as above...
>> 
>> 
>> o Prefer after returning to after
>> 
>> "after" advice is more costly than "after returning";
>> in most cases, users actually intend "after returning".
>> 
>> 
>> o Avoid {re}calculating things where possible
>> 
>> This is straight Java, but can have a lot of impact in advice
>> that runs frequently.
>> 
>> Examples:
>>   - deferring String concatenation until use
>>   - using lazy construction
>>   - caching results...
>> 
>> 
>> ---- writing inter-type declarations
>> 
>> o Avoiding maps
>> 
>> Maps can be used to associate state with a particular instance
>> or thread.  AspectJ provides inter-type member declarations and
>> "per" aspects that can do (and optimize) the same associations,
>> avoiding the hashing or the downcasts in your code.
>> 
>>   o Prefer perthis or pertarget when only using per-instance
>>     state in one aspect with the one master pointcut
>> 
>>   o Prefer private inter-type declarations to mapping from targets
>> 
>>   o Prefer percflow to mapping to Thread or using ThreadLocal
>> 
>> ---- writing aspects
>> 
>> o Prefer default (issingleton) aspects, other things being equal
>> 
>> 
>> ---- comment
>> 
>> Most of these heuristics are not worth saying without
>> more compelling examples.
>> 
>> I'm actually not sure that maps are always worse, but I've
>> seen bad code that uses maps when the AspectJ form was faster
>> and clearer.  It may have just been unnecessary complexity.
>> 
>> The advice heuristics reduce the runtime work done by preferring
>> cheaper alternatives; they're mostly straight Java.
>> 
>> The pointcut heuristics should reduce runtime costs
>> (and make compiles faster) by more strictly specifying the
>> join point shadows specified (or not) by the static pointcut
>> designators.  The dynamic pointcut designators are if(),
>> this(), target(), args(), cflow() and cflowbelow().
>> 
>> However, the dynamic checks should be efficient:
>> 
>> - this(), target() and args() are implemented by 'instanceof'
>>   checks, which have been optimized in most modern VM's
>> 
>> - cflow() and cflowbelow() are comparable to using ThreadLocal's
>> 
>> - if() is arbitrarily expensive, depending on the code invoked.
>> 
>> There's one exception to prefering static forms.  Given Type,
>> a subclass of SuperType, and foo(), defined in SuperType:
>> 
>>  static form:  call(* Type.foo(..))
>>  dynamic form: target(Type) && call(* foo(..))
>> 
>> Some older compilers incorrectly replaced the static reference
>> type of a method invocation with the type of the nearest
>> superclass implementation.  In that case, when weaving
>> binaries generated by older compilers, the method invocation
>> would not be matched by the first/static pointcut, but would
>> be matched by the second/dynamic one.  If it's what the
>> user intended, it could be changed to use the static form
>> 
>>     call(* SuperType.foo(..))
>> 
>> 
>> _______________________________________________
>> aspectj-dev mailing list
>> aspectj-dev@xxxxxxxxxxx
>> http://dev.eclipse.org/mailman/listinfo/aspectj-dev
> 
> _________________________________________________________________
> Get MSN 8 and enjoy automatic e-mail virus protection.
> http://join.msn.com/?page=features/virus
> 
> _______________________________________________
> aspectj-dev mailing list
> aspectj-dev@xxxxxxxxxxx
> http://dev.eclipse.org/mailman/listinfo/aspectj-dev



Back to the top