Hi Paul,
I think we have to separate memory consumption and performance a bit here. Of course allocating in a loop costs performance compared to not allocating. Creating new objects is not exactly cheap on the JVM, so here the two touch for this case.
For example the case of performance cost for CurriedClosure. Yes, we rewrap the array and thus we create a ton of useless Objects. But do we really have to? I think not. Nothing except manpower, time and motivation stops us from collapsing CurriedClosures. This can partially happen at construction time, partially this can happen at the callsite itself. And interestingly, we probably could even save on some guards.. Actually there is some space for optimization in general. But this does not solve the problem of if you create the CurriedClosure inside the hot loop. Then you still carry the cost of creating lots of CurriedClosure objects.
Quite some time ago I was toying with the idea of having a special non-capturing Closure, that is incompatible with the capturing Closure. Why? then for example a builder can make it clear by signature that it will require a delegate, or something like findAll can make it clear, that there will be no use of delegation. Well, that was an idea, did not get very far with it. I feel there is quite a bit optimization potential here as well - similar to non capturing lambdas. But if you then go and pack everything in a Closure as it is today, it won't got far I think.
Which is also why this works with lambdas in static compilation, but not in dynamic Groovy... unless we fundamentally change the way these things are done. For example in "words.findAll(String::startsWith, 'A')", we could in theory just have a reusable wrapper for a MethodHandle, even create the wrapper at the callsite only (and keep it there) to tailor it to what we require for the method we call it with... hmm... that is similar to what is done for lambdas I think... just not as dynamic.
Then there is the aspect of "looks". findAll with 2 parameters looks a bit odd on first look. But maybe that is something getting used too?
bye Jochen On 5/23/26 14:53, Paul King wrote:
Hi folks, Donald Raab of Eclipse Collections fame, did a recent blog post about what that library has done for better performance: https://donraab.medium.com/fat-free-lambdas-in-java-bf228da0613b Donald sent me a private message asking about Groovy's take on some of those optimisations, so I thought it might be interesting to analyse things. Past optimisation work, and the recent work Daniel did on optimizing non-capturing lambdas put Groovy on a good footing for some metrics but for many of our DGM methods that take Closures, we have ways (with varying degrees of impact) to make things more performant. I did a benchmark and the results are in: https://issues.apache.org/jira/browse/GROOVY-12034 There is a PR but currently the PR is just the benchmark tests themselves, not any code changes at present: https://github.com/apache/groovy/pull/2557 I'd encourage folks to read that ticket, we have a few options we might want to explore. TL;DR * there are a few potential optimsations we might be able to apply for existing code * we might want to consider a curryWith method, kind of a lazy rcurry * we might want to consider the equivalent of Eclipse Collections "*With" method variants. Instead of "words.findAll{ it.startsWith('A') }" we'd have a variant "words.findAll(String::startsWith, 'A') Anyway, I'll ponder things a bit more and make some follow-up PRs/emails. The analysis suggests just creating the extra variants on a handful of the most widely used DGM methods. But feedback is most welcome. Cheers, Paul.
