On Fri, 20 Nov 2020 10:55:21 GMT, Peter Levart <plev...@openjdk.org> wrote:
>> The trick is that if we know that a class like java.lang.invoke.Proxy may >> exist, >> it means that instead of distorting the j.l.r.Proxy API to increase of few >> percents the performance when calling a default method, you can come with a >> simpler design in term of API that just add an API point to call a default >> method. >> >> Better performance becoming on the the goals of java.lang.invoke.Proxy. > > Hi Mandy, > I re-ran the benchmark on your latest version (the static API) and I get > similar results as you: > > Benchmark Mode Cnt Score Error Units > ProxyBench.implClass avgt 5 3.745 ± 0.033 ns/op > ProxyBench.implProxy avgt 5 29.826 ± 0.183 ns/op > ProxyBench.ppImplClass avgt 5 3.683 ± 0.009 ns/op > ProxyBench.ppImplProxy avgt 5 29.124 ± 0.535 ns/op > > I also tried a variant where the access check in the invokeDefault static > method is not pre-screened with checking of the interface modifiers and > package export status but relies on Method.checkAccess cache alone: > > // access check > Class<?> caller = Reflection.getCallerClass(); > int modifiers = method.getModifiers(); > method.checkAccess(caller, intf, proxyClass, modifiers); > > ... and the results are not worse, even marginally better: > > Benchmark Mode Cnt Score Error Units > ProxyBench.implClass avgt 5 3.724 ± 0.012 ns/op > ProxyBench.implProxy avgt 5 29.138 ± 0.271 ns/op > ProxyBench.ppImplClass avgt 5 3.744 ± 0.009 ns/op > ProxyBench.ppImplProxy avgt 5 28.961 ± 0.182 ns/op > > I think this looks reasonably good. In my previous attempts when I was modifying the ProxyGenerator I noticed that generated proxy fields holding Method objects are just "static" but could be "static final". If they were "static final", JIT could constant-fold the Method instances. If this was combined with marking some fields in Method as @Stable, this could improve optimization of code a bit further. I did this with the following patch: Index: src/java.base/share/classes/java/lang/reflect/InvocationHandler.java IDEA additional info: Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP <+>UTF-8 =================================================================== --- src/java.base/share/classes/java/lang/reflect/InvocationHandler.java (revision 8352a20bf54a085a97d3f703b5dab482d3f9eccc) +++ src/java.base/share/classes/java/lang/reflect/InvocationHandler.java (date 1605869089804) @@ -271,15 +271,10 @@ throw new IllegalArgumentException(""" + method + "" is not a default method"); } Class<?> intf = method.getDeclaringClass(); - // access check if it is a non-public proxy interface or not unconditionally exported - if (!Modifier.isPublic(intf.getModifiers()) || - !intf.getModule().isExported(intf.getPackageName())) { - // throw IAE if the caller class has no access to the default method - // same access check to Method::invoke on the default method - int modifiers = method.getModifiers(); - Class<?> caller = Reflection.getCallerClass(); - method.checkAccess(caller, intf, proxyClass, modifiers); - } + // access check + Class<?> caller = Reflection.getCallerClass(); + int modifiers = method.getModifiers(); + method.checkAccess(caller, intf, proxyClass, modifiers); MethodHandle mh = Proxy.defaultMethodHandle(proxyClass, method); // invoke the super method Index: src/java.base/share/classes/java/lang/reflect/Method.java IDEA additional info: Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP <+>UTF-8 =================================================================== --- src/java.base/share/classes/java/lang/reflect/Method.java (revision 8352a20bf54a085a97d3f703b5dab482d3f9eccc) +++ src/java.base/share/classes/java/lang/reflect/Method.java (date 1605870445135) @@ -31,6 +31,7 @@ import jdk.internal.reflect.Reflection; import jdk.internal.vm.annotation.ForceInline; import jdk.internal.vm.annotation.IntrinsicCandidate; +import jdk.internal.vm.annotation.Stable; import sun.reflect.annotation.ExceptionProxy; import sun.reflect.annotation.TypeNotPresentExceptionProxy; import sun.reflect.generics.repository.MethodRepository; @@ -66,7 +67,7 @@ * @since 1.1 */ public final class Method extends Executable { - private Class<?> clazz; + @Stable private Class<?> clazz; private int slot; // This is guaranteed to be interned by the VM in the 1.4 // reflection implementation @@ -74,7 +75,7 @@ private Class<?> returnType; private Class<?>[] parameterTypes; private Class<?>[] exceptionTypes; - private int modifiers; + @Stable private int modifiers; // Generics and annotations support private transient String signature; // generic info repository; lazily initialized Index: src/java.base/share/classes/java/lang/reflect/ProxyGenerator.java IDEA additional info: Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP <+>UTF-8 =================================================================== --- src/java.base/share/classes/java/lang/reflect/ProxyGenerator.java (revision 8352a20bf54a085a97d3f703b5dab482d3f9eccc) +++ src/java.base/share/classes/java/lang/reflect/ProxyGenerator.java (date 1605870027579) @@ -490,7 +490,7 @@ for (List<ProxyMethod> sigmethods : proxyMethods.values()) { for (ProxyMethod pm : sigmethods) { // add static field for the Method object - visitField(Modifier.PRIVATE | Modifier.STATIC, pm.methodFieldName, + visitField(Modifier.PRIVATE | Modifier.STATIC | Modifier.FINAL, pm.methodFieldName, LJLR_METHOD, null, null); // Generate code for proxy method ...and this makes the following results: Benchmark Mode Cnt Score Error Units ProxyBench.implClass avgt 5 3.766 ± 0.040 ns/op ProxyBench.implProxy avgt 5 26.847 ± 0.626 ns/op ProxyBench.ppImplClass avgt 5 3.700 ± 0.017 ns/op ProxyBench.ppImplProxy avgt 5 26.322 ± 0.048 ns/op But this can be changed as a follow-up patch that also takes care of Constructor and Field. ------------- PR: https://git.openjdk.java.net/jdk/pull/313