On Thu, 22 Apr 2021 14:07:20 GMT, Сергей Цыпанов 
<github.com+10835776+stsypa...@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.

Spotted one more method to be improved:

@State(Scope.Benchmark)
@BenchmarkMode(Mode.AverageTime)
@OutputTimeUnit(TimeUnit.NANOSECONDS)
@Fork(jvmArgsAppend = {"-Xms2g", "-Xmx2g"})
public class ClassToStringBenchmark {
    private final Class<?> clazzWithShortName = Object.class;
    private final Class<?> interfaceWithShortName = Serializable.class;
    private final Class<?> primitive = int.class;

    @Benchmark
    public String classToString() {
        return clazzWithShortName.toString();
    }

    @Benchmark
    public String interfaceToString() {
        return interfaceWithShortName.toString();
    }

    @Benchmark
    public String primitiveToString() {
        return primitive.toString();
    }
}

giving

before
                                                    Mode  Cnt     Score     
Error   Units
classToString                                       avgt   50    30,769 ±   
0,352   ns/op
classToString:·gc.alloc.rate                        avgt   50  3769,212 ±  
42,959  MB/sec
classToString:·gc.alloc.rate.norm                   avgt   50   152,003 ±   
0,001    B/op
classToString:·gc.churn.G1_Eden_Space               avgt   50  3776,990 ± 
112,309  MB/sec
classToString:·gc.churn.G1_Eden_Space.norm          avgt   50   152,328 ±   
4,317    B/op
classToString:·gc.churn.G1_Survivor_Space           avgt   50     0,004 ±   
0,001  MB/sec
classToString:·gc.churn.G1_Survivor_Space.norm      avgt   50    ≈ 10⁻⁴         
     B/op
classToString:·gc.count                             avgt   50   385,000         
   counts
classToString:·gc.time                              avgt   50   378,000         
       ms

interfaceToString                                   avgt   50    35,571 ±   
0,583   ns/op
interfaceToString:·gc.alloc.rate                    avgt   50  3433,504 ±  
52,056  MB/sec
interfaceToString:·gc.alloc.rate.norm               avgt   50   160,003 ±   
0,001    B/op
interfaceToString:·gc.churn.G1_Eden_Space           avgt   50  3443,384 ±  
91,647  MB/sec
interfaceToString:·gc.churn.G1_Eden_Space.norm      avgt   50   160,456 ±   
3,454    B/op
interfaceToString:·gc.churn.G1_Survivor_Space       avgt   50     0,004 ±   
0,001  MB/sec
interfaceToString:·gc.churn.G1_Survivor_Space.norm  avgt   50    ≈ 10⁻⁴         
     B/op
interfaceToString:·gc.count                         avgt   50   351,000         
   counts
interfaceToString:·gc.time                          avgt   50   351,000         
       ms

primitiveToString                                   avgt   50    20,193 ±   
0,369   ns/op
primitiveToString:·gc.alloc.rate                    avgt   50  3932,425 ±  
68,453  MB/sec
primitiveToString:·gc.alloc.rate.norm               avgt   50   104,002 ±   
0,001    B/op
primitiveToString:·gc.churn.G1_Eden_Space           avgt   50  3943,662 ± 
119,781  MB/sec
primitiveToString:·gc.churn.G1_Eden_Space.norm      avgt   50   104,305 ±   
2,678    B/op
primitiveToString:·gc.churn.G1_Survivor_Space       avgt   50     0,005 ±   
0,001  MB/sec
primitiveToString:·gc.churn.G1_Survivor_Space.norm  avgt   50    ≈ 10⁻⁴         
     B/op
primitiveToString:·gc.count                         avgt   50   402,000         
   counts
primitiveToString:·gc.time                          avgt   50   387,000         
       ms

after

                                                    Mode  Cnt     Score     
Error   Units
classToString                                       avgt   50    13,552 ±   
0,114   ns/op
classToString:·gc.alloc.rate                        avgt   50  3602,305 ±  
27,881  MB/sec
classToString:·gc.alloc.rate.norm                   avgt   50    64,001 ±   
0,001    B/op
classToString:·gc.churn.G1_Eden_Space               avgt   50  3619,864 ± 
119,002  MB/sec
classToString:·gc.churn.G1_Eden_Space.norm          avgt   50    64,318 ±   
2,081    B/op
classToString:·gc.churn.G1_Survivor_Space           avgt   50     0,004 ±   
0,001  MB/sec
classToString:·gc.churn.G1_Survivor_Space.norm      avgt   50    ≈ 10⁻⁴         
     B/op
classToString:·gc.count                             avgt   50   369,000         
   counts
classToString:·gc.time                              avgt   50   366,000         
       ms

interfaceToString                                   avgt   50    16,480 ±   
0,113   ns/op
interfaceToString:·gc.alloc.rate                    avgt   50  3332,045 ±  
21,628  MB/sec
interfaceToString:·gc.alloc.rate.norm               avgt   50    72,001 ±   
0,001    B/op
interfaceToString:·gc.churn.G1_Eden_Space           avgt   50  3335,296 ±  
98,091  MB/sec
interfaceToString:·gc.churn.G1_Eden_Space.norm      avgt   50    72,086 ±   
2,187    B/op
interfaceToString:·gc.churn.G1_Survivor_Space       avgt   50     0,004 ±   
0,001  MB/sec
interfaceToString:·gc.churn.G1_Survivor_Space.norm  avgt   50    ≈ 10⁻⁴         
     B/op
interfaceToString:·gc.count                         avgt   50   340,000         
   counts
interfaceToString:·gc.time                          avgt   50   337,000         
       ms

primitiveToString                                   avgt   50     5,992 ±   
0,079   ns/op
primitiveToString:·gc.alloc.rate                    avgt   50  3056,666 ±  
39,390  MB/sec
primitiveToString:·gc.alloc.rate.norm               avgt   50    24,000 ±   
0,001    B/op
primitiveToString:·gc.churn.G1_Eden_Space           avgt   50  3070,543 ± 
107,567  MB/sec
primitiveToString:·gc.churn.G1_Eden_Space.norm      avgt   50    24,114 ±   
0,814    B/op
primitiveToString:·gc.churn.G1_Survivor_Space       avgt   50     0,004 ±   
0,001  MB/sec
primitiveToString:·gc.churn.G1_Survivor_Space.norm  avgt   50    ≈ 10⁻⁵         
     B/op
primitiveToString:·gc.count                         avgt   50   313,000         
   counts
primitiveToString:·gc.time                          avgt   50   310,000         
       ms

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

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

Reply via email to