-
Notifications
You must be signed in to change notification settings - Fork 5.5k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
8338544: Dedicated Array class descriptor implementation #20665
base: master
Are you sure you want to change the base?
Conversation
👋 Welcome back liach! A progress list of the required criteria for merging this PR into |
❗ This change is not yet ready to be integrated. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Apparently, changing method default
‑ness requires a CSR (even for fully sealed hierarchies), but I can’t find the PR where I was informed of this.
Using return this instanceof <type>ClassDescImpl
might allow C2 to better avoid megamorphic virtual invocations (see GH‑17488), but this needs to be benchmarked:
I know this requires a CSR; it's just not created due to this still being a draft. The main purpose of this is still for speeding up stack map generation; the results are not confirmed yet, so this patch is on hold. For megamorphic call site thing, the regular workload would be like:
I think polymorphism is fine here, as after the check we usually go straight to perform type-specific operations like |
Confirmed this is performance-wise neutral for startup. Ready for review. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM. Always a bit queasy to add a third to a nice pair of classes, but if this ever shows up as a problem on benchmarks we can revisit and think of alternatives (such as adding a rank field in each impl).
private static final ClassDesc CD_Object_array = ReferenceClassDescImpl.ofValidated("[Ljava/lang/Object;"); | ||
private static final ClassDesc CD_MethodHandle_array = ReferenceClassDescImpl.ofValidated("[Ljava/lang/invoke/MethodHandle;"); | ||
private static final ClassDesc CD_MethodHandle_array2 = ReferenceClassDescImpl.ofValidated("[[Ljava/lang/invoke/MethodHandle;"); | ||
private static final ClassDesc CD_Object_array = CD_Object.arrayType(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I guess CD_Object.arrayType()
shows up often enough now - even once in java.lang.constant.ConstantDescs
- that we might as well pin it down as a constant somewhere (ConstantDescs
is a candidate location, but that will take a CSR).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This patch already has a CSR for trivial signature changes. The real difficulty lies in how we should name our new array class descriptors, Object_array
or ObjectArray
or what else?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That said, can you leave a quick review on CSR https://bugs.openjdk.org/browse/JDK-8340963 too?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think I will do this in another patch that adds it to ConstantDescs
- there's a place in ConstantDescs
that could have used it, but if I put it in ConstantUtils
I fear we are at risk of circular initialization dependencies.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nice cleanup! I'm just not really sure about the new subtype relationship (see inline comment)
public String descriptorString() { | ||
var desc = cachedDescriptorString; | ||
if (desc != null) | ||
return desc; | ||
|
||
return cachedDescriptorString = computeDescriptor(); | ||
} | ||
|
||
private String computeDescriptor() { | ||
var componentDesc = element.descriptorString(); | ||
StringBuilder sb = new StringBuilder(rank + componentDesc.length()); | ||
sb.repeat('[', rank); | ||
sb.append(componentDesc); | ||
return sb.toString(); | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is there really that much benefit in lazily computing the descriptor? ReferenceClassDescImpl
doesn't do this either... Maybe we can keep things simple and initialize the descriptor in the constructor?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This laziness is actually the main reason I had this implementation: In stack map generation, we need to compute the array descriptors of many types yet not using them in the end; the string computation involved a lot of allocations, especially with frequent arrayType()
and componentType()
calls.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ok, thanks. That's useful information.
// Class.forName is slow on class or interface arrays | ||
Class<?> clazz = element.resolveConstantDesc(lookup); | ||
for (int i = 0; i < rank; i++) | ||
clazz = clazz.arrayType(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looking at the implementation of arrayType
, it reflectively creates an array and then returns its class. Just makes me think we need a better we to look up an array class directly in the JDK :D
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Indeed; and it's more alarming that Class.forName("[[[[[Ljava.lang.Object;")
is slower than Object.class.arrayType().arrayType().arrayType().arrayType().arrayType()
.
src/java.base/share/classes/jdk/internal/constant/MethodTypeDescImpl.java
Show resolved
Hide resolved
src/java.base/share/classes/jdk/internal/constant/ReferenceClassDescImpl.java
Outdated
Show resolved
Hide resolved
@JornVernee I have updated the implementation class name and the factories for clarity. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks reasonable to me.
An alternative could be to put an int rank
in both PrimitiveClassDescImpl
and ReferenceClassDescImpl
. Would lead to slightly gnarlier code in places, but keep the number of classes down. Worth considering if any throughput benchmark sees fallout from going from 2 to 3 types.
The internalNameToDesc
changes could perhaps better have been done in a separate PR but no point teasing it apart now.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks good with minor suggestion. I agree having a separate implementation class for array type and class/interface allows a cleaner subclass implementation.
src/java.base/share/classes/jdk/internal/constant/ArrayClassDescImpl.java
Outdated
Show resolved
Hide resolved
if (rank == MAX_ARRAY_TYPE_DESC_DIMENSIONS) | ||
throw new IllegalStateException( | ||
"Cannot create an array type descriptor with more than " | ||
+ MAX_ARRAY_TYPE_DESC_DIMENSIONS + " dimensions"); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
if (rank == MAX_ARRAY_TYPE_DESC_DIMENSIONS) | |
throw new IllegalStateException( | |
"Cannot create an array type descriptor with more than " | |
+ MAX_ARRAY_TYPE_DESC_DIMENSIONS + " dimensions"); | |
ConstantUtils.validateArrayDepth(rank + 1); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Unfortunately, this has to throw ISE while validateArrayDepth
throws IAE.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I have extracted this to a new utility method, with a switch on the thrown exception type.
Thanks for the reviews; can anyone review the associated CSR at https://bugs.openjdk.org/browse/JDK-8340963 as well? |
public static void ensureRankPositive(int rank) { | ||
if (rank <= 0) { | ||
throw new IllegalArgumentException("rank " + rank + " is not a positive value"); | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Suggest to inline the rank argument check in the caller method which makes the check explicit to the reader.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
So like if (rank <= 0) throw ConstantUtils.rankNotPositive(rank);
at individual use sites?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I meant no need to have a utility method. Just do this:
public static void ensureRankPositive(int rank) { | |
if (rank <= 0) { | |
throw new IllegalArgumentException("rank " + rank + " is not a positive value"); | |
} | |
} | |
public static void validateArrayDepth(int rank) { | |
if (rank <= 0) { | |
throw new IllegalArgumentException("rank " + rank + " is not a positive value"); | |
} | |
validateMaxArrayDepth(rank, true); | |
} |
Same change in ArrayClassDescImpl::arrayType(int)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done. The negative rank check is pulled out separately in ArrayClassDescImpl::arrayType(int)
.
} | ||
|
||
/** | ||
* Returns a human-readable name for the type described by this descriptor. | ||
* | ||
* @implSpec | ||
* <p>The default implementation returns the simple name | ||
* <p>The implementations return the simple name |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
* <p>The implementations return the simple name | |
* <p>The implementation returns the simple name |
I think this can be singular as it's not required to have more than 1 implementation class.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hmm, in fact, should we still have this @implSpec
if we make this default?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think this can be dropped as ClassDesc
as well. It can say "This method returns....". See what CSR review would say.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Latest version looks good
@liach this pull request can not be integrated into git checkout feature/array-cd
git fetch https://git.openjdk.org/jdk.git master
git merge FETCH_HEAD
# resolve conflicts and follow the instructions given by git merge
git commit -m "Merge master"
git push |
@cl4es discovered that Stack Map generation in ClassFile API uses
componentType
andarrayType
foraaload
aastore
instructions, which are currently quite slow. We can split out array class descriptors from class or interfaces to support fasterarrayType
andcomponentType
operations.Tentative, as I currently have no way to measure the actual impact of this patch on the startup performance; however, this made the
ClassDesc
implementations much cleaner.Progress
Issues
Reviewers
Reviewing
Using
git
Checkout this PR locally:
$ git fetch https://git.openjdk.org/jdk.git pull/20665/head:pull/20665
$ git checkout pull/20665
Update a local copy of the PR:
$ git checkout pull/20665
$ git pull https://git.openjdk.org/jdk.git pull/20665/head
Using Skara CLI tools
Checkout this PR locally:
$ git pr checkout 20665
View PR using the GUI difftool:
$ git pr show -t 20665
Using diff file
Download this PR as a diff file:
https://git.openjdk.org/jdk/pull/20665.diff
Webrev
Link to Webrev Comment