RFR: 8266642: improve ResolvedMethodTable hash function [v2]

David Holmes david.holmes at oracle.com
Tue May 11 00:45:00 UTC 2021


On 11/05/2021 3:43 am, Denghui Dong wrote:
> On Fri, 7 May 2021 06:27:25 GMT, Denghui Dong <ddong at openjdk.org> wrote:
> 
>>> JDK-8249719 has fixed the bad hash function problem, however, the performance problem still exists when there are a large number of classes with the same name.
>>> Adding the address of the corresponding ClassLoaderData as a factor of hash can solve the problem.
>>
>> Denghui Dong has updated the pull request incrementally with one additional commit since the last revision:
>>
>>    minor adjustment
> 
> Hi David,
> 
> Thanks for your comment!
> 
>> Can you please elaborate on when there are large numbers of classes with the same name? I can see this may help that case but how likely is that case?
> 
> Actually, the performance problem we noticed occurred in jdk 11u, here's a simplified piece of code:

Is there a real benchmark that demonstrates this, rather than a 
micro-benchmark? I'm just trying to understand under conditions this is 
a real problem.

> 
> import java.lang.invoke.*;
> import java.util.*;
> 
> class Test {
>      public static void main(String[] args) throws Throwable{
>          MethodType mt = MethodType.methodType(void.class);
> 
>          List<MethodHandle> mhs = new ArrayList<>();
>          for (int i = 0; i < 2000; i++) {
>              // In practice, it will point to different methods
>              mhs.add(MethodHandles.lookup().findVirtual(Test.class, "call",
>                                                         MethodType.methodType(void.class))
>                                   .bindTo(new Test()));
>          }
> 
>          for (int i = 0; i < 128; i++) {
>              for (MethodHandle mh : mhs) {
>                  mh.invokeExact();
>              }
>          }
>      }
> 
>      void call() {}
> }
> 
> 
> Run: `java -Xlog:membername+table=debug Test`, and we can see that all of the LambdaForm$MH use a same slot.
> 
> [0.359s][debug][membername,table] ResolvedMethod entry added for java.lang.invoke.LambdaForm$MH/0x0000000800254840.invoke(Ljava/lang/Object;)V index 219
> [0.359s][debug][membername,table] ResolvedMethod entry added for java.lang.invoke.LambdaForm$MH/0x0000000800254c40.invoke(Ljava/lang/Object;)V index 219
> [0.359s][debug][membername,table] ResolvedMethod entry added for java.lang.invoke.LambdaForm$MH/0x0000000800255040.invoke(Ljava/lang/Object;)V index 219
> [0.359s][debug][membername,table] ResolvedMethod entry added for java.lang.invoke.LambdaForm$MH/0x0000000800255440.invoke(Ljava/lang/Object;)V index 219
> 
> 
> 
> This problem doesn't exist in jdk upstream because of the name-mangling mechanism for hidden class(ClassFileParser::mangle_hidden_class_name).

Is the output above from JDK 17 or JDK 11? It seems to show the mangled 
(well "adorned") name, so shouldn't they map to different hash buckets 
anyway?

> However, if the user creates many classes with the same name by different classloaders, the performance problems will still appear, although I also think that the general user will not implement the code in this way.
> 
> My plan is to fix this problem in the upstream and then backport it to JDK 11, what do you think?

I'm not sure it is the right approach to "fix" a problem that doesn't 
really exist in mainline just so it can be backported to 11u

>> And what is the impact on other cases as we're now increasing the overhead of the hash computation.
> 
> I think the overhead is negligible, here is a benchmark based on ResolvedMethodTableHash.java:

Thanks for confirming.

David

> 
> import org.openjdk.jmh.annotations.Benchmark;
> import org.openjdk.jmh.annotations.Fork;
> import org.openjdk.jmh.annotations.Warmup;
> import org.openjdk.jmh.annotations.Setup;
> import org.openjdk.jmh.annotations.*;
> import org.openjdk.jmh.infra.Blackhole;
> 
> import java.lang.invoke.MethodHandle;
> import java.lang.invoke.MethodHandles;
> import java.lang.invoke.MethodType;
> import java.nio.ByteBuffer;
> import java.util.ArrayList;
> import java.util.List;
> import java.util.Random;
> 
> @State(Scope.Benchmark)
> public class MyBenchmark extends ClassLoader {
> 
>      // Produce a class file with the given name and a single method:
>      //     public static native void m();
>      private int writeClass(byte[] buf, String className) {
>         ... same as ResolvedMethodTableHash.java
>      }
> 
>      private List<Class<?>> classes = new ArrayList<>();
>      private Random r = new Random();
> 
>      @Setup
>      public void prepare() throws Exception {
>          for (int i = 0; i < 5000; i ++) {
>              byte[] buf = new byte[100];
>              int size = writeClass(buf, "MH$" + i);
>              classes.add(defineClass(null, buf, 0, size));
>          }
>      }
> 
> 
>      @Benchmark
>      @Fork(value=1)
>      @Warmup(iterations = 5, time = 1)
>      public void test(Blackhole bh) throws Exception {
>          MethodHandle mh = MethodHandles.publicLookup().findStatic(classes.get(r.nextInt(5000)),
>                                                                    "m", MethodType.methodType(void.class));
>          if (mh == null) {
>              throw new RuntimeException();
>          }
>      }
> }
> 
> 
> on linux x86_64,
> 
> result with the patch:
> 
> Result: 904126.215 ±(99.9%) 26603.356 ops/s [Average]
>    Statistics: (min, avg, max) = (852079.715, 904126.215, 927791.630), stdev = 30636.464
>    Confidence interval (99.9%): [877522.859, 930729.571]
> 
> Benchmark                Mode  Samples       Score  Score error  Units
> o.s.MyBenchmark.test    thrpt       20  904126.215    26603.356  ops/s
> 
> 
> result without the patch:
> 
> Result: 883669.014 ±(99.9%) 26455.778 ops/s [Average]
>    Statistics: (min, avg, max) = (830830.551, 883669.014, 906978.220), stdev = 30466.514
>    Confidence interval (99.9%): [857213.236, 910124.792]
> 
> Benchmark                Mode  Samples       Score  Score error  Units
> o.s.MyBenchmark.test    thrpt       20  883669.014    26455.778  ops/s
> 
> -------------
> 
> PR: https://git.openjdk.java.net/jdk/pull/3901
> 


More information about the hotspot-runtime-dev mailing list