LambdaMetafactory requires full privilege access, but doesn't seem to actually restrict functionality

Steven Schlansker stevenschlansker at gmail.com
Wed Jan 19 01:22:45 UTC 2022



> On Jan 13, 2022, at 7:05 AM, Remi Forax <forax at univ-mlv.fr> wrote:
> 
> ----- Original Message -----
>> From: "Steven Schlansker" <stevenschlansker at gmail.com>
>> To: "core-libs-dev" <core-libs-dev at openjdk.java.net>
>> Sent: Wednesday, January 12, 2022 9:56:30 PM
>> Subject: LambdaMetafactory requires full privilege access, but doesn't seem to actually restrict functionality
> 
>> Hi core-libs-dev,
>> 
>> I am maintaining a module for the popular Jackson JSON library that attempts to
>> simplify code-generation code without losing performance.
>> Long ago, it was a huge win to code-generate custom getter / setter / field
>> accessors rather than use core reflection. Now, the gap is closing a lot with
>> MethodHandles, but there still seems to be some benefit.
>> 
>> The previous approach used for code generation relied on the CGLib + ASM
>> libraries, which as I am sure you know leads to horrible-to-maintain code since
>> you essentially write bytecode directly.
> 
> I don't see the issue here, writing bytecodes in not that hard :)

Everything I write could be done in assembly, yet somehow I keep coming back to Java :)

> 
>> Feature development basically stopped because writing out long chains of
>> `visitVarInsn(ASTORE, 3)` and the like scares off most contributors, myself
>> included.
> 
> yes, sadly known issue, generating assembly code even a highlevel one like the Java byetcode by hands require a niche knowledge which sadly is rarely teached at university anymore (and let's not talked about bootcamp).  
> 
>> 
>> As an experiment, I started to port the custom class generation logic to use
>> LambdaMetafactory. The idea is to use the factory to generate `Function<Bean,
>> T>` getter and `BiConsumer<Bean, T>` setter implementations.
> 
> If you want to make the serailization/deserialization to JSON fast you will mostly battle two things,
> - polymorphic call site, a call that can call some many different implementations that the VM will not try to inline,
>   usually the trick is to just have one of such call to take care of the different kind of objects.
> - boxing, that kind of code tends to abstract over any values, but boxing means allocation if there is no inlining.  
> 
> For me, it means that you want two specialized codes, the decoder that switches over the keys and call the correct setter and the encoder that calls the getters in sequence.
> 
> Trying to abstract over getters and setters is a step too far because you loose the benefit to write a code specific to a peculiar class.

Yes, this is a good point. I mostly copied the existing code-gen approach which behaved this way. I should go back and see if I can generate the more full featured coders and decoders.
We do avoid boxing by using primitive specializations for our generated interface types, at least for the common primitive types.

> I suppose you try to use the method handles directly with invokeExact() if you are able to remove the boxing
> or with invoke() if not ?
> 
> Because it's not clear to me why you want to use the LambdaMetafactory ?

At the time it seemed to be significantly faster than non-static MethodHandles, and also faster than old-school reflection.
Ref: https://www.optaplanner.org/blog/2018/01/09/JavaReflectionButMuchFaster.html

I wrote some simple benchmarks that confirmed that it's almost as fast as Jackson's older direct code-gen support:
https://github.com/FasterXML/jackson-modules-base/tree/2.14/blackbird/benchmarks
The results were even better with Graal's compiler (for some benchmarks).

I haven't verified that all of the above is still true as of Java 17+, so my next step is to re-run the performance experiments with a more modern JVM.
I expect there's been many optimizations around MethodHandle.invoke and friends since I ran my benchmark.
Maybe it's the case that there is no longer a performance benefit to using LambdaMetafactory over (non-static) MethodHandle invoke, or if we can convert our code to use invokeExact.

> [...]
> 
>> 
>> I'm also curious for any feedback on the overall approach of using the
>> Metafactory, perhaps I am way off in the weeds, and should just trust
>> MethodHandles to perform well if you use invokeExact :) JMH does seem to show
>> some benefit though especially with Graal compiler.
>> 
>> Thanks a bunch for any thoughts,
>> Steven Schlansker
> 
> Rémi



More information about the core-libs-dev mailing list