On Fri, 16 Aug 2024 08:53:38 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.

Also note on ofDescriptor's use at bootstrap: This can be completely avoided as 
there should be no need for this method.

I think I have diagnosed the cause of such calls: one is here 
https://github.com/liachmodded/jdk/commit/64fd147bddd085fa3caa437a2b25a3dda3bb517a,
 and there are a few others in `SwitchBootstraps`. I need to check if there are 
still any other bootstrap usages after these are eliminated.

https://github.com/wenshao/jdk/pull/10
Sent a patch to allow fast parsing of 1 more parameter

src/java.base/share/classes/jdk/internal/constant/ConstantUtils.java line 296:

> 294:         // objectDesc appears a lot during the bootstrap process, so 
> optimize it
> 295:         String objectDesc = "Ljava/lang/Object;";
> 296:         if (len == objectDesc.length() && 
> descriptor.regionMatches(start, objectDesc, 0, len)) {

Note that from my bytestack investigations, `regionMatches` can be 
CPU-intensive like hashCode calculation. Running this trick against 
`Ljava/lang/String;` (same length) might cause a lot of misses and waste a lot 
of CPU time. Can you try on a case with many `Ljava/lang/String;` descriptors 
and see the results compared to the build without this special case?

src/java.base/share/classes/jdk/internal/constant/ConstantUtils.java line 304:

> 302:             throw new IllegalArgumentException("Bad method descriptor: " 
> + descriptor);
> 303:         ptypes.set(0, resolveClassDesc(descriptor, cur, rLen));
> 304:         return ptypes;

You can remove `parseMethodDescriptor` now

src/java.base/share/classes/jdk/internal/constant/MethodTypeDescImpl.java line 
108:

> 106:      */
> 107:     public static MethodTypeDescImpl ofDescriptor(String descriptor) {
> 108:         int rightBracket = descriptor.indexOf(')');

Would `lastIndexOf` be better for general purposes, since there's just one 
descriptor to the right?

src/java.base/share/classes/jdk/internal/constant/MethodTypeDescImpl.java line 
111:

> 109:         int len = descriptor.length() - rightBracket - 1;
> 110:         if (rightBracket <= 0 || descriptor.charAt(0) != '(' || len == 0
> 111:                 || len != 
> ConstantUtils.skipOverFieldSignature(descriptor, rightBracket + 1, 
> descriptor.length(), true)

I think this is the only place we still need this `true`; inlining the `V` 
return type check here (should be simple, we just need to check `len == 1 && 
descriptor.charAt(rightBracket + 1) == 'V'` and rejecting `V` in 
`skipOverFieldSignature`) might (or might not) help C2 inlining.

src/java.base/share/classes/jdk/internal/constant/MethodTypeDescImpl.java line 
113:

> 111:                 || (rightBracket = (descriptor.charAt(1) == ')' ? 1 : 
> descriptor.lastIndexOf(')'))) <= 0
> 112:                 || (len = descriptor.length() - rightBracket - 1) == 0
> 113:                 || (len != 1 && len != 
> ConstantUtils.skipOverFieldSignature(descriptor, rightBracket + 1, 
> descriptor.length()))

Has this be tested against input values like `()A`? I would prefer to fail 
explicitly with `descriptor.charAt(rightBracket + 1) == 'V'` instead of using 
`len != 1` and relying on `Wrapper.forBasicType`.

src/java.base/share/classes/jdk/internal/constant/MethodTypeDescImpl.java line 
125:

> 123:         int paramCount = 0;
> 124:         for (int cur = start; cur < end; ) {
> 125:             int len = ConstantUtils.skipOverFieldSignature(descriptor, 
> cur, end, false);

I recall skipping over signatures is the main reason descriptor parsing is 
slow. What is the benchmark result if you allocate a buffer array like `short[] 
offsets = new short[Math.min(end - start, 255)]` or a `BitSet offsets = new 
BitSet(end - start)`?

src/java.base/share/classes/jdk/internal/constant/MethodTypeDescImpl.java line 
145:

> 143:     }
> 144: 
> 145:     private static void badMethodDescriptor(String descriptor) {

We usually make such utilities return an exception that we can throw in control 
flow; this trick is that otherwise, compiler will still ask for a return value 
after this `badMethodDescriptor` call which can be annoying sometimes.

src/java.base/share/classes/jdk/internal/constant/MethodTypeDescImpl.java line 
158:

> 156:             }
> 157:             if (len == 0) {
> 158:                 len = ConstantUtils.skipOverFieldSignature(descriptor, 
> cur, end);

This can use a simpler version of skip that doesn't validate, like:

int start = cur;
while (descriptor.charAt(cur) == '[') {
    cur++;
}
if (descriptor.charAt(cur++) == 'L') {
    cur = descriptor.indexOf(';', cur) + 1;
}
paramTypes[paramIndex++] = ConstantUtils.resolveClassDesc(descriptor, start, 
cur - start);

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

PR Comment: https://git.openjdk.org/jdk/pull/20611#issuecomment-2295266083
PR Comment: https://git.openjdk.org/jdk/pull/20611#issuecomment-2295529521
PR Review Comment: https://git.openjdk.org/jdk/pull/20611#discussion_r1721097556
PR Review Comment: https://git.openjdk.org/jdk/pull/20611#discussion_r1719841359
PR Review Comment: https://git.openjdk.org/jdk/pull/20611#discussion_r1719838312
PR Review Comment: https://git.openjdk.org/jdk/pull/20611#discussion_r1719842641
PR Review Comment: https://git.openjdk.org/jdk/pull/20611#discussion_r1720768235
PR Review Comment: https://git.openjdk.org/jdk/pull/20611#discussion_r1719836907
PR Review Comment: https://git.openjdk.org/jdk/pull/20611#discussion_r1719849066
PR Review Comment: https://git.openjdk.org/jdk/pull/20611#discussion_r1720767719

Reply via email to