Why is LambdaMetafactory 10% slower than a static MethodHandle but 80% faster than a non-static MethodHandle?
Wenlei Xie
wenlei.xie at gmail.com
Mon Feb 19 22:54:56 UTC 2018
Thank you Vladimir for the explanation!
> 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.
Sorry if it's a dumb question, but why nonStaticMethodHandle cannot get
inlined here? -- In the benchmark it's always the same line with the same
final MethodHandle variable, can JIT based on some profiling info to inline
it (similar to the function object generated by LambdaMetafactory). -- Or
it cannot sine InvokeExact's PolymorphicSignature makes it quite special?
Also, does that mean if we try to pollute the LambdaMetafactory (e.g. by 3
different function objects) to prevent inline, we are likely to see similar
performance :)
Best,
Wenlei
On Mon, Feb 19, 2018 at 4:00 AM, Vladimir Ivanov <
vladimir.x.ivanov at oracle.com> wrote:
> Geoffrey,
>
> 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.
>
> If you want a fair comparison, then you have to measure with polluted
> profile so no inlining happens. In that case [3] non-static MethodHandles
> are on par (or even slightly faster):
>
> LMF._4_lmf_fs avgt 10 20.020 ± 0.635 ns/op
> LMF._4_lmf_mhs avgt 10 18.360 ± 0.181 ns/op
>
> (scores for 3 invocations in a row.)
>
> Best regards,
> Vladimir Ivanov
>
> [1] 715 126 b org.lmf.LMF::_1_staticMethodHandle (11 bytes)
> ...
> @ 37 java.lang.invoke.DirectMethodHandle$Holder::invokeVirtual (14
> bytes) force inline by annotation
> @ 1 java.lang.invoke.DirectMethodHandle::internalMemberName (8
> bytes) force inline by annotation
> @ 10 org.lmf.LMF$Dog::getName (5 bytes) accessor
>
>
>
>
> [2] 678 117 b org.lmf.LMF::_2_lambdaMetafactory (14 bytes)
> @ 8 org.lmf.LMF$$Lambda$37/552160541::apply (8 bytes) inline (hot)
> \-> TypeProfile (6700/6700 counts) = org/lmf/LMF$$Lambda$37
> @ 4 org.lmf.LMF$Dog::getName (5 bytes) accessor
>
>
> [3] http://cr.openjdk.java.net/~vlivanov/misc/LMF.java
>
> static Function make() throws Throwable {
> CallSite site = LambdaMetafactory.metafactory(LOOKUP,
> "apply",
> MethodType.methodType(Function.class),
> MethodType.methodType(Object.class, Object.class),
> LOOKUP.findVirtual(Dog.class, "getName",
> MethodType.methodType(String.class)),
> MethodType.methodType(String.class, Dog.class));
> return (Function) site.getTarget().invokeExact();
> }
>
> private Function[] fs = new Function[] {
> make(), make(), make()
> };
>
> private MethodHandle[] mhs = new MethodHandle[] {
> nonStaticMethodHandle,
> nonStaticMethodHandle,
> nonStaticMethodHandle
> };
>
> @Benchmark
> public Object _4_lmf_fs() throws Throwable {
> Object r = null;
> for (Function f : fs {
> r = f.apply(dogObject);
> }
> return r;
> }
>
> @Benchmark
> public Object _4_lmf_mh() throws Throwable {
> Object r = null;
> for (MethodHandle mh : mhs) {
> r = mh.invokeExact(dogObject);
> }
> return r;
>
> }
>
> On 2/19/18 1:42 PM, Geoffrey De Smet wrote:
>
>> Hi guys,
>>
>> I ran the following JMH benchmark on JDK 9 and JDK 8.
>> Source code and detailed results below.
>>
>> Benchmark on JDK 9 Score
>> staticMethodHandle 2.770
>> lambdaMetafactory 3.052 // 10% slower
>> nonStaticMethodHandle 5.250 // 90% slower
>>
>> Why is LambdaMetafactory 10% slower than a static MethodHandle
>> but 80% faster than a non-static MethodHandle?
>>
>>
>> Source code (copy paste ready)
>> ====================
>>
>> import java.lang.invoke.CallSite;
>> import java.lang.invoke.LambdaMetafactory;
>> import java.lang.invoke.MethodHandle;
>> import java.lang.invoke.MethodHandles;
>> import java.lang.invoke.MethodType;
>> import java.util.concurrent.TimeUnit;
>> import java.util.function.Function;
>>
>> import org.openjdk.jmh.annotations.Benchmark;
>> import org.openjdk.jmh.annotations.BenchmarkMode;
>> import org.openjdk.jmh.annotations.Fork;
>> import org.openjdk.jmh.annotations.Measurement;
>> import org.openjdk.jmh.annotations.Mode;
>> import org.openjdk.jmh.annotations.OutputTimeUnit;
>> import org.openjdk.jmh.annotations.Scope;
>> import org.openjdk.jmh.annotations.State;
>> import org.openjdk.jmh.annotations.Warmup;
>>
>> //Benchmark on JDK 9 Mode Cnt Score Error Units
>> //staticMethodHandle avgt 30 2.770 ± 0.023 ns/op // Baseline
>> //lambdaMetafactory avgt 30 3.052 ± 0.004 ns/op // 10% slower
>> //nonStaticMethodHandle avgt 30 5.250 ± 0.137 ns/op // 90% slower
>>
>> //Benchmark on JDK 8 Mode Cnt Score Error Units
>> //staticMethodHandle avgt 30 2.772 ± 0.022 ns/op // Baseline
>> //lambdaMetafactory avgt 30 3.060 ± 0.007 ns/op // 10% slower
>> //nonStaticMethodHandle avgt 30 5.037 ± 0.022 ns/op // 81% slower
>>
>> @Warmup(iterations = 5, time = 1, timeUnit = TimeUnit.SECONDS)
>> @Measurement(iterations = 10, time = 1, timeUnit = TimeUnit.SECONDS)
>> @Fork(3)
>> @BenchmarkMode(Mode.AverageTime)
>> @OutputTimeUnit(TimeUnit.NANOSECONDS)
>> @State(Scope.Thread)
>> public class LamdaMetafactoryWeirdPerformance {
>>
>> // ************************************************************
>> ************
>> // Set up of the 3 approaches.
>> // ************************************************************
>> ************
>>
>> // Unusable for Java framework developers. Only usable by JVM
>> language developers. Baseline.
>> private static final MethodHandle staticMethodHandle;
>>
>> // Usuable for Java framework developers. 30% slower
>> private final Function lambdaMetafactoryFunction;
>>
>> // Usuable for Java framework developers. 100% slower
>> private final MethodHandle nonStaticMethodHandle;
>>
>> static {
>> // Static MethodHandle setup
>> try {
>> staticMethodHandle = MethodHandles.lookup()
>> .findVirtual(Dog.class, "getName",
>> MethodType.methodType(String.class))
>> .asType(MethodType.methodType(Object.class,
>> Object.class));
>> } catch (NoSuchMethodException | IllegalAccessException e) {
>> throw new IllegalStateException(e);
>> }
>> }
>>
>> public LamdaMetafactoryWeirdPerformance() {
>> try {
>> MethodHandles.Lookup lookup = MethodHandles.lookup();
>>
>> // LambdaMetafactory setup
>> CallSite site = LambdaMetafactory.metafactory(lookup,
>> "apply",
>> MethodType.methodType(Function.class),
>> MethodType.methodType(Object.class, Object.class),
>> lookup.findVirtual(Dog.class, "getName",
>> MethodType.methodType(String.class)),
>> MethodType.methodType(String.class, Dog.class));
>> lambdaMetafactoryFunction = (Function)
>> site.getTarget().invokeExact();
>>
>> // Non-static MethodHandle setup
>> nonStaticMethodHandle = lookup
>> .findVirtual(Dog.class, "getName",
>> MethodType.methodType(String.class))
>> .asType(MethodType.methodType(Object.class,
>> Object.class));
>> } catch (Throwable e) {
>> throw new IllegalStateException(e);
>> }
>> }
>>
>> // ************************************************************
>> ************
>> // Benchmark
>> // ************************************************************
>> ************
>>
>> private Object dogObject = new Dog("Fido");
>>
>>
>> @Benchmark
>> public Object _1_staticMethodHandle() throws Throwable {
>> return staticMethodHandle.invokeExact(dogObject);
>> }
>>
>> @Benchmark
>> public Object _2_lambdaMetafactory() {
>> return lambdaMetafactoryFunction.apply(dogObject);
>> }
>>
>> @Benchmark
>> public Object _3_nonStaticMethodHandle() throws Throwable {
>> return nonStaticMethodHandle.invokeExact(dogObject);
>> }
>>
>> private static class Dog {
>> private String name;
>>
>> public Dog(String name) {
>> this.name = name;
>> }
>>
>> public String getName() {
>> return name;
>> }
>>
>> }
>>
>> }
>>
>>
>> With kind regards,
>> Geoffrey De Smet
>>
>> _______________________________________________
>> mlvm-dev mailing list
>> mlvm-dev at openjdk.java.net
>> http://mail.openjdk.java.net/mailman/listinfo/mlvm-dev
>>
> _______________________________________________
> mlvm-dev mailing list
> mlvm-dev at openjdk.java.net
> http://mail.openjdk.java.net/mailman/listinfo/mlvm-dev
>
--
Best Regards,
Wenlei Xie (谢文磊)
Email: wenlei.xie at gmail.com
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://mail.openjdk.java.net/pipermail/mlvm-dev/attachments/20180219/1441b275/attachment-0001.html>
More information about the mlvm-dev
mailing list