[
Date Prev][
Date Next][
Thread Prev][
Thread Next][
Date Index][
Thread Index]
[
List Home]
[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