Skip to main content

[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




Back to the top