On Wed, 12 May 2021 09:59:32 GMT, Claes Redestad <redes...@openjdk.org> wrote:

>> Hello, from discussion in https://github.com/openjdk/jdk/pull/3464 I've 
>> found out, that in a few of JDK core classes, e.g. in `j.l.Class` 
>> expressions like `baseName.replace('.', '/') + '/' + name` are not compiled 
>> into `invokedynamic`-based code, but into one using `StringBuilder`. This 
>> happens due to some bootstraping issues.
>> 
>> However the code like
>> 
>> private String getSimpleName0() {
>>     if (isArray()) {
>>         return getComponentType().getSimpleName() + "[]";
>>     }
>>     //...
>> }
>> 
>> can be improved via replacement of `+` with `String.concat()` call.
>> 
>> I've used this benchmark to measure impact:
>> 
>> @State(Scope.Thread)
>> @BenchmarkMode(Mode.AverageTime)
>> @OutputTimeUnit(TimeUnit.NANOSECONDS)
>> @Fork(jvmArgsAppend = {"-Xms2g", "-Xmx2g"})
>> public class ClassMethodsBenchmark {
>>   private final Class<? extends Object[]> arrayClass = Object[].class;
>>   private Method descriptorString;
>> 
>>   @Setup
>>   public void setUp() throws NoSuchMethodException {
>>     //fore some reason compiler doesn't allow me to call 
>> Class.descriptorString() directly
>>     descriptorString = Class.class.getDeclaredMethod("descriptorString");
>>   }
>> 
>>   @Benchmark
>>   public Object descriptorString() throws Exception {
>>     return descriptorString.invoke(arrayClass);
>>   }
>> 
>>   @Benchmark
>>   public Object typeName() {
>>     return arrayClass.getTypeName();
>>   }
>> }
>> 
>> and got those results
>> 
>>                                                    Mode  Cnt     Score     
>> Error   Units
>> descriptorString                                   avgt   60    91.480 ±   
>> 1.839   ns/op
>> descriptorString:·gc.alloc.rate.norm               avgt   60   404.008 ±   
>> 4.033    B/op
>> descriptorString:·gc.churn.G1_Eden_Space           avgt   60  2791.866 ± 
>> 181.589  MB/sec
>> descriptorString:·gc.churn.G1_Eden_Space.norm      avgt   60   401.702 ±  
>> 26.047    B/op
>> descriptorString:·gc.churn.G1_Survivor_Space       avgt   60     0.003 ±   
>> 0.001  MB/sec
>> descriptorString:·gc.churn.G1_Survivor_Space.norm  avgt   60    ≈ 10⁻³       
>>        B/op
>> descriptorString:·gc.count                         avgt   60   205.000       
>>      counts
>> descriptorString:·gc.time                          avgt   60   216.000       
>>          ms
>> 
>> patched
>>                                                    Mode  Cnt     Score     
>> Error   Units
>> descriptorString                                   avgt   60    65.016 ±   
>> 3.446   ns/op
>> descriptorString:·gc.alloc.rate                    avgt   60  2844.240 ± 
>> 115.719  MB/sec
>> descriptorString:·gc.alloc.rate.norm               avgt   60   288.006 ±   
>> 0.001    B/op
>> descriptorString:·gc.churn.G1_Eden_Space           avgt   60  2832.996 ± 
>> 206.939  MB/sec
>> descriptorString:·gc.churn.G1_Eden_Space.norm      avgt   60   286.955 ±  
>> 17.853    B/op
>> descriptorString:·gc.churn.G1_Survivor_Space       avgt   60     0.003 ±   
>> 0.001  MB/sec
>> descriptorString:·gc.churn.G1_Survivor_Space.norm  avgt   60    ≈ 10⁻³       
>>        B/op
>> descriptorString:·gc.count                         avgt   60   208.000       
>>      counts
>> descriptorString:·gc.time                          avgt   60   228.000       
>>          ms
>> -----------------
>> typeName                                           avgt   60    34.273 ±   
>> 0.480   ns/op
>> typeName:·gc.alloc.rate                            avgt   60  3265.356 ±  
>> 45.113  MB/sec
>> typeName:·gc.alloc.rate.norm                       avgt   60   176.004 ±   
>> 0.001    B/op
>> typeName:·gc.churn.G1_Eden_Space                   avgt   60  3268.454 ± 
>> 134.458  MB/sec
>> typeName:·gc.churn.G1_Eden_Space.norm              avgt   60   176.109 ±   
>> 6.595    B/op
>> typeName:·gc.churn.G1_Survivor_Space               avgt   60     0.003 ±   
>> 0.001  MB/sec
>> typeName:·gc.churn.G1_Survivor_Space.norm          avgt   60    ≈ 10⁻⁴       
>>        B/op
>> typeName:·gc.count                                 avgt   60   240.000       
>>      counts
>> typeName:·gc.time                                  avgt   60   255.000       
>>          ms
>> 
>> patched
>> 
>> typeName                                           avgt   60    15.787 ±   
>> 0.214   ns/op
>> typeName:·gc.alloc.rate                            avgt   60  2577.554 ±  
>> 32.339  MB/sec
>> typeName:·gc.alloc.rate.norm                       avgt   60    64.001 ±   
>> 0.001    B/op
>> typeName:·gc.churn.G1_Eden_Space                   avgt   60  2574.039 ± 
>> 147.774  MB/sec
>> typeName:·gc.churn.G1_Eden_Space.norm              avgt   60    63.895 ±   
>> 3.511    B/op
>> typeName:·gc.churn.G1_Survivor_Space               avgt   60     0.003 ±   
>> 0.001  MB/sec
>> typeName:·gc.churn.G1_Survivor_Space.norm          avgt   60    ≈ 10⁻⁴       
>>        B/op
>> typeName:·gc.count                                 avgt   60   189.000       
>>      counts
>> typeName:·gc.time                                  avgt   60   199.000       
>>          ms
>> 
>> I think this patch is likely to improve reflection operations related to 
>> arrays.
>> 
>> If one finds this patch useful, then I'll create a ticket to track this. 
>> Also it'd be nice to have a list of classes, that are compiled in the same 
>> way as `j.l.Class` (i.e. have no access to `invokedynamic`) so I could look 
>> into them for other snippets where two String are joined so `concat`-based 
>> optimization is possible.
>> 
>> Pre-sizing of `StringBuilder` for `Class.gdescriptorString()` and 
>> `Class.getCanonicalName0()` is one more improvement opportunity here.
>
> src/java.base/share/classes/java/lang/Class.java line 4351:
> 
>> 4349: 
>> 4350:         if (isArray()) {
>> 4351:             return "[".concat(componentType.descriptorString());
> 
> This could be optimized further for multi-dimensional arrays:
> 
> if (isArray()) {
>   int arrayDepth = 1;
>   Class<?> ct = componentType;
>   while (ct.isArray()) {
>     arrayDepth++;
>     ct = ct.componentType;
>   }
>   return "[".repeat(arrayDepth).concat(ct.descriptorString());

But isn't `componentType.descriptorString()` does this itself? Also 
multi-dimensional arrays are quite an infrequent usecase, aren't they?

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

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

Reply via email to