220 likes | 353 Views
Optimizing AspectJ. Pavel Avgustinov, Aske Simon Christensen, Laurie Hendren, Sascha Kuzins, Jennifer Lhotak, Ondrej Lhotak, Oege de Moor, Damien Sereni, Ganesh Sittampalam, Julian Tibble Oxford University University of Aarhus McGill University PLDI 2005. Presented by Geoff Hulette.
E N D
Optimizing AspectJ Pavel Avgustinov, Aske Simon Christensen, Laurie Hendren, Sascha Kuzins, Jennifer Lhotak, Ondrej Lhotak, Oege de Moor, Damien Sereni, Ganesh Sittampalam, Julian Tibble Oxford University University of Aarhus McGill University PLDI 2005 Presented by Geoff Hulette
What is AspectJ? • Aspect-Oriented Programming (AOP) language extension for Java
What is AOP? • AOP addresses cross-cutting concerns • Examples: logging, debug traces, method-level security • Goal: remove cross-cutting concerns from objects, put them in aspects
What is an Aspect? • Join point: span of code in the execution of a program • Pointcut: query that picks out join points • Advice: code to execute at a join point
Why Optimize? • Dynamic model naïve compilers add considerable overhead • Fortunately, a lot of advice can be compiled statically
Case 1: Around Advice • Executes instead of a join point, but can transfer control back into the join point using proceed() • Problem: how do you know where proceed should take you?
Standard Solution • Closures: pass an object to the advice method with the right code and context • “Inlining”: duplicate the advice method for each join point
ABC’s Solution • Keep proceed method in the original class • Call the advice method with IDs for the originating class and join point • Sort it out with a switch statement
Results • Code is 25% smaller, on average. • Recursive execution times are much faster (3 to 6 times) when recursive advice is applied, about the same otherwise • ABC is insensitive to recursive advice
Case 2: cflow • Cflow(p): picks out join points in the control flow of p • Example pointcut: cflow(call(bar())) && call(foo())
AJC’s Solution • Create a stack for each cflow • Where p becomes true, push context • At join points, test if stack is non-empty
First the easy stuff • Combine cflow stacks:cflow(call(a())) && call(b)||cflow(call(a())) && call(c)cflow(call(a())) && (call(b)||call(c)) • Use counters instead of stacks, if possible • Cache stacks/counters
Then the hard stuff • Goal: classify instructions to make dynamic checks into static ones
MayCflow • Instructions are in mayCflow(sh) if they may be in the scope of update shadow sh • If a query is not in mayCflow, we can replace it with neverMatch
Compute MayFlow • Begin with instructions in an update shadow (between push and pop) • Add all instructions that might be called from within
MustCflow • Instructions are in MustCflow(stack) if that instruction is always in that cflow • Replace queries in set with alwaysMatch
Compute MustFlow • Pre-compute a list of all statements within a cflow that have no dynamic queries • Start with all statements • Eliminate those that can be reached from entry points without passing through the pre-computed list
NecessaryShadows • Set of update shadows whose effects may be observed, and whose effects are not duplicated • Any update shadow not in this set can be eliminated
Compute NecessaryShadows • The set of update shadows whose mayFlow sets contain no dynamic queries • Also remove those that are in the mustflow of another update
Results • Easy stuff led to biggest gains (up to 54x) • Hard stuff helped too, but generally not much
Other Optimizations • Static Joinpoint data • Eliminate boxing/unboxing • Other standard optimizations