Uploaded image for project: 'JDK'
  1. JDK
  2. JDK-8266622

Optimize Class.descriptorString() and Class.getCanonicalName0()

    XMLWordPrintable

Details

    • Enhancement
    • Resolution: Fixed
    • P4
    • 17
    • None
    • core-libs
    • None
    • b23

    Description

      Hello, from discussion in https://github.com/openjdk/jdk/pull/3464 it appears, that in j.l.Class expressions like

      String str = baseName.replace('.', '/') + '/' + name;

      are not compiled into invokedynamic-based code, but into one using StringBuilder.

      This happens due to some bootstraping issues. Currently the bytecode for the last (most often used) branch of Class.descriptorString() looks like

      public sb()Ljava/lang/String;
         L0
          LINENUMBER 21 L0
          NEW java/lang/StringBuilder
          DUP
          INVOKESPECIAL java/lang/StringBuilder.<init> ()V
          ASTORE 1
         L1
          LINENUMBER 23 L1
          ALOAD 1
          LDC "a"
          INVOKEVIRTUAL java/lang/StringBuilder.append (Ljava/lang/String;)Ljava/lang/StringBuilder;
          POP
         L2
          LINENUMBER 24 L2
          ALOAD 1
          LDC "b"
          INVOKEVIRTUAL java/lang/StringBuilder.append (Ljava/lang/String;)Ljava/lang/StringBuilder;
          POP
         L3
          LINENUMBER 26 L3
          ALOAD 1
          INVOKEVIRTUAL java/lang/StringBuilder.toString ()Ljava/lang/String;
          ARETURN

      Here the StringBuilder is created with default constructor and then expands if necessary while appending.

      This can be improved by manually allocating StringBuilder of exact size. The benchmark demonstrates measurable improvement:

      @State(Scope.Benchmark)
      @BenchmarkMode(Mode.AverageTime)
      @OutputTimeUnit(TimeUnit.NANOSECONDS)
      @Fork(jvmArgsAppend = {"-Xms2g", "-Xmx2g"})
      public class ClassDescriptorStringBenchmark {

          private final Class<?> clazzWithShortDescriptor = Object.class;
          private final Class<?> clazzWithLongDescriptor = getClass();

          @Benchmark
          public String descriptorString_short() {
              return clazzWithShortDescriptor.descriptorString();
          }

          @Benchmark
          public String descriptorString_long() {
              return clazzWithLongDescriptor.descriptorString();
          }
      }

      original
                                                     Mode Score Error Units
      descriptorString_long avgt 74.835 ± 0.262 ns/op
      descriptorString_short avgt 43.822 ± 0.788 ns/op
      descriptorString_long:·gc.alloc.rate.norm avgt 504.010 ± 0.001 B/op
      descriptorString_short:·gc.alloc.rate.norm avgt 208.004 ± 0.001 B/op

      patched
                                                     Mode Score Error Units
      descriptorString_long avgt 42.864 ± 0.160 ns/op
      descriptorString_short avgt 27.255 ± 0.381 ns/op
      descriptorString_long:·gc.alloc.rate.norm avgt 224.005 ± 0.001 B/op
      descriptorString_short:·gc.alloc.rate.norm avgt 120.002 ± 0.001 B/op

      Same can be done also for Class.isHidden() branch in Class.descriptorString() and for Class.getCanonicalName0()

      Attachments

        Issue Links

          Activity

            People

              stsypanov Sergey Tsypanov
              stsypanov Sergey Tsypanov
              Votes:
              0 Vote for this issue
              Watchers:
              3 Start watching this issue

              Dates

                Created:
                Updated:
                Resolved: