-
Notifications
You must be signed in to change notification settings - Fork 25.2k
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
Use different G1GC options for small heaps #59667
Conversation
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]>
distribution/src/config/jvm.options
Outdated
@@ -42,7 +42,6 @@ | |||
# following three lines to your version of the JDK |
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.
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
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, 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..
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.
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?
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 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>[^}]+)}" |
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.
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")); |
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 we use Booleans.parseBoolean
? Also, please use == false
for negation.
distribution/src/config/jvm.options
Outdated
@@ -42,7 +42,6 @@ | |||
# following three lines to your version of the JDK |
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.
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?
…-ergonomics-parallelgc
…-ergonomics-parallelgc
…-ergonomics-parallelgc
…-ergonomics-parallelgc
Pinging @elastic/es-core-infra (:Core/Infra/Core) |
distribution/src/config/jvm.options
Outdated
# 8-13:-XX:-UseConcMarkSweepGC | ||
# 8-13:-XX:-UseCMSInitiatingOccupancyOnly | ||
14-:-XX:+UseG1GC | ||
14-:-XX:G1ReservePercent=25 |
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.
won't these G1 options break the system if parallelGC is selected?
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.
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.
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.
Have you tested if that is true for all G1 options? I wonder if we should completely move all GC options into ergonomics.
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 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); |
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.
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.
…-ergonomics-parallelgc
…-ergonomics-parallelgc
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.
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); |
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 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.
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.
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"); |
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.
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.
distribution/src/config/jvm.options
Outdated
14-:-XX:+UseG1GC | ||
14-:-XX:G1ReservePercent=25 | ||
14-:-XX:InitiatingHeapOccupancyPercent=30 | ||
# 14-:-XX:+UseG1GC |
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.
Since we are not choosing the actual GC implementation in ergonomics, doesn't this need to stay uncommented?
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.
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?
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.
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.
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 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"); |
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 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) { |
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 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:
Line 144 in 01dc110
finalJvmOptions.addAll(substitutedJvmOptions); |
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) { |
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.
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
.
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.
Thank you for catching this, I updated the pr.
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, 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).
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 too
@henningandersen @rjernst Thank you for the review! |
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 `-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]
I added the |
I'm interested of the impact on the memory usage for tiny heap size <=1Gb. |
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]