Optimize sun.invoke.util.BytecodeDescriptor.unparse

Roger Riggs Roger.Riggs at oracle.com
Fri Aug 21 13:32:33 UTC 2020


Pushed as     8252127: Optimize sun.invoke.util.BytecodeDescriptor.unparse

https://bugs.openjdk.java.net/browse/JDK-8252127

Thanks Christoph

On 8/20/20 4:15 PM, Roger Riggs wrote:
> Hi Christoph,
>
> Looks good.
>
> Note that Claes added the cases for Object.class and int.class
> to maximize the performance of those common cases.
>
> I'll sponsor the change.
>
> Thanks, Roger
>
>
> On 8/20/20 2:01 PM, Christoph Dreis wrote:
>> Hi Roger,
>>
>> thanks for taking a look!
>>
>>> Though I wonder if performs differently than just calling  
>>> t.descriptorString()?
>> Seems pretty much to be the same.
>> The difference of 1 ns for the primitive case is a bit weird, but I 
>> guess at this levels it's also possible to have fluctuations here and 
>> there.
>>
>> Long.class
>> Benchmark                                          Mode  Cnt 
>> Score     Error   Units
>> MyBenchmark.unparseChristoph                       avgt   10 36,995 
>> ±   0,748   ns/op
>> MyBenchmark.unparseChristoph:·gc.alloc.rate.norm   avgt   10 168,009 
>> ±   0,001    B/op
>> MyBenchmark.unparseRoger                           avgt   10 36,857 
>> ±   1,472   ns/op
>> MyBenchmark.unparseRoger:·gc.alloc.rate.norm       avgt   10 168,009 
>> ±   0,001    B/op
>> MyBenchmark.unparseOld                             avgt   10 53,926 
>> ±   1,991   ns/op
>> MyBenchmark.unparseOld:·gc.alloc.rate.norm         avgt   10 256,014 
>> ±   0,001    B/op
>> long.class
>> Benchmark                                          Mode  Cnt 
>> Score     Error   Units
>> MyBenchmark.unparseChristoph                       avgt   10 5,184 
>> ±   0,168   ns/op
>> MyBenchmark.unparseChristoph:·gc.alloc.rate.norm   avgt   10 ≈ 
>> 10⁻⁶              B/op
>> MyBenchmark.unparseRoger                           avgt   10 6,149 
>> ±   0,238   ns/op
>> MyBenchmark.unparseRoger:·gc.alloc.rate.norm       avgt   10 ≈ 
>> 10⁻⁶              B/op
>> MyBenchmark.unparseOld                             avgt   10 11,236 
>> ±   0,464   ns/op
>> MyBenchmark.unparseOld:·gc.alloc.rate.norm         avgt   10 80,003 
>> ±   0,001    B/op
>>
>>> It should be equivalent, without extra checks.
>> It seems to be indeed equivalent, so I would change my proposal to 
>> the following.
>> I would keep the check for Object.class and int.class above as they 
>> seem to be the most common.
>> At least Object.class is good to have to avoid unnecessary String 
>> allocations in from descriptorString() imho.
>>
>> =========== PATCH ===============
>> --- 
>> a/src/java.base/share/classes/sun/invoke/util/BytecodeDescriptor.java 
>> Thu Aug 13 09:33:28 2020 -0700
>> +++ 
>> b/src/java.base/share/classes/sun/invoke/util/BytecodeDescriptor.java 
>> Thu Aug 20 19:44:57 2020 +0200
>> @@ -110,9 +110,7 @@
>>           } else if (type == int.class) {
>>               return "I";
>>           }
>> -        StringBuilder sb = new StringBuilder();
>> -        unparseSig(type, sb);
>> -        return sb.toString();
>> +        return type.descriptorString();
>>       }
>>
>> What do you think?
>>
>> Cheers,
>> Christoph
>>
>> On 8/13/20 1:31 PM, Christoph Dreis wrote:
>>> Hi,
>>>
>>> I just stumbled upon sun.invoke.util.BytecodeDescriptor.unparse that 
>>> seems to unnecessarily create a StringBuilder and checks for the 
>>> given type to be of Object.class twice in certain scenarios.
>>>
>>> When I apply the attached patch below with the following isolated 
>>> benchmark:
>>>
>>> @BenchmarkMode(Mode.AverageTime)
>>> @OutputTimeUnit(TimeUnit.NANOSECONDS)
>>> @State(Scope.Thread)
>>> public class MyBenchmark {
>>>
>>>     @State(Scope.Thread)
>>>     public static class BenchmarkState {
>>>         private Class<?> test = String.class; // long.class;
>>>     }
>>>
>>>     @Benchmark
>>>     public String unparseNew(BenchmarkState state) {
>>>         return BytecodeDescriptor.unparseNew(state.test);
>>>     }
>>>
>>>     @Benchmark
>>>     public String unparseOld(BenchmarkState state) {
>>>         return BytecodeDescriptor.unparseOld(state.test);
>>>     }
>>> }
>>>
>>> I get the following results:
>>> String.class
>>> Benchmark                                   Mode  Cnt Score     
>>> Error   Units
>>> MyBenchmark.unparseNew                      avgt   10 47,207 ±   
>>> 1,918   ns/op
>>> MyBenchmark.unparseNew:·gc.alloc.rate.norm  avgt   10 232,011 ±   
>>> 0,002    B/op
>>> MyBenchmark.unparseOld                      avgt   10 87,197 ±  
>>> 22,843   ns/op
>>> MyBenchmark.unparseOld:·gc.alloc.rate.norm  avgt   10 384,020 ±   
>>> 0,001    B/op
>>> long.class
>>> Benchmark                                   Mode  Cnt Score     
>>> Error   Units
>>> MyBenchmark.unparseNew                      avgt   10 4,996 ±    
>>> 0,022   ns/op
>>> MyBenchmark.unparseNew:·gc.alloc.rate.norm  avgt   10    ≈ 
>>> 10⁻⁶               B/op
>>> MyBenchmark.unparseOld                      avgt   10 13,303 ±    
>>> 6,305   ns/op
>>> MyBenchmark.unparseOld:·gc.alloc.rate.norm  avgt   10 80,003 ±    
>>> 0,001    B/op
>>>
>>> As you can see the new way makes things allocation free for 
>>> primitives and also improves normal classes.
>>>
>>> It seems like a relatively trivial improvement. In case you think 
>>> this is worthwhile, I would appreciate it if someone could sponsor 
>>> the change.
>>>
>>> Cheers,
>>> Christoph
>>>
>>> ======= PATCH =======
>>> --- 
>>> a/src/java.base/share/classes/sun/invoke/util/BytecodeDescriptor.java 
>>> Thu Aug 13 09:33:28 2020 -0700
>>> +++ 
>>> b/src/java.base/share/classes/sun/invoke/util/BytecodeDescriptor.java 
>>> Thu Aug 13 19:27:26 2020 +0200
>>> @@ -110,9 +110,13 @@
>>>            } else if (type == int.class) {
>>>                return "I";
>>>            }
>>> -        StringBuilder sb = new StringBuilder();
>>> -        unparseSig(type, sb);
>>> -        return sb.toString();
>>> +        Wrapper basicType = Wrapper.forBasicType(type);
>>> +        char c = basicType.basicTypeChar();
>>> +        if (c != 'L') {
>>> +            return basicType.basicTypeString();
>>> +        } else {
>>> +            return type.descriptorString();
>>> +        }
>>>        }
>>>
>>>
>>
>>
>



More information about the core-libs-dev mailing list