Skip to content

Commit f15d423

Browse files
swesongaDavid Holmes
authored andcommitted
6942632: Hotspot should be able to use more than 64 logical processors on Windows
Reviewed-by: jsjolen, dholmes
1 parent 1a50bd0 commit f15d423

File tree

10 files changed

+720
-58
lines changed

10 files changed

+720
-58
lines changed

make/test/JtregNativeHotspot.gmk

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1512,7 +1512,7 @@ else
15121512
BUILD_HOTSPOT_JTREG_EXECUTABLES_LIBS_exeGetCreatedJavaVMs := -lpthread
15131513
BUILD_HOTSPOT_JTREG_EXECUTABLES_JDK_LIBS_exeGetCreatedJavaVMs := java.base:libjvm
15141514

1515-
BUILD_HOTSPOT_JTREG_EXCLUDE += libNativeException.c
1515+
BUILD_HOTSPOT_JTREG_EXCLUDE += libNativeException.c exeGetProcessorInfo.c
15161516
endif
15171517

15181518
ifeq ($(ASAN_ENABLED), true)

src/hotspot/os/windows/globals_windows.hpp

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,9 @@
3535
range, \
3636
constraint) \
3737
\
38+
product(bool, UseAllWindowsProcessorGroups, false, \
39+
"Use all processor groups on supported Windows versions") \
40+
\
3841
product(bool, UseOSErrorReporting, false, \
3942
"Let VM fatal error propagate to the OS (ie. WER on Windows)")
4043

src/hotspot/os/windows/os_windows.cpp

Lines changed: 298 additions & 57 deletions
Large diffs are not rendered by default.

src/hotspot/os/windows/os_windows.hpp

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,13 @@ class os::win32 {
4343
static julong _physical_memory;
4444
static bool _is_windows_server;
4545
static bool _has_exit_bug;
46+
static bool _processor_group_warning_displayed;
47+
static bool _job_object_processor_group_warning_displayed;
48+
49+
static int _major_version;
50+
static int _minor_version;
51+
static int _build_number;
52+
static int _build_minor;
4653

4754
static void print_windows_version(outputStream* st);
4855
static void print_uptime_info(outputStream* st);
@@ -56,6 +63,37 @@ class os::win32 {
5663
// Windows-specific interface:
5764
static void initialize_system_info();
5865
static void setmode_streams();
66+
static bool is_windows_11_or_greater();
67+
static bool is_windows_server_2022_or_greater();
68+
static int windows_major_version() {
69+
assert(_major_version > 0, "windows version not initialized.");
70+
return _major_version;
71+
}
72+
static int windows_minor_version() {
73+
assert(_major_version > 0, "windows version not initialized.");
74+
return _minor_version;
75+
}
76+
static int windows_build_number() {
77+
assert(_major_version > 0, "windows version not initialized.");
78+
return _build_number;
79+
}
80+
static int windows_build_minor() {
81+
assert(_major_version > 0, "windows version not initialized.");
82+
return _build_minor;
83+
}
84+
85+
static void set_processor_group_warning_displayed(bool displayed) {
86+
_processor_group_warning_displayed = displayed;
87+
}
88+
static bool processor_group_warning_displayed() {
89+
return _processor_group_warning_displayed;
90+
}
91+
static void set_job_object_processor_group_warning_displayed(bool displayed) {
92+
_job_object_processor_group_warning_displayed = displayed;
93+
}
94+
static bool job_object_processor_group_warning_displayed() {
95+
return _job_object_processor_group_warning_displayed;
96+
}
5997

6098
// Processor info as provided by NT
6199
static int processor_type() { return _processor_type; }
@@ -72,6 +110,8 @@ class os::win32 {
72110
private:
73111

74112
static void initialize_performance_counter();
113+
static void initialize_windows_version();
114+
static DWORD active_processors_in_job_object(DWORD* active_processor_groups = nullptr);
75115

76116
public:
77117
// Generic interface:

test/hotspot/gtest/runtime/test_os_windows.cpp

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -703,6 +703,25 @@ TEST_VM(os_windows, reserve_memory_special) {
703703
TestReserveMemorySpecial_test();
704704
}
705705

706+
TEST_VM(os_windows, processor_count) {
707+
JVMFlag* flag = JVMFlag::find_flag("UseAllWindowsProcessorGroups");
708+
EXPECT_NE(flag, nullptr) << "Expected UseAllWindowsProcessorGroups product flag to be available";
709+
710+
int processors = os::processor_count();
711+
EXPECT_GT(processors, 0) << "Expected at least 1 processor";
712+
713+
int active_processors = os::active_processor_count();
714+
EXPECT_GT(active_processors, 0) << "Expected at least 1 active processor";
715+
716+
bool schedules_all_processor_groups = os::win32::is_windows_11_or_greater() || os::win32::is_windows_server_2022_or_greater();
717+
if (schedules_all_processor_groups && UseAllWindowsProcessorGroups) {
718+
EXPECT_EQ(active_processors, processors) << "Expected all processors to be active";
719+
} else {
720+
// active_processors should be at most the number of processors in 1 Windows processor group.
721+
EXPECT_LE(active_processors, processors) << "Expected active processors to not exceed available processors";
722+
}
723+
}
724+
706725
class ReserveMemorySpecialRunnable : public TestRunnable {
707726
public:
708727
void runUnitTest() const {

test/hotspot/jtreg/TEST.groups

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -153,6 +153,7 @@ tier1_common = \
153153
gtest/MetaspaceGtests.java \
154154
gtest/LargePageGtests.java \
155155
gtest/NMTGtests.java \
156+
gtest/WindowsProcessorGroups.java
156157

157158
tier1_compiler = \
158159
:tier1_compiler_1 \
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
/*
2+
* Copyright (c) 2024, Oracle and/or its affiliates. All rights reserved.
3+
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4+
*
5+
* This code is free software; you can redistribute it and/or modify it
6+
* under the terms of the GNU General Public License version 2 only, as
7+
* published by the Free Software Foundation.
8+
*
9+
* This code is distributed in the hope that it will be useful, but WITHOUT
10+
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11+
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
12+
* version 2 for more details (a copy is included in the LICENSE file that
13+
* accompanied this code).
14+
*
15+
* You should have received a copy of the GNU General Public License version
16+
* 2 along with this work; if not, write to the Free Software Foundation,
17+
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
18+
*
19+
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
20+
* or visit www.oracle.com if you need additional information or have any
21+
* questions.
22+
*
23+
*/
24+
25+
/*
26+
* This runs the os related gtests on Windows with all processor groups enabled.
27+
*/
28+
29+
/* @test id=use-all-windows-processor-groups
30+
* @summary Run gtests with all Windows processor groups enabled
31+
* @library /test/lib
32+
* @requires os.family == "windows"
33+
* @run main/native GTestWrapper --gtest_filter=os* -XX:+UseAllWindowsProcessorGroups
34+
*/
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
/*
2+
* Copyright (c) 2024, Oracle and/or its affiliates. All rights reserved.
3+
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4+
*
5+
* This code is free software; you can redistribute it and/or modify it
6+
* under the terms of the GNU General Public License version 2 only, as
7+
* published by the Free Software Foundation.
8+
*
9+
* This code is distributed in the hope that it will be useful, but WITHOUT
10+
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11+
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
12+
* version 2 for more details (a copy is included in the LICENSE file that
13+
* accompanied this code).
14+
*
15+
* You should have received a copy of the GNU General Public License version
16+
* 2 along with this work; if not, write to the Free Software Foundation,
17+
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
18+
*
19+
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
20+
* or visit www.oracle.com if you need additional information or have any
21+
* questions.
22+
*
23+
*/
24+
25+
public class GetAvailableProcessors {
26+
public static void main(String[] args) {
27+
System.out.println("Runtime.availableProcessors: " + Runtime.getRuntime().availableProcessors());
28+
}
29+
}
Lines changed: 218 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,218 @@
1+
/*
2+
* Copyright (c) 2024, Oracle and/or its affiliates. All rights reserved.
3+
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4+
*
5+
* This code is free software; you can redistribute it and/or modify it
6+
* under the terms of the GNU General Public License version 2 only, as
7+
* published by the Free Software Foundation.
8+
*
9+
* This code is distributed in the hope that it will be useful, but WITHOUT
10+
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11+
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
12+
* version 2 for more details (a copy is included in the LICENSE file that
13+
* accompanied this code).
14+
*
15+
* You should have received a copy of the GNU General Public License version
16+
* 2 along with this work; if not, write to the Free Software Foundation,
17+
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
18+
*
19+
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
20+
* or visit www.oracle.com if you need additional information or have any
21+
* questions.
22+
*
23+
*/
24+
25+
/*
26+
* @test
27+
* @bug 6942632
28+
* @requires os.family == "windows"
29+
* @summary This test verifies that OpenJDK can use all available
30+
* processors on Windows 11/Windows Server 2022 and later.
31+
* @requires vm.flagless
32+
* @library /test/lib
33+
* @compile GetAvailableProcessors.java
34+
* @run testng TestAvailableProcessors
35+
*/
36+
37+
import java.io.IOException;
38+
import java.nio.file.Path;
39+
import java.nio.file.Paths;
40+
import java.util.ArrayList;
41+
import java.util.List;
42+
import java.util.HashSet;
43+
import java.util.Set;
44+
45+
import jdk.test.lib.Utils;
46+
import jdk.test.lib.process.OutputAnalyzer;
47+
import jdk.test.lib.process.ProcessTools;
48+
49+
import org.testng.Assert;
50+
import org.testng.annotations.Test;
51+
52+
public class TestAvailableProcessors {
53+
54+
private static final String totalProcessorCountMessage = "Active processor count across all processor groups: ";
55+
private static final String processorCountPerGroupMessage = "Active processors per group: ";
56+
private static final String isWindowsServerMessage = "IsWindowsServer: ";
57+
58+
private static final String runtimeAvailableProcessorsMessage = "Runtime.availableProcessors: ";
59+
private static final String osVersionMessage = "OS Version: ";
60+
private static final String unsupportedPlatformMessage = "The UseAllWindowsProcessorGroups flag is not supported on this Windows version and will be ignored.";
61+
62+
private static String getWindowsVersion() throws IOException {
63+
String systeminfoPath = "systeminfo.exe";
64+
65+
var processBuilder = new ProcessBuilder(systeminfoPath);
66+
OutputAnalyzer outputAnalyzer = new OutputAnalyzer(processBuilder.start());
67+
outputAnalyzer.shouldHaveExitValue(0);
68+
outputAnalyzer.shouldContain(osVersionMessage);
69+
List<String> lines = outputAnalyzer.stdoutAsLines();
70+
71+
String osVersion = null;
72+
for (var line: lines) {
73+
if (line.startsWith(osVersionMessage)) {
74+
osVersion = line.substring(osVersionMessage.length()).trim();
75+
break;
76+
}
77+
}
78+
79+
System.out.println("Found OS version: " + osVersion);
80+
return osVersion;
81+
}
82+
83+
private static boolean getSchedulesAllProcessorGroups(boolean isWindowsServer) throws IOException {
84+
String windowsVer = getWindowsVersion();
85+
String[] parts = windowsVer.split(" ");
86+
String[] versionParts = parts[0].split("\\.");
87+
88+
if (versionParts.length != 3) {
89+
throw new RuntimeException("Unexpected Windows version format.");
90+
}
91+
92+
int major = Integer.parseInt(versionParts[0]);
93+
int minor = Integer.parseInt(versionParts[1]);
94+
int build = Integer.parseInt(versionParts[2]);
95+
96+
if (major > 10) {
97+
return true;
98+
}
99+
100+
if (major < 10) {
101+
return false;
102+
}
103+
104+
if (minor > 0) {
105+
return true;
106+
}
107+
108+
if (isWindowsServer) {
109+
return build >= 20348;
110+
} else {
111+
return build >= 22000;
112+
}
113+
}
114+
115+
private static OutputAnalyzer getAvailableProcessorsOutput(boolean productFlagEnabled) throws IOException {
116+
String productFlag = productFlagEnabled ? "-XX:+UseAllWindowsProcessorGroups" : "-XX:-UseAllWindowsProcessorGroups";
117+
118+
ProcessBuilder processBuilder = ProcessTools.createLimitedTestJavaProcessBuilder(
119+
new String[] {productFlag, "GetAvailableProcessors"}
120+
);
121+
122+
var output = new OutputAnalyzer(processBuilder.start());
123+
output.shouldHaveExitValue(0);
124+
output.shouldContain(runtimeAvailableProcessorsMessage);
125+
126+
return output;
127+
}
128+
129+
private static int getAvailableProcessors(OutputAnalyzer outputAnalyzer) {
130+
int runtimeAvailableProcs = 0;
131+
List<String> output = outputAnalyzer.stdoutAsLines();
132+
133+
for (var line: output) {
134+
if (line.startsWith(runtimeAvailableProcessorsMessage)) {
135+
String runtimeAvailableProcsStr = line.substring(runtimeAvailableProcessorsMessage.length());
136+
runtimeAvailableProcs = Integer.parseInt(runtimeAvailableProcsStr);
137+
}
138+
}
139+
140+
return runtimeAvailableProcs;
141+
}
142+
143+
private static int getAvailableProcessors(boolean productFlagEnabled) throws IOException {
144+
OutputAnalyzer outputAnalyzer = getAvailableProcessorsOutput(productFlagEnabled);
145+
return getAvailableProcessors(outputAnalyzer);
146+
}
147+
148+
private static void verifyAvailableProcessorsWithDisabledProductFlag(Set<Integer> processorGroupSizes) throws IOException {
149+
boolean productFlagEnabled = false;
150+
int runtimeAvailableProcs = getAvailableProcessors(productFlagEnabled);
151+
152+
String error = String.format("Runtime.availableProcessors (%d) is not a valid processor group size on this machine.", runtimeAvailableProcs);
153+
Assert.assertTrue(processorGroupSizes.contains(runtimeAvailableProcs), error);
154+
}
155+
156+
private static void verifyAvailableProcessorsWithEnabledProductFlag(boolean schedulesAllProcessorGroups, int totalProcessorCount, Set<Integer> processorGroupSizes) throws IOException {
157+
boolean productFlagEnabled = true;
158+
159+
OutputAnalyzer outputAnalyzer = getAvailableProcessorsOutput(productFlagEnabled);
160+
int runtimeAvailableProcs = getAvailableProcessors(outputAnalyzer);
161+
162+
if (schedulesAllProcessorGroups) {
163+
String error = String.format("Runtime.availableProcessors (%d) is not equal to the expected total processor count (%d)", runtimeAvailableProcs, totalProcessorCount);
164+
Assert.assertEquals(runtimeAvailableProcs, totalProcessorCount, error);
165+
} else {
166+
outputAnalyzer.shouldContain(unsupportedPlatformMessage);
167+
168+
String error = String.format("Runtime.availableProcessors (%d) is not a valid processor group size on this machine.", runtimeAvailableProcs);
169+
Assert.assertTrue(processorGroupSizes.contains(runtimeAvailableProcs), error);
170+
}
171+
}
172+
173+
@Test
174+
private static void testProcessorAvailability() throws IOException {
175+
// Launch GetProcessorInfo.exe to gather processor counts
176+
Path nativeGetProcessorInfo = Paths.get(Utils.TEST_NATIVE_PATH)
177+
.resolve("GetProcessorInfo.exe")
178+
.toAbsolutePath();
179+
180+
var processBuilder = new ProcessBuilder(nativeGetProcessorInfo.toString());
181+
var outputAnalyzer= new OutputAnalyzer(processBuilder.start());
182+
outputAnalyzer.shouldHaveExitValue(0);
183+
outputAnalyzer.shouldContain(totalProcessorCountMessage);
184+
outputAnalyzer.shouldContain(processorCountPerGroupMessage);
185+
outputAnalyzer.shouldContain(isWindowsServerMessage);
186+
187+
int totalProcessorCount = 0;
188+
boolean isWindowsServer = false;
189+
var processorGroupSizes = new HashSet<Integer>();
190+
191+
List<String> lines = outputAnalyzer.stdoutAsLines();
192+
193+
for (var line: lines) {
194+
if (line.startsWith(totalProcessorCountMessage)) {
195+
String totalProcessorCountStr = line.substring(totalProcessorCountMessage.length());
196+
totalProcessorCount = Integer.parseInt(totalProcessorCountStr);
197+
} else if (line.startsWith(processorCountPerGroupMessage)) {
198+
String processorCountPerGroupStr = line.substring(processorCountPerGroupMessage.length());
199+
String[] processorCountsPerGroup = processorCountPerGroupStr.split(",");
200+
201+
for (var processorCountStr: processorCountsPerGroup) {
202+
int processorCount = Integer.parseInt(processorCountStr);
203+
processorGroupSizes.add(processorCount);
204+
}
205+
} else if (line.startsWith(isWindowsServerMessage)) {
206+
String isWindowsServerStr = line.substring(isWindowsServerMessage.length());
207+
isWindowsServer = Integer.parseInt(isWindowsServerStr) > 0;
208+
}
209+
}
210+
211+
// Launch java without the start command and with the product flag disabled
212+
verifyAvailableProcessorsWithDisabledProductFlag(processorGroupSizes);
213+
214+
// Launch java without the start command and with the product flag enabled
215+
boolean schedulesAllProcessorGroups = getSchedulesAllProcessorGroups(isWindowsServer);
216+
verifyAvailableProcessorsWithEnabledProductFlag(schedulesAllProcessorGroups, totalProcessorCount, processorGroupSizes);
217+
}
218+
}

0 commit comments

Comments
 (0)