groovy-users mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From "Winnebeck, Jason" <Jason.Winneb...@windstream.com>
Subject RE: JDK8 Streams / Closure cast to interface
Date Tue, 22 Nov 2016 18:37:08 GMT
I was referring to a compile-time generation of the class -- that the Closure itself that is
normally generated implements the interface natively. That would make it equivalent to anonymous
class in Java 7 and earlier for calling functional (or any SAM type) methods. That wouldn't
have any problems on Android, and should be as efficient as Java without lambdas? I would
assume the interface's method delegating to doCall would get inlined. In other words, Groovy
generating code like:

class X {
	public static void main(String[] args) {
		Stream.of(1).forEach(new x__closure1(X.class, X.class));
	}

	private static class x__closure1 extends Closure<Void> implements Consumer<Integer>
{
		public x__closure1(Object owner, Object thisObject) {
			super(owner, thisObject);
		}

		void doCall(Integer x) {
			System.out.println(x);
		}

		@Override
		public void accept(Integer x) {
			doCall(x);
		}
	}
}

>From Groovy: Stream.of(1).forEach { println it }

The new part being that Groovy added the accept method and implements to the closure it already
normally would have generated, and castToType would not need to be called. All of the code
manipulation is done at compile-time so it is fully STC and Android compatible, and no reflection
is in use. You still have the a little more overhead of Closure object compared to Java static
inner class, but I imagine this must be a lot less than proxy, but still allows Closure to
use the owner/delegate patterns that Groovy is known for, and I assume would not affect backwards
compatibility as superclass stays Closure.

Of course, if it were possible for compiler to determine that the closure is never using owner,
delegate, or "thisObject", then it could be possible to drop the "extends Closure" entirely
if it can be proven that the "closureness" of the object can never be observed. But that's
likely not possible as any method taking an interface could choose to check for instanceof
Closure and/or cast or do something special if Groovy closure is passed in -- although is
that even possible today since Groovy actually passes in a proxy?

Jason

-----Original Message-----
From: Jochen Theodorou [mailto:blackdrag@gmx.org] 
Sent: Tuesday, November 22, 2016 12:01 PM
To: users@groovy.apache.org
Subject: Re: JDK8 Streams / Closure cast to interface



On 22.11.2016 15:14, Winnebeck, Jason wrote:
> I love Groovy. I also love the new streams functionality in JDK 8. 
> But, I am weary of the performance implications of Groovy + Streams, 
> because to use streams you must use Groovy closures. I see the code 
> generated creates a new closure instance then uses castToType to cast 
> the closure to the JDK8 functionality interface. Does Groovy make this 
> efficient, or is the proxy generation from this going to be excessive 
> if running a lot of small stream operations?

it is using a dynamic proxy with some optimized code paths.. such a proxy does for example
not have to go through call to get to doCall. If you want this really efficient, you have
to skip the generation of the Groovy Closure instance. This is doable and I plan to do this,
but it will be a breaking change (all open blocks would be realized using the same class for
example, having that instance would even become optional).

> I think an interesting feature of Groovy would be if it sees a closure 
> cast implicitly or explicitly to a certain interface type, it could 
> make the closure implement the interface.  You'd still have the 
> overhead of compile-time class generation versus lambdas, but at least 
> you wouldn't have to create proxies, and maybe there is an improved 
> chance of JIT inlining? Even if it was supported within a single 
> statement, like "Function<String, String> x = {it.trim()}" or 
> Stream.of("abc").map {
> it.trim() }, the closure with trim could implement Function. Of course 
> for backwards compatibility the class could still extend Closure and 
> still implement call methods, but also implement the method apply, 
> which delegates to call.

The price question is now... is that going to be cheaper? runtime class generation is much
more expensive than a reflective call - and considering Android, also much more problematic.
And you have to be aware of the following... If I make a method call with an open block to
a method taking a functional interface, the point in time for the conversion is after runtime
method selection and before the actual invocation (or during invocation). At this point the
original Closure instance already exists. Really, without test it is difficult to tell about
the gain here

Real gains you get only with a much deeper integration

bye Jochen

This email message and any attachments are for the sole use of the intended recipient(s).
Any unauthorized review, use, disclosure or distribution is prohibited. If you are not the
intended recipient, please contact the sender by reply email and destroy all copies of the
original message and any attachments.

Mime
View raw message