فهرست منبع

Use different G1GC options for small heaps (#59667)

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 daniel.mitterdorfer@elastic.co
Evgenia Badyanova 5 سال پیش
والد
کامیت
b29423f6a0

+ 0 - 2
distribution/src/config/jvm.options

@@ -43,8 +43,6 @@
 # 8-13:-XX:-UseConcMarkSweepGC
 # 8-13:-XX:-UseCMSInitiatingOccupancyOnly
 14-:-XX:+UseG1GC
-14-:-XX:G1ReservePercent=25
-14-:-XX:InitiatingHeapOccupancyPercent=30
 
 ## JVM temporary directory
 -Djava.io.tmpdir=${ES_TMPDIR}

+ 76 - 9
distribution/tools/launchers/src/main/java/org/elasticsearch/tools/launchers/JvmErgonomics.java

@@ -53,25 +53,62 @@ final class JvmErgonomics {
      */
     static List<String> choose(final List<String> userDefinedJvmOptions) throws InterruptedException, IOException {
         final List<String> ergonomicChoices = new ArrayList<>();
-        final Map<String, Optional<String>> finalJvmOptions = finalJvmOptions(userDefinedJvmOptions);
+        final Map<String, JvmOption> finalJvmOptions = finalJvmOptions(userDefinedJvmOptions);
         final long heapSize = extractHeapSize(finalJvmOptions);
         final long maxDirectMemorySize = extractMaxDirectMemorySize(finalJvmOptions);
         if (maxDirectMemorySize == 0) {
             ergonomicChoices.add("-XX:MaxDirectMemorySize=" + heapSize / 2);
         }
+
+        final boolean tuneG1GCForSmallHeap = tuneG1GCForSmallHeap(heapSize);
+        final boolean tuneG1GCHeapRegion = tuneG1GCHeapRegion(finalJvmOptions, tuneG1GCForSmallHeap);
+        final boolean tuneG1GCInitiatingHeapOccupancyPercent = tuneG1GCInitiatingHeapOccupancyPercent(finalJvmOptions);
+        final int tuneG1GCReservePercent = tuneG1GCReservePercent(finalJvmOptions, tuneG1GCForSmallHeap);
+
+        if (tuneG1GCHeapRegion) {
+            ergonomicChoices.add("-XX:G1HeapRegionSize=4m");
+        }
+        if (tuneG1GCInitiatingHeapOccupancyPercent) {
+            ergonomicChoices.add("-XX:InitiatingHeapOccupancyPercent=30");
+        }
+        if (tuneG1GCReservePercent != 0) {
+            ergonomicChoices.add("-XX:G1ReservePercent=" + tuneG1GCReservePercent);
+        }
+
         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>[^}]+)}"
     );
 
-    static Map<String, Optional<String>> finalJvmOptions(final List<String> userDefinedJvmOptions) throws InterruptedException,
-        IOException {
+    private static class JvmOption {
+        private final String value;
+        private final String origin;
+
+        JvmOption(String value, String origin) {
+            this.value = value;
+            this.origin = origin;
+        }
+
+        public Optional<String> getValue() {
+            return Optional.ofNullable(value);
+        }
+
+        public String getMandatoryValue() {
+            return value;
+        }
+
+        public boolean isCommandLineOrigin() {
+            return "command line".equals(this.origin);
+        }
+    }
+
+    static Map<String, JvmOption> finalJvmOptions(final List<String> userDefinedJvmOptions) throws InterruptedException, IOException {
         return flagsFinal(userDefinedJvmOptions).stream()
             .map(OPTION::matcher)
             .filter(Matcher::matches)
-            .collect(Collectors.toUnmodifiableMap(m -> m.group("flag"), m -> Optional.ofNullable(m.group("value"))));
+            .collect(Collectors.toUnmodifiableMap(m -> m.group("flag"), m -> new JvmOption(m.group("value"), m.group("origin"))));
     }
 
     private static List<String> flagsFinal(final List<String> userDefinedJvmOptions) throws InterruptedException, IOException {
@@ -116,12 +153,42 @@ final class JvmErgonomics {
     }
 
     // package private for testing
-    static Long extractHeapSize(final Map<String, Optional<String>> finalJvmOptions) {
-        return Long.parseLong(finalJvmOptions.get("MaxHeapSize").get());
+    static Long extractHeapSize(final Map<String, JvmOption> finalJvmOptions) {
+        return Long.parseLong(finalJvmOptions.get("MaxHeapSize").getMandatoryValue());
+    }
+
+    static long extractMaxDirectMemorySize(final Map<String, JvmOption> finalJvmOptions) {
+        return Long.parseLong(finalJvmOptions.get("MaxDirectMemorySize").getMandatoryValue());
+    }
+
+    // Tune G1GC options for heaps < 8GB
+    static boolean tuneG1GCForSmallHeap(final long heapSize) {
+        return heapSize < 8L << 30;
+    }
+
+    static boolean tuneG1GCHeapRegion(final Map<String, JvmOption> finalJvmOptions, final boolean tuneG1GCForSmallHeap) {
+        JvmOption g1GCHeapRegion = finalJvmOptions.get("G1HeapRegionSize");
+        JvmOption g1GC = finalJvmOptions.get("UseG1GC");
+        return (tuneG1GCForSmallHeap && g1GC.getMandatoryValue().equals("true") && g1GCHeapRegion.isCommandLineOrigin() == false);
+    }
+
+    static int tuneG1GCReservePercent(final Map<String, JvmOption> finalJvmOptions, final boolean tuneG1GCForSmallHeap) {
+        JvmOption g1GC = finalJvmOptions.get("UseG1GC");
+        JvmOption g1GCReservePercent = finalJvmOptions.get("G1ReservePercent");
+        if (g1GC.getMandatoryValue().equals("true")) {
+            if (g1GCReservePercent.isCommandLineOrigin() == false && tuneG1GCForSmallHeap) {
+                return 15;
+            } else if (g1GCReservePercent.isCommandLineOrigin() == false && tuneG1GCForSmallHeap == false) {
+                return 25;
+            }
+        }
+        return 0;
     }
 
-    static long extractMaxDirectMemorySize(final Map<String, Optional<String>> finalJvmOptions) {
-        return Long.parseLong(finalJvmOptions.get("MaxDirectMemorySize").get());
+    static boolean tuneG1GCInitiatingHeapOccupancyPercent(final Map<String, JvmOption> finalJvmOptions) {
+        JvmOption g1GC = finalJvmOptions.get("UseG1GC");
+        JvmOption g1GCInitiatingHeapOccupancyPercent = finalJvmOptions.get("InitiatingHeapOccupancyPercent");
+        return g1GCInitiatingHeapOccupancyPercent.isCommandLineOrigin() == false && g1GC.getMandatoryValue().equals("true");
     }
 
     private static final Pattern SYSTEM_PROPERTY = Pattern.compile("^-D(?<key>[\\w+].*?)=(?<value>.*)$");

+ 40 - 0
distribution/tools/launchers/src/test/java/org/elasticsearch/tools/launchers/JvmErgonomicsTests.java

@@ -23,6 +23,7 @@ import java.io.IOException;
 import java.util.Arrays;
 import java.util.Collections;
 import java.util.HashMap;
+import java.util.List;
 import java.util.Map;
 
 import static org.hamcrest.Matchers.containsString;
@@ -113,6 +114,45 @@ public class JvmErgonomicsTests extends LaunchersTestCase {
         assertEquals(expectedSystemProperties, parsedSystemProperties);
     }
 
+    public void testG1GOptionsForSmallHeap() throws InterruptedException, IOException {
+        List<String> jvmErgonomics = JvmErgonomics.choose(Arrays.asList("-Xms6g", "-Xmx6g", "-XX:+UseG1GC"));
+        assertThat(jvmErgonomics, hasItem("-XX:G1HeapRegionSize=4m"));
+        assertThat(jvmErgonomics, hasItem("-XX:InitiatingHeapOccupancyPercent=30"));
+        assertThat(jvmErgonomics, hasItem("-XX:G1ReservePercent=15"));
+    }
+
+    public void testG1GOptionsForSmallHeapWhenTuningSet() throws InterruptedException, IOException {
+        List<String> jvmErgonomics = JvmErgonomics.choose(
+            Arrays.asList("-Xms6g", "-Xmx6g", "-XX:+UseG1GC", "-XX:G1HeapRegionSize=4m", "-XX:InitiatingHeapOccupancyPercent=45")
+        );
+        assertThat(jvmErgonomics, everyItem(not(startsWith("-XX:G1HeapRegionSize="))));
+        assertThat(jvmErgonomics, everyItem(not(startsWith("-XX:InitiatingHeapOccupancyPercent="))));
+        assertThat(jvmErgonomics, hasItem("-XX:G1ReservePercent=15"));
+    }
+
+    public void testG1GOptionsForLargeHeap() throws InterruptedException, IOException {
+        List<String> jvmErgonomics = JvmErgonomics.choose(Arrays.asList("-Xms8g", "-Xmx8g", "-XX:+UseG1GC"));
+        assertThat(jvmErgonomics, hasItem("-XX:InitiatingHeapOccupancyPercent=30"));
+        assertThat(jvmErgonomics, hasItem("-XX:G1ReservePercent=25"));
+        assertThat(jvmErgonomics, everyItem(not(startsWith("-XX:G1HeapRegionSize="))));
+    }
+
+    public void testG1GOptionsForSmallHeapWhenOtherGCSet() throws InterruptedException, IOException {
+        List<String> jvmErgonomics = JvmErgonomics.choose(Arrays.asList("-Xms6g", "-Xmx6g", "-XX:+UseParallelGC"));
+        assertThat(jvmErgonomics, everyItem(not(startsWith("-XX:G1HeapRegionSize="))));
+        assertThat(jvmErgonomics, everyItem(not(startsWith("-XX:InitiatingHeapOccupancyPercent="))));
+        assertThat(jvmErgonomics, everyItem(not(startsWith("-XX:G1ReservePercent="))));
+    }
+
+    public void testG1GOptionsForLargeHeapWhenTuningSet() throws InterruptedException, IOException {
+        List<String> jvmErgonomics = JvmErgonomics.choose(
+            Arrays.asList("-Xms8g", "-Xmx8g", "-XX:+UseG1GC", "-XX:InitiatingHeapOccupancyPercent=60", "-XX:G1ReservePercent=10")
+        );
+        assertThat(jvmErgonomics, everyItem(not(startsWith("-XX:InitiatingHeapOccupancyPercent="))));
+        assertThat(jvmErgonomics, everyItem(not(startsWith("-XX:G1ReservePercent="))));
+        assertThat(jvmErgonomics, everyItem(not(startsWith("-XX:G1HeapRegionSize="))));
+    }
+
     public void testExtractNoSystemProperties() {
         Map<String, String> parsedSystemProperties = JvmErgonomics.extractSystemProperties(Arrays.asList("-Xms1024M", "-Xmx1024M"));
         assertTrue(parsedSystemProperties.isEmpty());