-
Notifications
You must be signed in to change notification settings - Fork 722
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
Skip zero init when copied array is the result of StringBuilder.toString #19370
Skip zero init when copied array is the result of StringBuilder.toString #19370
Conversation
When StringBuilder.toString is called, it creates a new array and copy the string into that. Since the size of string builder value is always grater than or equal to the size of resulting string, we can allocate the copy array from the non zero TLH.
It should be safe to just check if array is created in This call chain is responsible for a significant portion of primitive array allocations and it increase non zero TLH utilization in some applications. |
runtime/compiler/ilgen/Walker.cpp
Outdated
{ | ||
int32_t callerIndex = comp()->getCurrentInlinedCallSite()->_byteCodeInfo.getCallerIndex(); | ||
int32_t callerOfCallerIndex = comp()->getInlinedCallSite(callerIndex)._byteCodeInfo.getCallerIndex(); | ||
TR::ResolvedMethodSymbol *callerOfCaller = callerOfCallerIndex > -1 ? comp()->getInlinedResolvedMethodSymbol(callerOfCallerIndex) : comp()->getOptimizer()->getMethodSymbol(); |
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.
When the index is -1, I thought I could use comp()->getMethodSymbol()
but it returns the symbol for the inner most method (copyOfRangeByte
) unless TR_DisableReturnCalleeInIlgen
env var is set. It is funny that when I use comp()->getMethodSymbol()
in dynamic debug counters it returns the outer most method symbol (index -1) but when use it here it returns the inner most method symbol! comp()->getOptimizer()->getMethodSymbol()
seemingly works fine but I need to check with @r30shah
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 question is still valid
runtime/compiler/ilgen/Walker.cpp
Outdated
int32_t callerOfCallerIndex = comp()->getInlinedCallSite(callerIndex)._byteCodeInfo.getCallerIndex(); | ||
TR::ResolvedMethodSymbol *callerOfCaller = callerOfCallerIndex > -1 ? comp()->getInlinedResolvedMethodSymbol(callerOfCallerIndex) : comp()->getOptimizer()->getMethodSymbol(); | ||
|
||
if(callerOfCaller->getRecognizedMethod() == TR::java_lang_String_init_AbstractStringBuilder_Void) |
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 only check if the call chain is String(ASB, Void) -> Anything -> copyOfRangeByte
the assumption is that the mission is to convert String builder to string when that pattern is observed but it may not be a safe assumption!
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.
Can you elaborate bit more, what you meant by this assumption ?
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 mean the purpose of String(ASB, Void) is to convert StringBuilder to String and the size of the resulting string can not be more than the string builder value. Therefore, when I see copyOfRangeByte
with second caller String(ASB, Void)
, I assume it is safe to skip zero init.
Started sanity tests in jenkins personal 21810 for jdk11 and jdk21 all systems with batch clear and dualTLH enabled |
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.
Some initial thoughts. I will also checkout the call chain to evaluate if this is a safe to do or not.
runtime/compiler/ilgen/Walker.cpp
Outdated
int32_t callerOfCallerIndex = comp()->getInlinedCallSite(callerIndex)._byteCodeInfo.getCallerIndex(); | ||
TR::ResolvedMethodSymbol *callerOfCaller = callerOfCallerIndex > -1 ? comp()->getInlinedResolvedMethodSymbol(callerOfCallerIndex) : comp()->getOptimizer()->getMethodSymbol(); | ||
|
||
if(callerOfCaller->getRecognizedMethod() == TR::java_lang_String_init_AbstractStringBuilder_Void) |
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.
Can you elaborate bit more, what you meant by this assumption ?
Tested for both UTF16 and Latin1 strings. |
49ba441
to
b520865
Compare
b520865
to
de183e2
Compare
Tested on all platform for java11 and java21 when batch clearing and dual TLH were enabled by default (21920). Most tests passed. The failed tests were unrelated to the changes and passed on retry. |
@r30shah The pr is rebased and ready! |
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.
Minor changes, Overall looks OK to me
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.
Also, can we update the commit message with more useful information. I would emphasize the point that when a new array is allocated inside copyOfRangeByte which is inlined (In the call chain you are checking), it will write into complete array hence can skip allocation from zero memory
de183e2
to
d4fcd33
Compare
Thanks @r30shah for your review. I addressed all of your comments. Please let me know if the changes look good to you. |
d4fcd33
to
aa99067
Compare
jenkins test sanity all jdk21 |
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.
Change looks OK to me, some minor change in comment/function name. Please wait for build to finish before you make change and push it.
When a new array is created in Arrays.copyOfRangeByte and it is inlined into: Arrays.copyOfRange -> String<init>(AbstractStringBuilder, Void) -> String<init>(StringBuilder) -> StringBuilder.toString() we can skip zero initialization because it writes the whole range of the array. Signed-off-by: Ehsan Kiani Far <[email protected]>
aa99067
to
03850dd
Compare
FYI @vijaysun-omr - This change of Ehsan recognizes a call chain that was seen consistently Spring Petclinic app when ran in Liberty. As the part of the call chain when reaches to copyOfRangeByte allocates new array and writes it completely - It can be allocated from non zero initialized heap. Re: #19370 (comment) - Build link : |
jenkins test sanity all jdk21 |
Merging this change as all the tests passes. |
StringBuilder.toString calls String(StringBuilder) which calls String(AbstractStringBuilder, void)
which calls Array.copyOfRange which calls
Array.copyOfRangeByte.
In this chain we can skip Zero alloc.