diff --git a/src/hotspot/share/compiler/compilationPolicy.cpp b/src/hotspot/share/compiler/compilationPolicy.cpp index d95debeda44..39c90281c79 100644 --- a/src/hotspot/share/compiler/compilationPolicy.cpp +++ b/src/hotspot/share/compiler/compilationPolicy.cpp @@ -550,6 +550,7 @@ void CompilationPolicy::initialize() { int count = CICompilerCount; bool c1_only = CompilerConfig::is_c1_only(); bool c2_only = CompilerConfig::is_c2_or_jvmci_compiler_only(); + int min_count = (c1_only || c2_only) ? 1 : 2; #ifdef _LP64 // Turn on ergonomic compiler count selection @@ -560,7 +561,7 @@ void CompilationPolicy::initialize() { // Simple log n seems to grow too slowly for tiered, try something faster: log n * log log n int log_cpu = log2i(os::active_processor_count()); int loglog_cpu = log2i(MAX2(log_cpu, 1)); - count = MAX2(log_cpu * loglog_cpu * 3 / 2, 2); + count = MAX2(log_cpu * loglog_cpu * 3 / 2, min_count); // Make sure there is enough space in the code cache to hold all the compiler buffers size_t c1_size = 0; #ifdef COMPILER1 @@ -574,7 +575,7 @@ void CompilationPolicy::initialize() { int max_count = (ReservedCodeCacheSize - (CodeCacheMinimumUseSpace DEBUG_ONLY(* 3))) / (int)buffer_size; if (count > max_count) { // Lower the compiler count such that all buffers fit into the code cache - count = MAX2(max_count, c1_only ? 1 : 2); + count = MAX2(max_count, min_count); } FLAG_SET_ERGO(CICompilerCount, count); } @@ -593,9 +594,10 @@ void CompilationPolicy::initialize() { #endif if (c1_only) { - // No C2 compiler thread required + // No C2 compiler threads are needed set_c1_count(count); } else if (c2_only) { + // No C1 compiler threads are needed set_c2_count(count); } else { #if INCLUDE_JVMCI @@ -613,6 +615,9 @@ void CompilationPolicy::initialize() { } assert(count == c1_count() + c2_count(), "inconsistent compiler thread count"); set_increase_threshold_at_ratio(); + } else { + // Interpreter mode creates no compilers + FLAG_SET_ERGO(CICompilerCount, 0); } set_start_time(nanos_to_millis(os::javaTimeNanos())); } diff --git a/test/hotspot/jtreg/compiler/arguments/TestCompilerCounts.java b/test/hotspot/jtreg/compiler/arguments/TestCompilerCounts.java new file mode 100644 index 00000000000..420dc2b3414 --- /dev/null +++ b/test/hotspot/jtreg/compiler/arguments/TestCompilerCounts.java @@ -0,0 +1,177 @@ +/* + * Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * @test + * @library /test/lib / + * @bug 8356000 + * @requires vm.flagless + * @requires vm.bits == "64" + * @run driver compiler.arguments.TestCompilerCounts + */ + +package compiler.arguments; + +import java.io.IOException; +import java.util.List; +import java.util.Arrays; +import java.util.ArrayList; +import java.util.function.Function; +import jdk.test.lib.Asserts; +import jdk.test.lib.process.ProcessTools; +import jdk.test.lib.process.OutputAnalyzer; +import jdk.test.lib.cli.CommandLineOptionTest; + +public class TestCompilerCounts { + + // Try not to go over max CPU count on the machine, since we do not + // know if the rest of runtime would accept it. + // For local testing, feel free to override this to a larger value + // if you want to see how heuristics works on even larger machines. + static final int MAX_CPUS = Runtime.getRuntime().availableProcessors(); + + // Test at most 16 CPUs linearly, to limit test execution time. + // After this limit, go for exponential steps. + static final int MAX_LINEAR_CPUS = Math.min(16, MAX_CPUS); + + public static void main(String[] args) throws Throwable { + // CICompilerCount=0 is incorrect in default modes. + fail("-XX:CICompilerCount=0"); + + // Interpreter-only mode accepts all values, but sets 0 threads + pass(0, "-XX:CICompilerCount=1", "-XX:TieredStopAtLevel=0"); + + // C1/C2 only modes accept CICompilerCount=1 + pass(1, "-XX:CICompilerCount=1", "-XX:TieredStopAtLevel=1"); + pass(1, "-XX:CICompilerCount=1", "-XX:TieredStopAtLevel=2"); + pass(1, "-XX:CICompilerCount=1", "-XX:TieredStopAtLevel=3"); + pass(1, "-XX:CICompilerCount=1", "-XX:-TieredCompilation"); + + // C1+C2 modes need at least 2 threads + fail("-XX:CICompilerCount=1"); + fail("-XX:CICompilerCount=1", "-XX:TieredStopAtLevel=4"); + + // Overriding the CICompilerCount overrides compiler counts hard. + for (int count = 2; count <= MAX_CPUS; count += (count >= MAX_LINEAR_CPUS ? count : 1)) { + String opt = "-XX:CICompilerCount=" + count; + + // Interpreter-only mode always sets 0 threads + pass(0, opt, "-XX:TieredStopAtLevel=0"); + + // All compiled modes accept reasonable CICompilerCount + pass(count, opt); + pass(count, opt, "-XX:TieredStopAtLevel=1"); + pass(count, opt, "-XX:TieredStopAtLevel=2"); + pass(count, opt, "-XX:TieredStopAtLevel=3"); + pass(count, opt, "-XX:TieredStopAtLevel=4"); + pass(count, opt, "-XX:-TieredCompilation"); + } + + // Per CPU heuristics is disabled, we are going to set up defaults. + + for (int cpus = 2; cpus <= MAX_CPUS; cpus += (cpus >= MAX_LINEAR_CPUS ? cpus : 1)) { + String opt = "-XX:ActiveProcessorCount=" + cpus; + String opt2 = "-XX:-CICompilerCountPerCPU"; + + // Interpreter-only mode always set 0 threads + pass(0, opt, opt2, "-XX:TieredStopAtLevel=0"); + + // All compiled modes default to 2 threads, statically compiled in + pass(2, opt, opt2); + pass(2, opt, opt2, "-XX:TieredStopAtLevel=1"); + pass(2, opt, opt2, "-XX:TieredStopAtLevel=2"); + pass(2, opt, opt2, "-XX:TieredStopAtLevel=3"); + pass(2, opt, opt2, "-XX:TieredStopAtLevel=4"); + pass(2, opt, opt2, "-XX:-TieredCompilation"); + } + + // Otherwise, we set CICompilerCount heuristically. + + // Check hitting the lower values exactly first. + for (int cpus = 1; cpus <= 3; cpus++) { + String opt = "-XX:ActiveProcessorCount=" + cpus; + + // Interpreter-only mode always set 0 threads + pass(0, opt, "-XX:TieredStopAtLevel=0"); + + // Non-tiered modes set 1 thread + pass(1, opt, "-XX:TieredStopAtLevel=1"); + pass(1, opt, "-XX:TieredStopAtLevel=2"); + pass(1, opt, "-XX:TieredStopAtLevel=3"); + pass(1, opt, "-XX:-TieredCompilation"); + + // Tiered modes set 2 threads + pass(2, opt); + pass(2, opt, "-XX:TieredStopAtLevel=4"); + } + + // Check what heuristics sets past the trivial number of CPUs. + for (int cpus = 4; cpus <= MAX_CPUS; cpus += (cpus >= MAX_LINEAR_CPUS ? cpus : 1)) { + String opt = "-XX:ActiveProcessorCount=" + cpus; + + // Interpreter-only mode always set 0 threads + pass(0, opt, "-XX:TieredStopAtLevel=0"); + + // Non-tiered modes + int nonTieredCount = heuristicCount(cpus, false); + pass(nonTieredCount, opt, "-XX:TieredStopAtLevel=1"); + pass(nonTieredCount, opt, "-XX:TieredStopAtLevel=2"); + pass(nonTieredCount, opt, "-XX:TieredStopAtLevel=3"); + pass(nonTieredCount, opt, "-XX:-TieredCompilation"); + + // Tiered modes + int tieredCount = heuristicCount(cpus, true); + pass(tieredCount, opt); + pass(tieredCount, opt, "-XX:TieredStopAtLevel=4"); + + // Also check that heuristics did not set up more threads than CPUs available + Asserts.assertTrue(nonTieredCount <= cpus, + "Non-tiered count is larger than number of CPUs: " + nonTieredCount + " > " + cpus); + Asserts.assertTrue(tieredCount <= cpus, + "Tiered count is larger than number of CPUs: " + tieredCount + " > " + cpus); + } + } + + // Direct translation from CompilationPolicy::initialize: + public static int heuristicCount(int cpus, boolean tiered) { + int log_cpu = log2(cpus); + int loglog_cpu = log2(Math.max(log_cpu, 1)); + int min_count = tiered ? 2 : 1; + return Math.max(log_cpu * loglog_cpu * 3 / 2, min_count); + } + + public static int log2(int v) { + return (int)(Math.log(v) / Math.log(2)); + } + + public static void pass(int count, String... args) throws Throwable { + CommandLineOptionTest.verifyOptionValueForSameVM("CICompilerCount", "" + count, "", args); + } + + public static void fail(String... args) throws Throwable { + ProcessBuilder pb = ProcessTools.createLimitedTestJavaProcessBuilder(args); + OutputAnalyzer output = new OutputAnalyzer(pb.start()); + output.shouldNotHaveExitValue(0); + } + +}