On 2/19/18 11:43 PM, Wenlei Xie wrote:
Never mind. I miss some points in the previous discussion. Static method handle can get further benefit from JIT:

> 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 an orthogonal optimization with MethodHandle customization?

Yes, they are complementary. LambdaForm customization is applied to method handles observed at MH.invokeExact()/invoke() call sites as non-constants (in JIT-compiled code). There won't be any customization applied (at least, at that particular call site) to a method handle coming from a static final field.

Best regards,
Vladimir Ivanov

On Mon, Feb 19, 2018 at 12:36 PM, Wenlei Xie <wenlei....@gmail.com <mailto:wenlei....@gmail.com>> wrote:

    > However, for java framework developers,
    > it would be really useful to have inlining for non-static method handles 
too (see Charles's thread),

    Is the problem that non-static MethodHandle doesn't get customized,
    or it's because in the benchmark, each time it will use a new
    MethodHandle from reflection?

    I remember a MethodHandle will be customized when it was called over
    a threshold (127 is the default). Thus as long as you are using the
    same MethodHandle over the time, you will get the performance
    benefit from customization, right?




    Best,
    Wenlei


    On Mon, Feb 19, 2018 at 5:41 AM, Geoffrey De Smet
    <ge0ffrey.s...@gmail.com <mailto:ge0ffrey.s...@gmail.com>> wrote:

        Thank you for the insight, Vladimir.

            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.

        Ah, so it's unlikely that a future JDK version could eliminate
        that 10% difference between LambdaMetafactory and
        staticMethodHandle?

        Good to know.

            But comparing that with nonStaticMethodHandle is not fair:
            there's no inlining happening there.

        Agreed.

        However, for java framework developers,
        it would be really useful to have inlining for non-static method
        handles too (see Charles's thread),
        because - unlike JVM language developers - we can't use static
        method handles and don't want to use code generation.

        For example, if a JPA or JAXB implementation did use a static
        fields,
        the code to call methods on a domain hierarchy of classes would
        look like this:

        public final class MyAccessors {

             private static final MethodHandle handle1; // Person.getName()
             private static final MethodHandle handle2; // Person.getAge()
             private static final MethodHandle handle3; // Company.getName()
             private static final MethodHandle handle4; //
        Company.getAddress()
             private static final MethodHandle handle5; // ...
             private static final MethodHandle handle6;
             private static final MethodHandle handle7;
             private static final MethodHandle handle8;
             private static final MethodHandle handle9;
             ...
             private static final MethodHandle handle1000;

        }

        And furthermore, it would break down with domain hierarchies
        that have more than 1000 getters/setters.


        With kind regards,
        Geoffrey De Smet

        On 19/02/18 13:00, Vladimir Ivanov 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
            <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.Be
                <http://org.openjdk.jmh.annotations.Be>nchmark;
                import org.openjdk.jmh.annotations.Be
                <http://org.openjdk.jmh.annotations.Be>nchmarkMode;
                import org.openjdk.jmh.annotations.Fo
                <http://org.openjdk.jmh.annotations.Fo>rk;
                import org.openjdk.jmh.annotations.Me
                <http://org.openjdk.jmh.annotations.Me>asurement;
                import org.openjdk.jmh.annotations.Mo
                <http://org.openjdk.jmh.annotations.Mo>de;
                import org.openjdk.jmh.annotations.OutputTimeUnit;
                import org.openjdk.jmh.annotations.Sc
                <http://org.openjdk.jmh.annotations.Sc>ope;
                import org.openjdk.jmh.annotations.St
                <http://org.openjdk.jmh.annotations.St>ate;
                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 <http://this.name> = name;
                          }

                          public String getName() {
                              return name;
                          }

                      }

                }


                With kind regards,
                Geoffrey De Smet

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


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




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

    Email: wenlei....@gmail.com <mailto:wenlei....@gmail.com>




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

Email: wenlei....@gmail.com <mailto: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