MethodHandle vs function types
Peter Levart
peter.levart at marand.si
Mon Feb 22 02:14:45 PST 2010
On Monday 22 February 2010 07:25:31 Neal Gafter wrote:
> When function types are represented as generic interfaces, as in BGGA, the
> generated implementation must cast each reference argument from Object (the
> type of the argument in the function's erased type) to the declared type of
> the lambda argument. For example, in the lambda expression
>
> #(Integer i) (i)
>
> the bytecode for the body of this lambda must do something like this
>
> #(Object _i) {
> Integer i = (Integer)_i; // copy the erased parameter to a variable with
> the proper type
> return i;
> }
>
> Similarly, values received from a lambda invocation must be cast by the
> caller to the return type of the function. For example, this
>
> #Integer() function = ...;
> Integer x = function.();
>
> would be translated in the generated bytecode to something like this
>
> #Object() function = ...;
> Integer x = (Integer)function.();
>
> These translations are described in the JLS and were implemented in JDK5 as
> part of the "erasure" scheme for generics.
>
> Some people are displeased about these casts (and their performance impact)
> remaining in the code, and would like the implementation of project lambda
> to avoid them. One approach that has been suggested is erasing function
> types to MethodHandle instead of interfaces. Unfortunately, MethodHandle
> does not implement the subtyping scheme for values of function type. That
> necessitates one of three approaches. Either (1) code is used to implement
> the subtype conversion, or (2) the same erasure scheme that would have been
> used with interfaces be used with MethodHandle (the method handle uses
> Object in place of all reference types), or (3) the MethodHandle primitive
> be relaxed to support the function subtying scheme.
>
> Approach (1) doesn't work for various reasons. As we've already seen by the
> failure of Howard Lovatt's attempt to reify function types, we must preserve
> reference identity for all widening reference conversions.
>
> Approach (2) retains the very disadvantage that MethodHandle was meant to
> solve. Of course, MethodHandle may come with other advantages that make it
> still worth using.
Yes, the problem, as I see it, is that code must invoke MethodHandle with exact types of arguments and return type which reflect the types of arguments/return type of the method that it delegates to. Those types can not always be known for every call site at compile time, so code would have to "adapt" the method handle at every call site. The question is what is the overhead of such adaptation. Considering the semantics of for example MethodHandles.convertArguments(MethodHandle, MethodType) that could do the adaptation, I imagine the overhead be at least the overhead of casting each argument and return value (as per generics approach).
The other overhead that should be compared is the overhead of capturing free variables. The MethodHandle approach, I imagine, is to be using MethodHandles.insertArgument(MethodHandle, int, Object) semantics for each captured reference (primitives have to be boxed?) or a special capturing context object would have to be constructed for each lambda which negates the purpose of MethodHandles not having to generate specialized classes for each lambda. One could argue that lambda construction overhead is not so important as each lambda is potentially executed multiple times, but I imagine that capturing many values via "inserArgument" constructs a "chain" of method handles which at invocation time delegate to each other. I may be wrong but is this delegation without overhead?
>
> Approach (3) is outside the scope of project lambda to address (it is an
> issue for jsr292), but I think it would be a very good idea for us to
> encourage them to work on it.
I agree and I think they are already very busy tackling those problems.
>
> Cheers,
> Neal
>
>
Regards, Peter
More information about the lambda-dev
mailing list