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