RFR: 8338532: Speed up the ClassFile API MethodTypeDesc#ofDescriptor

Chen Liang liach at openjdk.org
Mon Aug 19 06:32:23 UTC 2024


On Sat, 17 Aug 2024 16:27:50 GMT, Shaojin Wen <duke at openjdk.org> wrote:

>> The current implementation of ofDescriptor puts return type and parameter types together in an ArrayList, and then splits them into return type and array of parameter types. This ArrayList creation is unnecessary, considering most descriptors only have few parameter types.
>> 
>> By splitting return type and parameter types separately and scanning the descriptor first to get the number of parameters, we can just allocate an exact, trusted array for the resulting MethodTypeDesc without copy.
>
> The following are the performance numbers on multiple platforms. When the number of parameters is > 8, the performance will regress by about 33%. When the number of parameters is <= 8, the performance will be significantly improved or neutral.
> 
> ## 1. Benchmark script
> 
> # baseline
> git checkout 74066bcca82749722e6fee57469520d418bf3430
> make test TEST="micro:java.lang.constant.MethodTypeDescFactories.ofDescriptor"
> 
> # current
> git checkout c02d2306935d99685e34ef960aa72e10feb82a39
> make test TEST="micro:java.lang.constant.MethodTypeDescFactories.ofDescriptor"
> 
> 
> ## 2. Performance numbers
> 
> ### 2.1 Mac Book M1 Pro
> 
> -# baseline
> -Benchmark                                                         (descString)  Mode  Cnt     Score     Error  Units
> -MethodTypeDescFactories.ofDescriptor   (Ljava/lang/Object;Ljava/lang/String;)I  avgt    6   104.154 ?   2.636  ns/op
> -MethodTypeDescFactories.ofDescriptor                                       ()V  avgt    6     3.843 ?   0.014  ns/op
> -MethodTypeDescFactories.ofDescriptor  ([IJLjava/lang/String;Z)Ljava/util/List;  avgt    6   221.013 ?  81.200  ns/op
> -MethodTypeDescFactories.ofDescriptor                     ()[Ljava/lang/String;  avgt    6    20.603 ?   0.026  ns/op
> -MethodTypeDescFactories.ofDescriptor                                  (..IIJ)V  avgt    6   221.404 ? 189.754  ns/op
> -MethodTypeDescFactories.ofDescriptor                  (.....................).  avgt    6  1128.418 ?  16.850  ns/op
> 
> +# current
> +Benchmark                                                         (descString)  Mode  Cnt     Score    Error  Units
> +MethodTypeDescFactories.ofDescriptor   (Ljava/lang/Object;Ljava/lang/String;)I  avgt    6    53.900 ?  0.061  ns/op
> +MethodTypeDescFactories.ofDescriptor                                       ()V  avgt    6     3.609 ?  0.017  ns/op
> +MethodTypeDescFactories.ofDescriptor  ([IJLjava/lang/String;Z)Ljava/util/List;  avgt    6    69.666 ?  0.596  ns/op
> +MethodTypeDescFactories.ofDescriptor                     ()[Ljava/lang/String;  avgt    6    20.978 ?  0.197  ns/op
> +MethodTypeDescFactories.ofDescriptor                                  (..IIJ)V  avgt    6   126.968 ?  0.715  ns/op
> +MethodTypeDescFactories.ofDescriptor                  (.....................).  avgt    6  1651.624 ? 30.462  ns/op
> 
> 
> | descString | baseline  | current | delta |
> | --- | --- | --- | --- |
> | (Ljava/lang/Object;Ljava/lang/String;)I | 104.154 | 53.900 | 93.24% |
> | ()V | 3.843 | 3.609 | 6.48% |
> | ([IJLjava/lang/String;Z)Ljava/util/List; ...

@wenshao Do you think we should resort back to allocating an `ArrayList` if our long cannot encode the descriptor's offset info? I think you can add a case of 10 args like `([III.Z[B..[.[B)` and see if your patch improves over the baseline. You can add this case to `MethodTypeDescFactories` too.

<details>
<summary>
Only look at this if your approach is still slower than ArrayList allocation in my given simpler case.
</summary>
My proposal for `paramTypes` is like:

 - Fast return if empty
 - Fast loop to fill up `lengths`
   - Break if we encounter the 9th params or a long field descriptor
 - At break, if `cur == len`, means fast path works, we go through fast path
 - Else, we fall back to the old path of using `ArrayList`; add elements collected in `lengths` and the element between the last of `lengths` and cur (so it's either 9th element or too long)

This approach completely avoids a 2nd scan.
</details>

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

PR Comment: https://git.openjdk.org/jdk/pull/20611#issuecomment-2295038272


More information about the core-libs-dev mailing list