On Sat, 17 Aug 2024 16:27:50 GMT, Shaojin Wen <d...@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