First draft of translation document
Nathan Bryant
nathan.bryant at linkshare.com
Fri Jul 2 13:34:00 PDT 2010
Brian Goetz wrote:
> This may not seem like a big deal, but partial application gives the
VM way
> more information -- type information, nullity information, array
bounds
> information -- with which it can perform all sorts of optimizations
(type
> sharpening which leads to better inlining, null check elimination,
array
> bounds check elimination, dead code elimination, etc).
I'm not sure where these optimizations terminate. It seems like they
don't occur outside of lambdas.
For example, in ParallelArray, the things I'd think we'd most want to
optimize are inner loops like these, to make it more likely that
"procedure" can be inlined into leafApply:
final void leafApply(int lo, int hi, Procedure procedure) {
final Object[] a = this.array;
for (int i = lo; i < hi; ++i)
procedure.op(a[i]);
}
Obviously, the VM can't simply inline all the way from the caller of the
HOF through to the worker thread. Scalar replacement doesn't happen; the
"procedure.op" call site is, therefore, virtual and likely megamorphic
for any nontrivial program.
Your description seems to imply that MethodHandle.bind is the
fundamental primitive that enables the desired optimizations; without it
they don't take place; so they don't happen for code written in the
classic OO style.
So unless I'm misreading you, then, because there is currently nothing
that would cause a bind operation /surrounding/ leafApply, those
optimizations won't happen /within/ leafApply, and the programmer might
conceivably have to rewrite the code that calls it along these lines to
force a bind operation to take place:
static final class FJOApply extends FJBase {
final #void(int l, int h) atLeaf; // instead of storing
Procedure here as was previously done
FJOApply(AbstractParallelAnyArray pap, int lo, int hi, FJBase
next,
Procedure procedure) {
super(pap, lo, hi, next);
this.atLeaf = #(int l, int h)( pap.leafApply(l, h,
procedure) ); // have to hope that pap.leafApply also gets inlined, in
this case
}
FJOApply(AbstractParallelAnyArray pap, int lo, int hi, FJBase
next,
#void(int l, int h) atLeaf) {
super(pap, lo, hi, next);
this.atLeaf = atLeaf;
}
FJBase newSubtask(int l, int h, FJBase r) {
return new FJOApply(pap, l, h, r, atLeaf); // instead of
passing Procedure
}
void atLeaf(int l, int h) { // overrides a method in FJBase
atleaf.(l, h);
}
}
It seems advantageous for the library code to perform the binding
operation "#(int l, int h)( pap.leafApply(l, h, procedure) )" as early
as possible, since creating this method handle is likely to be costly.
More information about the lambda-dev
mailing list