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 :)
As far as I can tell, I see a similar performance
for this benchmark uses a megamorphic approach:
  https://github.com/ge0ffrey/ge0ffrey-presentations/blob/master/code/fasterreflection/fasterreflection-client/src/main/java/be/ge0ffrey/presentations/fasterreflection/client/MegamorphicFasterReflectionClientBenchmark.java#L40

Result:

Benchmark                                                          Mode  Cnt   Score   Error  Units
MegamorphicFasterReflectionClientBenchmark._200_MethodHandle       avgt   60  17.507 ± 0.281  ns/op // Non-static MethodHandle, still seriously slower
MegamorphicFasterReflectionClientBenchmark._400_LambdaMetafactory  avgt   60  14.393 ± 0.275  ns/op

With kind regards,
Geoffrey De Smet

On 19/02/18 23:54, Wenlei Xie wrote:
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.iva...@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@openjdk.java.net
http://mail.openjdk.java.net/mailman/listinfo/mlvm-dev
_______________________________________________
mlvm-dev mailing list
mlvm-dev@openjdk.java.net
http://mail.openjdk.java.net/mailman/listinfo/mlvm-dev



--
Best Regards,
Wenlei Xie (谢文磊)

Email: wenlei....@gmail.com

_______________________________________________
mlvm-dev mailing list
mlvm-dev@openjdk.java.net
http://mail.openjdk.java.net/mailman/listinfo/mlvm-dev

Reply via email to