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? Best, Wenlei On Mon, Feb 19, 2018 at 12:36 PM, Wenlei Xie <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 > > 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 >>> >>> 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 > -- 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