Why is LambdaMetafactory 10% slower than a static MethodHandle but 80% faster than a non-static MethodHandle?
Vladimir Ivanov
vladimir.x.ivanov at oracle.com
Mon Feb 19 13:31:42 UTC 2018
>> In both staticMethodHandle & lambdaMetafactory Dog::getName is
>> inlined, but using different mechanisms.
>>
>> In staticMethodHandle target method is statically known [1], but in
>> case of lambdaMetafactory [2] compiler has to rely on profiling info
>> to devirtualize Function::apply(). The latter requires exact type
>> check on the receiver at runtime and that explains the difference you
>> are seeing.
>>
>> But comparing that with nonStaticMethodHandle is not fair: there's no
>> inlining happening there.
>
> I actually never dared to ask, what kind of information is really
> provided by the java compiler here to make the static version so fast?
Java compiler doesn't do anything special in that case. All the "magic"
happens during JIT-compilation: JIT-compiler extracts method handle
instance from static final field (as if it were a constant from class
constant pool) and inlines through MH.invokeExact() down to the target
method.
> Is it because the static final version becomes a member of the class
> pool? Is the lambdafactory so fast, because here the handle will become
> the member of the pool of the generated class? And is there a way for me
In that particular case, no method handles are involved.
LambdaMetafactory produces a class file w/o any method handle constants.
The target method is directly referenced from bytecode [1].
> to bring nonStaticMethodHandle more near to staticMethodHandle, short of
> making it static?
CallSites are the best you can get (JITs treat CallSite.target as
constant and aggressively inlines through them), but you have to bind
CallSite instance either to invokedynamic call site or put it into
static final field.
If such scheme doesn't work for you, there's no way to match the
performance of invocations on constant method handles.
The best thing you can do is to wrap method handle constant into a newly
created class (put it into constant pool or static final field) and
define a method which invokes the method handle constant (both indy &
MH.invokeExact() work). The method should either implement a method from
super-interface or overrides a method from a super-class (so there's a
way to directly reference it at use sites). The latter is preferable,
because invokevirtual is faster than invokeinterface. (LambdaMetafactory
does the former and that's the reason it can't beat MH.invokeExact() on
non-constant MH).
Best regards,
Vladimir Ivanov
[1]
final class org.lmf.LMF$$Lambda$37 implements java.util.function.Function
...
Constant pool:
...
#19 = Methodref #15.#18 //
org/lmf/LMF$Dog.getName:()Ljava/lang/String;
...
public java.lang.Object apply(java.lang.Object);
descriptor: (Ljava/lang/Object;)Ljava/lang/Object;
flags: (0x0001) ACC_PUBLIC
Code:
stack=1, locals=2, args_size=2
0: aload_1
1: checkcast #15 // class org/lmf/LMF$Dog
4: invokevirtual #19 // Method
org/lmf/LMF$Dog.getName:()Ljava/lang/String;
7: areturn
More information about the mlvm-dev
mailing list