Skip to content

Use different G1GC options for small heaps #59667

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

Merged
merged 23 commits into from
Oct 6, 2020

Conversation

ebadyano
Copy link
Contributor

@ebadyano ebadyano commented Jul 15, 2020

Our benchmarks have demonstrated that Elasticsearch performs better with -XX:G1HeapRegionSize=4M and -XX:G1ReservePercent=15 options for the small heap sizes. With this commit we ergonomically choose different G1GC options for heap sizes smaller than (not including) 8GB.

Co-authored-by: Daniel Mitterdorfer [email protected]

    Our benchmarks have demonstrated that Elasticsearch performs better with
    the parallel collector than with the default G1 collector when the heap
    size is small. With this commit we ergonomically choose the parallel
    collector for heap sizes smaller than (and including) 4GB and turn real
    memory circuit breaker off when running with  ParallelGC.
Co-authored-by: Evgenia Badiyanova <[email protected]>
Co-authored-by: Daniel Mitterdorfer <[email protected]>
@ebadyano ebadyano added :Core/Infra/Core Core issues without another label >enhancement labels Jul 15, 2020
@@ -42,7 +42,6 @@
# following three lines to your version of the JDK
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Drive by comment (sorry!), but the comment in jvm.options would be incorrect after this change, since the user would no longer be uncommenting the lines to use G1

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

hmm, I wonder what we want to add instead.. we want to users to be able to use G1GC before jdk 14, but if they specifically add -XX:+UseG1GC the ergonomics choice won't kick in..

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Isn't the point fo the change to make ergonomics choose which GC to use? It doesn't just have to be based on small heaps, it can be based on java version as well?

Copy link
Contributor Author

@ebadyano ebadyano Jul 29, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I updated the jvm.options, any comments/thoughts are welcome!

return ergonomicChoices;
}

private static final Pattern OPTION = Pattern.compile(
"^\\s*\\S+\\s+(?<flag>\\S+)\\s+:?=\\s+(?<value>\\S+)?\\s+\\{[^}]+?\\}\\s+\\{[^}]+}"
"^\\s*\\S+\\s+(?<flag>\\S+)\\s+:?=\\s+(?<value>\\S+)?\\s+\\{[^}]+?\\}\\s+\\{(?<origin>[^}]+)}"
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Drive-by comment, feel free to ignore - should we pass Pattern.COMMENTS here so that we can break up the regex with whitespace, to make it easier to read?

Setting.boolSetting("indices.breaker.total.use_real_memory", true, Property.NodeScope);
Setting.boolSetting("indices.breaker.total.use_real_memory", settings -> {
// turn real memory circuit breaker off for ParallelGC
return String.valueOf(!JvmInfo.jvmInfo().useParallelGC().equals("true"));
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we use Booleans.parseBoolean? Also, please use == false for negation.

@@ -42,7 +42,6 @@
# following three lines to your version of the JDK
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Isn't the point fo the change to make ergonomics choose which GC to use? It doesn't just have to be based on small heaps, it can be based on java version as well?

@ebadyano ebadyano marked this pull request as ready for review July 31, 2020 16:20
@elasticmachine
Copy link
Collaborator

Pinging @elastic/es-core-infra (:Core/Infra/Core)

@elasticmachine elasticmachine added the Team:Core/Infra Meta label for core/infra team label Jul 31, 2020
# 8-13:-XX:-UseConcMarkSweepGC
# 8-13:-XX:-UseCMSInitiatingOccupancyOnly
14-:-XX:+UseG1GC
14-:-XX:G1ReservePercent=25
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

won't these G1 options break the system if parallelGC is selected?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

with -XX:+UseParallelGC java seem to ignore -XX:G1ReservePercent=25 and XX:InitiatingHeapOccupancyPercent them as long as -XX:+UseG1GC is not specified. On the other hand it might be confusing to have G1GC options there when running ParallelGC.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Have you tested if that is true for all G1 options? I wonder if we should completely move all GC options into ergonomics.

Copy link
Contributor

@henningandersen henningandersen left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am somewhat concerned about turning off the real memory circuit breaker for small heaps. I wonder if G1 GC tuning could be done instead to bring performance closer to what we get with parallel GC?

Setting.boolSetting("indices.breaker.total.use_real_memory", true, Property.NodeScope);
Setting.boolSetting("indices.breaker.total.use_real_memory", settings -> {
// turn real memory circuit breaker off for ParallelGC
return String.valueOf(Booleans.parseBoolean(JvmInfo.jvmInfo().useParallelGC()) == false);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In #46751, a check is made purely against the parent breaker, relying on this being the real memory circuit breaker. This PR disables that PR by default for small heaps.

I seem to remember seeing that approach (just checking the parent breaker, now that we have real memory circuit breaker) has come up a few times.

@ebadyano ebadyano changed the title Use the parallel collector for small heaps Use different G1GC options for small heaps Sep 29, 2020
Copy link
Contributor

@henningandersen henningandersen left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks @ebadyano , I added a couple of comments to the specific ergonomics chosen.

static boolean tuneG1GCForSmallHeap(final Map<String, JvmOption> finalJvmOptions, final long heapSize) {
JvmOption g1GC = finalJvmOptions.get("UseG1GC");
JvmOption g1GCHeapRegion = finalJvmOptions.get("G1HeapRegionSize");
return (heapSize <= 4L << 30 && g1GC.getMandatoryValue().equals("true") && g1GCHeapRegion.isCommandLineOrigin() == false);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we should turn the heap size check into heapSize < 8L << 30, since java before version 15 will round the region size down, causing a 7.99GB heap to have a region size of 2MB.

Copy link
Contributor Author

@ebadyano ebadyano Oct 2, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry missread the comment. Make sense, so the tuning options will be for heaps strictly smaller than 8G.

final boolean tuneG1GCForSmallHeap = tuneG1GCForSmallHeap(finalJvmOptions, heapSize);
final boolean tuneG1GCForLargeHeap = tuneG1GCForLargeHeap(finalJvmOptions, heapSize);
if (tuneG1GCForSmallHeap) {
ergonomicChoices.add("-XX:G1HeapRegionSize=4m");
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As mentioned on another channel, I think we should make reserve pct 15 rather than the default 10 - to stay safely clear of real memory circuit breaker exceptions.

I also think we should add in the IHOP always if it is not defined, just to be safe. It should not matter for performance since G1 will adaptively adjust the value.

14-:-XX:+UseG1GC
14-:-XX:G1ReservePercent=25
14-:-XX:InitiatingHeapOccupancyPercent=30
# 14-:-XX:+UseG1GC
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since we are not choosing the actual GC implementation in ergonomics, doesn't this need to stay uncommented?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Or if we are relying on G1GC being the default now, then do we still need this line in the jvm options at all? Will the ergonomics detect using G1GC when it is not explicitly added to the options?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you for catching this. I should've uncommented the line 14-:-XX:+UseG1GC. I think we still want to keep it uncommented in case someone wants to use G1GC with jdk older than jdk14. Then the comment above still makes sense.

Copy link
Contributor

@henningandersen henningandersen left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I added a couple of comments, otherwise looking good.

if (tuneG1GCForSmallHeap) {
ergonomicChoices.add("-XX:G1HeapRegionSize=4m");
ergonomicChoices.add("-XX:G1ReservePercent=15");
ergonomicChoices.add("-XX:InitiatingHeapOccupancyPercent=45");
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we should use 30 in this case too.

final long heapSize = extractHeapSize(finalJvmOptions);
final long maxDirectMemorySize = extractMaxDirectMemorySize(finalJvmOptions);
if (maxDirectMemorySize == 0) {
ergonomicChoices.add("-XX:MaxDirectMemorySize=" + heapSize / 2);
}
final boolean tuneG1GCForSmallHeap = tuneG1GCForSmallHeap(finalJvmOptions, heapSize);
final boolean tuneG1GCForLargeHeap = tuneG1GCForLargeHeap(finalJvmOptions, heapSize);
if (tuneG1GCForSmallHeap) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it is surprising that if you manually specify any of the 3 parameters you loose all 3. For instance if they decide to raise G1ReservePercent to 20, the region size adjustment is lost.

I think the check in tuneG1GCForSmallHeap should only check heap size and maybe that G1 is in use.

The code below should then only add options where there is no existing command line origin option.

Alternatively (to the last part), we could swap the two lines here:

to be:

        finalJvmOptions.addAll(ergonomicJvmOptions);
        finalJvmOptions.addAll(substitutedJvmOptions);

since that would ensure that the original command line options override the ergonomics picked here.

ergonomicChoices.add("-XX:G1HeapRegionSize=4m");
ergonomicChoices.add("-XX:G1ReservePercent=15");
ergonomicChoices.add("-XX:InitiatingHeapOccupancyPercent=45");
} else if (tuneG1GCForLargeHeap) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same comment as above, is surprising that if one of them is specified you loose the other. In particular if IHOP is specified, it seems quite dangerous to no longer set G1ReservePercent.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you for catching this, I updated the pr.

Copy link
Contributor

@henningandersen henningandersen left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM, thanks for the extra iteration @ebadyano .

One nit: I would like to have a test added too that using ParallelGC (or any other GC than G1) does not add any of the options. No need for an additional round and not mandatory for merging (can do as follow-up too if you prefer).

Copy link
Member

@rjernst rjernst left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM too

@ebadyano ebadyano merged commit b29423f into elastic:master Oct 6, 2020
@ebadyano
Copy link
Contributor Author

ebadyano commented Oct 6, 2020

@henningandersen @rjernst Thank you for the review!

ebadyano added a commit to ebadyano/rally-teams that referenced this pull request Oct 7, 2020
dliappis pushed a commit to dliappis/elasticsearch that referenced this pull request Oct 7, 2020
Our benchmarks have demonstrated that Elasticsearch performs better with `-XX:G1HeapRegionSize=4M` and `-XX:G1ReservePercent=15` options for the small heap sizes. With this commit we ergonomically choose different G1GC options for heap sizes smaller than (not including) 8GB.

Co-authored-by: Daniel Mitterdorfer [email protected]
ebadyano added a commit to elastic/rally-teams that referenced this pull request Oct 8, 2020
ebadyano added a commit that referenced this pull request Oct 13, 2020
Our benchmarks have demonstrated that Elasticsearch performs better with
`-XX:G1HeapRegionSize=4M` and `-XX:G1ReservePercent=15` options for the
small heap sizes. With this commit we ergonomically choose different
G1GC options for heap sizes smaller than (not including) 8GB.

Co-authored-by: Daniel Mitterdorfer [email protected]
ebadyano added a commit to elastic/rally-teams that referenced this pull request Oct 13, 2020
@jpountz
Copy link
Contributor

jpountz commented Oct 14, 2020

I added the release highlight label given the great effect on nightly benchmarks.

@YohanSciubukgian
Copy link

YohanSciubukgian commented Jan 19, 2021

I'm interested of the impact on the memory usage for tiny heap size <=1Gb.
@ebadyano / @jpountz, Do you have any public link to share about benchmarks (before/after this PR) you are talking about ?
Is this PR applies the dynamic rule to ES for JDK8+ ?
Do you have any further recommendations for tiny heap size (<=1Gb) ?

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.

10 participants