On Sat, 21 Aug 2021 22:37:05 GMT, Mandy Chung <mch...@openjdk.org> wrote:

>> This reimplements core reflection with method handles.
>> 
>> For `Constructor::newInstance` and `Method::invoke`, the new implementation 
>> uses `MethodHandle`.  For `Field` accessor, the new implementation uses 
>> `VarHandle`.    For the first few invocations of one of these reflective 
>> methods on a specific reflective object we invoke the corresponding method 
>> handle directly. After that we spin a dynamic bytecode stub defined in a 
>> hidden class which loads the target `MethodHandle` or `VarHandle` from its 
>> class data as a dynamically computed constant. Loading the method handle 
>> from a constant allows JIT to inline the method-handle invocation in order 
>> to achieve good performance.
>> 
>> The VM's native reflection methods are needed during early startup, before 
>> the method-handle mechanism is initialized. That happens soon after 
>> System::initPhase1 and before System::initPhase2, after which we switch to 
>> using method handles exclusively.
>> 
>> The core reflection and method handle implementation are updated to handle 
>> chained caller-sensitive method calls [1] properly. A caller-sensitive 
>> method can define with a caller-sensitive adapter method that will take an 
>> additional caller class parameter and the adapter method will be annotated 
>> with `@CallerSensitiveAdapter` for better auditing.   See the detailed 
>> description from [2].
>> 
>> Ran tier1-tier8 tests.   
>> 
>> [1] https://bugs.openjdk.java.net/browse/JDK-8013527
>> [2] 
>> https://bugs.openjdk.java.net/browse/JDK-8271820?focusedCommentId=14439430&page=com.atlassian.jira.plugin.system.issuetabpanels:comment-tabpanel#comment-14439430
>
> Mandy Chung has updated the pull request incrementally with one additional 
> commit since the last revision:
> 
>   Remove separate accessor for static vs instance method
>   
>   There is no effective difference when using MethodHandle::dropArgument for 
> static method.   Removing Static*Accessor and Instance*Accessor simplifies 
> the implementation.

[cff856f](https://github.com/openjdk/jdk/pull/5027/commits/cff856f9df89293cbc8f2f1e977148cd6ece4f85)
 removed the  distinct classes to specialize for static vs instance methods 
simplify
the `DirectMethodAccessorImpl` implementation and its subclasses.

There is no effective difference for static method invocation to drop
the first argument via MethodHandle::dropArgument or special case it
in `StaticMethodAccessor::invoke`.  The cold startup time increases
4ms from 92.73ms to 96.80ms when MethodHandle::dropArgument is used.


Current implementation

Benchmark                                   Mode  Cnt   Score   Error  Units
ReflectionMethods.instance_method           avgt   10  14.902 ? 0.031  ns/op
ReflectionMethods.instance_method_3arg      avgt   10  18.099 ? 0.632  ns/op
ReflectionMethods.instance_method_var       avgt   10  14.670 ? 0.044  ns/op
ReflectionMethods.instance_method_var_3arg  avgt   10  18.038 ? 0.109  ns/op
ReflectionMethods.static_method             avgt   10  20.140 ? 0.032  ns/op
ReflectionMethods.static_method_3arg        avgt   10  26.999 ? 0.046  ns/op
ReflectionMethods.static_method_var         avgt   10  22.272 ? 0.153  ns/op
ReflectionMethods.static_method_var_3arg    avgt   10  26.567 ? 0.088  ns/op

No special casing for static vs instance methods

Benchmark                                   Mode  Cnt   Score   Error  Units
ReflectionMethods.instance_method           avgt   10  15.177 ? 0.032  ns/op
ReflectionMethods.instance_method_3arg      avgt   10  17.906 ? 0.050  ns/op
ReflectionMethods.instance_method_var       avgt   10  14.759 ? 0.083  ns/op
ReflectionMethods.instance_method_var_3arg  avgt   10  18.054 ? 0.197  ns/op
ReflectionMethods.static_method             avgt   10  13.599 ? 0.029  ns/op
ReflectionMethods.static_method_3arg        avgt   10  16.910 ? 0.099  ns/op
ReflectionMethods.static_method_var         avgt   10  13.993 ? 0.189  ns/op
ReflectionMethods.static_method_var_3arg    avgt   10  17.555 ? 0.068  ns/op

$ perf stat -r 50 jep-build/linux-x86_64-server-release/images/jdk/bin/java -cp 
$MICRO_JAR org.openjdk.bench.java.lang.reflect.ReflectionColdstartBenchmark

 Performance counter stats for 
'build/linux-x86_64-server-release/images/jdk/bin/java -cp 
build/linux-x86_64-server-release/images/test/micro/benchmarks.jar 
org.openjdk.bench.java.lang.reflect.ReflectionColdstartBenchmark' (50 runs):

        138.361238      task-clock:u (msec)       #    1.492 CPUs utilized      
      ( +-  0.79% )
                 0      context-switches:u        #    0.000 K/sec
                 0      cpu-migrations:u          #    0.000 K/sec
              4029      page-faults:u             #    0.029 M/sec              
      ( +-  0.12% )
         165249965      cycles:u                  #    1.194 GHz                
      ( +-  0.85% )
         187374236      instructions:u            #    1.13  insn per cycle     
      ( +-  0.44% )
          35240188      branches:u                #  254.697 M/sec              
      ( +-  0.46% )
           1205192      branch-misses:u           #    3.42% of all branches    
      ( +-  1.15% )

       0.092733825 seconds time elapsed                                         
 ( +-  1.02% )

$ perf stat -r 50 build/linux-x86_64-server-release/images/jdk/bin/java -cp 
$MICRO_JAR org.openjdk.bench.java.lang.reflect.ReflectionColdstartBenchmark     

 Performance counter stats for 
'build/linux-x86_64-server-release/images/jdk/bin/java -cp 
build/linux-x86_64-server-release/images/test/micro/benchmarks.jar 
org.openjdk.bench.java.lang.reflect.ReflectionColdstartBenchmark' (50 runs):

        153.339577      task-clock:u (msec)       #    1.584 CPUs utilized      
      ( +-  0.83% )
                 0      context-switches:u        #    0.000 K/sec
                 0      cpu-migrations:u          #    0.000 K/sec
              4129      page-faults:u             #    0.027 M/sec              
      ( +-  0.10% )
         187019454      cycles:u                  #    1.220 GHz                
      ( +-  1.06% )
         203065287      instructions:u            #    1.09  insn per cycle     
      ( +-  0.34% )
          38350445      branches:u                #  250.101 M/sec              
      ( +-  0.36% )
           1385764      branch-misses:u           #    3.61% of all branches    
      ( +-  1.08% )

       0.096803078 seconds time elapsed                                         
 ( +-  1.07% )


Note that static_method* are slower than instance_method* in the current 
implementation
but seem to perform better after this patch.   Claes looked into the current 
implementation
via the disassembly and found that instance_method* get a more optimal code 
shape at
the call-site but static_methods* do not.   With this patch, we will need to 
look closer if
instance_method* and static_method* benchmarks probbly get the optimal code 
shape
at the call-site.

-------------

PR: https://git.openjdk.java.net/jdk/pull/5027

Reply via email to