Skip to content
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

Conversation

ehsankianifar
Copy link
Contributor

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.

@ehsankianifar ehsankianifar marked this pull request as draft April 23, 2024 18:33
@ehsankianifar
Copy link
Contributor Author

ehsankianifar commented Apr 23, 2024

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.
The call chain looks like this:

StringBuilder.toString() ->
   String<init>(StringBuilder) ->
      String<init>(AbstractStringBuilder, Void) ->
         Arrays.copyOfRange([BII) ->
            Arrays.copyOfRangeByte([BII) ->

It should be safe to just check if array is created in Arrays.copyOfRangeByte and the second caller is String<init>(AbstractStringBuilder, Void) since Arrays.copyOfRangeByte is a private method and is only called by Arrays.copyOfRange and also String<init>(AbstractStringBuilder, Void) is also a private constructor and called to convert StringBuilder to String.

This call chain is responsible for a significant portion of primitive array allocations and it increase non zero TLH utilization in some applications.

@r30shah

{
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();
Copy link
Contributor Author

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

Copy link
Contributor Author

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

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)
Copy link
Contributor Author

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!

Copy link
Contributor

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 ?

Copy link
Contributor Author

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.

@ehsankianifar
Copy link
Contributor Author

Started sanity tests in jenkins personal 21810 for jdk11 and jdk21 all systems with batch clear and dualTLH enabled

Copy link
Contributor

@r30shah r30shah left a 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 Show resolved Hide resolved
runtime/compiler/ilgen/Walker.cpp Outdated Show resolved Hide resolved
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)
Copy link
Contributor

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 ?

@ehsankianifar
Copy link
Contributor Author

Tested for both UTF16 and Latin1 strings.

@ehsankianifar ehsankianifar force-pushed the SkipZeroTLHWhenStringBuilderToStringCopyArray branch 2 times, most recently from 49ba441 to b520865 Compare May 2, 2024 13:44
@ehsankianifar ehsankianifar force-pushed the SkipZeroTLHWhenStringBuilderToStringCopyArray branch from b520865 to de183e2 Compare May 6, 2024 23:21
@ehsankianifar ehsankianifar marked this pull request as ready for review May 6, 2024 23:24
@ehsankianifar
Copy link
Contributor Author

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.

@ehsankianifar
Copy link
Contributor Author

@r30shah The pr is rebased and ready!

@knn-k knn-k added the comp:jit label May 7, 2024
@r30shah r30shah self-assigned this May 10, 2024
Copy link
Contributor

@r30shah r30shah left a 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

runtime/compiler/ilgen/Walker.cpp Outdated Show resolved Hide resolved
runtime/compiler/ilgen/Walker.cpp Outdated Show resolved Hide resolved
runtime/compiler/ilgen/Walker.cpp Outdated Show resolved Hide resolved
Copy link
Contributor

@r30shah r30shah left a 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

@ehsankianifar ehsankianifar force-pushed the SkipZeroTLHWhenStringBuilderToStringCopyArray branch from de183e2 to d4fcd33 Compare May 16, 2024 15:38
@ehsankianifar
Copy link
Contributor Author

Thanks @r30shah for your review. I addressed all of your comments. Please let me know if the changes look good to you.

@ehsankianifar ehsankianifar force-pushed the SkipZeroTLHWhenStringBuilderToStringCopyArray branch from d4fcd33 to aa99067 Compare May 22, 2024 15:06
@r30shah
Copy link
Contributor

r30shah commented Jun 10, 2024

jenkins test sanity all jdk21

Copy link
Contributor

@r30shah r30shah left a 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.

runtime/compiler/ilgen/Walker.cpp Outdated Show resolved Hide resolved
runtime/compiler/ilgen/Walker.cpp Outdated Show resolved Hide resolved
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]>
@ehsankianifar ehsankianifar force-pushed the SkipZeroTLHWhenStringBuilderToStringCopyArray branch from aa99067 to 03850dd Compare June 11, 2024 13:03
@r30shah
Copy link
Contributor

r30shah commented Jun 11, 2024

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 :
https://openj9-jenkins.osuosl.org/view/Pull%20Requests/job/PullRequest-OpenJ9/5617/

@r30shah
Copy link
Contributor

r30shah commented Jun 28, 2024

jenkins test sanity all jdk21

@r30shah r30shah merged commit 289a133 into eclipse-openj9:master Jul 4, 2024
17 checks passed
@r30shah
Copy link
Contributor

r30shah commented Jul 4, 2024

Merging this change as all the tests passes.

@ehsankianifar ehsankianifar deleted the SkipZeroTLHWhenStringBuilderToStringCopyArray branch July 15, 2024 19:42
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants