Current State of Closures
Fredrik Ohrstrom
oehrstroem at gmail.com
Thu Jul 29 07:56:16 PDT 2010
I do not think that generic removal of unboxing is going to be the way to
solve every problem in the world. The main problem is that you cannot
always inline.
The methodhandle.invokeGeneric however uses boxing/unboxing in a
somewhat more controlled manner to make boxing/unboxing invisible only
at call sites. Not for anything else, like storage et al.
It is possible to make invokeGeneric work very efficiently when the target
is compatible without boxing ie it is co/contra variant. In fact it can be
as fast as an invokeinterface call, or sometimes faster.
Thus if function types are added, these will really help you to control
that the callsite and the target are co/contra variant and you will get
high speed calls. With a nice backup if you really want to express
generic functions that swap two arguments. When you swap primitives
this will require boxing, so when you do that, it is up to you, to use
that advanced feature in a setting that can be easily inlined.
Also the function types can be the standard generics where Integer
is used instead of int. At the callsite however, each Integer is dropped
to int. This means that the specification uses Generics but the runtime
uses primitives and because of invokeGeneric, this will simply work.
I did a presentation of this at the JVM language summit, you can find
the slides here:
http://wiki.jvmlangsummit.com/Efficient_compilation_of_Lambdas_using_MethodHandles_and_JRockit
The video will probably be available somewhere in the near future.
//Fredrik
2010/7/29 Reinier Zwitserloot <reinier at zwitserloot.com>:
> I'm guessing the comments referred to are the ones that basically go:
>
> if we don't solve this primitives-in-generics problem in some way or another
> when closures are released, solving the problem becomes a lot harder than it
> already is, because of backwards incompatibility problems. If my PinG
> proposal is deemed unsuitable because eliminating back-to-back box/unbox
> operations across method boundaries is impossible (something I rather doubt,
> even if current JVMs aren't doing a good job), then we should look for
> another solution.
>
> Also, the things the primitives in generics proposal tries to fix aren't
> just the performance problems of autoboxing. On the contrary, in fact.
>
> Autoboxing only works for _objects_. It doesn't work for types. You can't
> sort a list of Integers using: Collections.sort(integerList, #(int a, int b)
> { .... }). You can't even write a method that can sort an int[] using
> java.util.Comparator, and as adding a java.util.IntComparator was evidently
> deemed as too silly or too dangerous, there is in fact no method in the
> runtime api to sort a list of ints using a custom ordering, which is rather
> strange when you think about it. With PinG, you could do all these things -
> you can make a List<int>, even if that is really sugar for a List<Integer>,
> and then sort it with #(int a, int b) { ... }. You could easily write an API
> method that takes an int[] and sorts it using a Comparator<int>. You can
> even make a fast implementation of an integer list and access it (still
> fast) via a reference of the List<int> type, though this part of it depends
> on a JVM that is capable of eliminating back-to-back box/unbox across method
> boundaries.
>
> Perhaps more to the point, you can introduce
> DoubleParallelArray.reduce(Reducer<double>) and avoid creating the 100+ SAMs
> in Ops, which for reasons that have been stated before, if we do introduce
> them now, can never go away *AND* prevent ever adding
> reduce(Reducer<double>) to DoubleParallelArray in the future.
>
> --Reinier Zwitserloot
>
>
>
> On Thu, Jul 29, 2010 at 1:00 AM, Nathan Bryant
> <nathan.bryant at linkshare.com>wrote:
>
>>
>> Regarding implementing primitives in generics as basically syntactic
>> sugar for the boxed types:
>>
>> This doesn't solve much of anything that has not been already addressed
>> by autoboxing.
>>
>> More, the motivating use case for primitives-in-generics is to avoid the
>> runtime overhead of autoboxing, and the rationale for Reinier's proposal
>> is that it's OK to rely on VM optimizations for this. But the tests I've
>> performed on JRockit, JDK6 and JDK7 indicate pretty clearly that
>> autoboxing-elimination does not work as well as primitive types (simple
>> operations can still be an order of magnitude slower, even in the
>> sequential case which is much easier for the VM to inline.) Simply put,
>> I can't get the optimization to work at all, for some pretty simple
>> cases.
>>
>> -----Original Message-----
>> From: lambda-dev-bounces at openjdk.java.net
>> [mailto:lambda-dev-bounces at openjdk.java.net] On Behalf Of Josh Stone
>> Sent: Wednesday, July 28, 2010 6:45 PM
>> To: lambda-dev at openjdk.java.net
>> Subject: Re: Current State of Closures
>>
>> I assume this is why you haven't addressed Reinier's comments in the
>> "Revisiting primitives in generics" thread, among others, but I do
>> believe
>> they warrant a response.
>>
>> Josh
>>
>> Yes, you're seeing denial in action.
>> >
>> > On Jul 28, 2010, at 2:37 PM, Reinier Zwitserloot wrote:
>> >
>> > > I keep seeing #returnType(paramTypes) mentioned, but doesn't SotL
>> > eliminate
>> > > those entirely?
>> > >
>> > > --Reinier Zwitserloot
>> > >
>> > >
>> > >
>> > > On Tue, Jul 27, 2010 at 12:34 PM, Talden <talden at gmail.com>
>> wrote:
>> > >
>> > >> On Tue, Jul 27, 2010 at 8:44 PM, Peter Levart <peter.levart at
>> > marand.si>
>> > >> wrote:
>> > >>> I didn't see that mentioned in the drafts. But with current
>> prototype
>> > and
>> > >> it's syntax using target typing with inferal of lambda's argument
>> types,
>> > the
>> > >> alternative is not so much longer and is more general, since you
>> have
>> > the
>> > >> control over argument positions:
>> > >>>
>> > >>> public class TestClosures
>> > >>> {
>> > >>> public static #Integer(Integer) partial(final #Integer(Integer,
>> > Integer)
>> > >> func, final int arg1)
>> > >>> {
>> > >>> return #(arg2){ func.(arg1, arg2) };
>> > >>> }
>> > >>>
>> > >>> public static void main(String[] args)
>> > >>> {
>> > >>> #Integer(Integer,Integer) pow = #(x, y){x * y};
>> > >>> #Integer(Integer) part = partial(pow, 2);
>> > >>> System.out.println(part.(2));
>> > >>> }
>> > >>> }
>> > >>
>> > >> I assume it would be possible to provide a nearly equivalent
>> > >> genericised form (only nearly because you can't generically involve
>> > >> the primitive).
>> > >>
>> > >> Something like this?
>> > >>
>> > >> public static <X, Y, Z> #Z(Y) curryFirst(final #Z(X, Y) func, final
>> X
>> > arg1)
>> > >> {
>> > >> return #(arg2) { func.(arg1, arg2) };
>> > >> }
>> > >>
>> > >> Such that you could say
>> > >>
>> > >> #Integer(Integer, Integer) adder = #(x, y) { x + y };
>> > >> #Integer(Integer) plus10 = curryFirst(adder, 10);
>> > >>
>> > >> Do I have that right?
>> > >>
>> > >> Are these lambda type declarations still valid under the current
>> state
>> > >> of the lambda or are SAMs required instead now? I thought I saw
>> > >> something about these declarations not being supported and the
>> removal
>> > >> of the "block.(...)" notation as well. A shame if that's the case.
>> > >>
>> > >> --
>> > >> Aaron Scott-Boddendijk
>> > >>
>> > >>
>> > >
>> >
>> >
>>
>>
>>
>
>
More information about the lambda-dev
mailing list