Skip to main content

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

I mostly just want to echo Jim's sentiment about performance guidelines,
by adding an additional way of looking at it.

I believe two properties of AspectJ are important here:

(1) By and large, AspectJ does a pretty good job of not having more
    than one way to define the same semantics. So, as Jim points out

    pointcut addToList(): target(ArrayList) && call(* add(*));
    pointcut addToList(): call(* ArrayList.add(*));

    have different semantics, they are not two ways of saying the
    same thing.

    Similarly withincode and cflow have very different semantics, as
    do after and after returning and so on.

(2) In addition, AspectJ does a pretty good job of having only constructs
    for which the "inherent implementation" is pretty much what you would
    write if you had to write the same functionality by hand.

Given this, I would encourage people to choose the AspectJ construct they
want based on its semantics rather than performance. If, after doing that,
the performance is problematic, then that may indicate that more work is
needed in the weaver. (And you should send mail about it.)

NOTE: I am NOT saying that semantics is more important than performance.
What I'm saying is that the combination of the two properties above means
that in AspectJ choosing based on semantics mostly implies choosing the
right performance.

Or, put another way, AspectJ is a "low-enough level language" that it
doesn't have to guess how to implement programmer intent. Given that,
it can't guess wrong. (perthis and pertarget are perhaps exceptions)

All that said, it seems that there is demand for text like Wes's that
explains the performance of AspectJ. One way to do that might be to
explain the "inherent implementation" of the various constructs and,
critically explain how the inherent implementation of the various
pointcuts compose.

Gregor

PS Of course there is still room for the AspectJ weaver to do some
optimization of the simple inherent weaving of the code. Some of
that happens, more can happen in the future. This is something that Jim
and Erik have much more to say about than I do.





> -----Original Message-----
> From: aspectj-dev-admin@xxxxxxxxxxx 
> [mailto:aspectj-dev-admin@xxxxxxxxxxx] On Behalf Of Jim Hugunin
> Sent: Monday, August 25, 2003 9:33 AM
> To: aspectj-users@xxxxxxxxxxx
> Cc: aspectj-dev@xxxxxxxxxxx
> Subject: [aspectj-dev] Re: [aspectj-users] Performance facts and tips
> 
> 
> Wes Isberg wrote:
> > Below are heuristics for avoiding unnecessary performance
> > overhead in programs using our implementation of AspectJ 1.1.
> 
> A list of performance facts and tips without real examples and 
> benchmarks to back it up can be a dangerous thing.  I find it hard to 
> comment on the details below with first seeing those examples and 
> benchmarks.  I hope that your solicitation for contributions 
> along those 
> lines is sucessful.  Nevertheless, I do have a few specific comments:
> 
> <snip>
> >   o Prefer perthis or pertarget when only using per-instance
> >     state in one aspect with the one master pointcut
> 
> perthis and pertarget have the potential to cause major performance 
> problems if not used extremely carefully.  I would need to see some 
> compelling benchmark data before recommending these to someone for 
> performance reasons.
> 
> >   o Prefer percflow to mapping to Thread or using ThreadLocal
> 
> If you're concerned solely about performance, use ThreadLocal 
> instead of 
> percflow.  percflow ultimately must use ThreadLocal (or a close 
> substitute that works in 1.1) in its implementation, so it is very 
> unlikely to perform better than using ThreadLocal directly.
> 
> I doubt that benchmarks will show much performance difference at all 
> between these approaches, and therefore the decision should 
> be based not 
> on performance but on which will produce the most readable and 
> understandable code.  BTW - This is my suspicion about most 
> performance 
> issues in the absence of benchmarks.
> 
>  >   o Prefer private inter-type declarations to mapping from targets
> 
> I agree with this advice completely, and I think it should 
> probably be 
> worthy of its own message to users or FAQ entry.  The reason 
> that I can 
> say this even without benchmarks is that I believe it will lead to 
> simpler and easier-to-read code that will benefit from static type 
> checking.  I'm also pretty confident that this will have noticable 
> performance benefits, but I wouldn't want to push those too 
> hard without 
> benchmarks to back me up.
> 
> <snip>
> > 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(..))
> 
> This is an important issue that I'm nervous to see showing up in a 
> message about performance tips.  I believe that all users should be 
> strongly encouraged to use the dynamic form here, i.e.:
> 
>    pointcut addToList(): target(ArrayList) && call(* add(*));
> 
> If this form isn't used, the behavior could be very surprising.  For 
> example:
> 
>    List l = new ArrayList();
>    l.add(item):
> 
> The call to add here will not be matched by the simple 
> pointcut "call(* 
> ArrayList.add(*))" because at the call site, the static signature is 
> "List.add(*)".  This is a subtle issue that doesn't match most 
> programmers' model of how method dispatch works in an OO 
> system and can 
> easily be confusing.  The ability to restrict the declaring type of a 
> method in a call-PCD is an advanced feature of AspectJ and it should 
> only be used in rare situations.  Here's one example:
> 
>    declare warning: call(* ArrayList.*(..)):
>      "Should be accessing through List interface";
> 
> There are some compiler limitations that can add further confusion:
> > 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(..))
> 
> <dangerous-bend>  If you really know what you're doing, and 
> you want to 
> use a declaring type in a call pcd you can include the root declaring 
> type of the method that you're interested in.  Unless you're doing 
> something special that cares about static signatures like the 
> "declare 
> warning" shown above, then this root declaring type is the 
> only one you 
> should ever use in a call PCD.
> 
> The root declaring type of a method is the upper-most class 
> or interface 
>   in the hierarchy that implements the method.  So, in the example 
> above, you could write:
> 
>    pointcut addToList(): target(ArrayList) && call(* List.add(*));
> 
> This makes it explicit that you're interested in the add 
> method declared 
> by the List interface when implemented by an ArrayList.  Because this 
> uses the top-most type that declares this method you can't 
> run into the 
> kinds of weird static signature issues shown above.
> </dangerous-bend>
> 
> My two cents - Jim
> 
> 
> _______________________________________________
> aspectj-dev mailing list
> aspectj-dev@xxxxxxxxxxx
> http://dev.eclipse.org/mailman/listinfo/aspectj-dev
> 



Back to the top