----- Mail original ----- > De: "Vladimir Ivanov" <vladimir.x.iva...@oracle.com> > À: "Wenlei Xie" <wenlei....@gmail.com>, "Da Vinci Machine Project" > <mlvm-dev@openjdk.java.net> > Envoyé: Mardi 20 Février 2018 00:14:42 > Objet: Re: Why is LambdaMetafactory 10% slower than a static MethodHandle but > 80% faster than a non-static MethodHandle?
>> 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? > > Yes, method handle invokers are special and ordinary type profiling > (class-based) doesn't work for them. > > There was an idea to implement value profiling for MH invokers: record > individual MethodHandle instances observed at invoker call sites and use > that to guide devirtualizaiton & inlining decisions. But it looked way > too specialized to be beneficial in practice. Here is a code that does exactly that, https://gist.github.com/forax/7bf08669f58804991fd45656a671c381 [...] > Best regards, > Vladimir Ivanov Rémi >> On Mon, Feb 19, 2018 at 4:00 AM, Vladimir Ivanov >> <vladimir.x.iva...@oracle.com <mailto: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 >> <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> > _______________________________________________ > 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