Skip to content

Commit 7ef1881

Browse files
Dhamoder NallaPaul Hohensee
authored andcommitted
8338136: Hotspot should support multiple large page sizes on Windows
Backport-of: 4ded28380b6756e0679d80706f76bd6e78c370b9
1 parent 6236ffb commit 7ef1881

File tree

4 files changed

+153
-12
lines changed

4 files changed

+153
-12
lines changed

src/hotspot/os/windows/globals_windows.hpp

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,10 @@
3939
product(bool, UseAllWindowsProcessorGroups, false, \
4040
"Use all processor groups on supported Windows versions") \
4141
\
42+
product(bool, EnableAllLargePageSizesForWindows, false, \
43+
"Enable support for multiple large page sizes on " \
44+
"Windows Server") \
45+
\
4246
product(bool, UseOSErrorReporting, false, \
4347
"Let VM fatal error propagate to the OS (ie. WER on Windows)")
4448

src/hotspot/os/windows/os_windows.cpp

Lines changed: 39 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -3085,7 +3085,7 @@ class NUMANodeListHolder {
30853085

30863086
static size_t _large_page_size = 0;
30873087

3088-
static bool request_lock_memory_privilege() {
3088+
bool os::win32::request_lock_memory_privilege() {
30893089
HANDLE hProcess = OpenProcess(PROCESS_QUERY_INFORMATION, FALSE,
30903090
os::current_process_id());
30913091

@@ -3269,14 +3269,14 @@ static char* allocate_pages_individually(size_t bytes, char* addr, DWORD flags,
32693269
return p_buf;
32703270
}
32713271

3272-
static size_t large_page_init_decide_size() {
3272+
size_t os::win32::large_page_init_decide_size() {
32733273
// print a warning if any large page related flag is specified on command line
32743274
bool warn_on_failure = !FLAG_IS_DEFAULT(UseLargePages) ||
32753275
!FLAG_IS_DEFAULT(LargePageSizeInBytes);
32763276

3277-
#define WARN(msg) if (warn_on_failure) { warning(msg); }
3277+
#define WARN(...) if (warn_on_failure) { warning(__VA_ARGS__); }
32783278

3279-
if (!request_lock_memory_privilege()) {
3279+
if (!os::win32::request_lock_memory_privilege()) {
32803280
WARN("JVM cannot use large page memory because it does not have enough privilege to lock pages in memory.");
32813281
return 0;
32823282
}
@@ -3287,15 +3287,26 @@ static size_t large_page_init_decide_size() {
32873287
return 0;
32883288
}
32893289

3290-
#if defined(IA32) || defined(AMD64)
3291-
if (size > 4*M || LargePageSizeInBytes > 4*M) {
3290+
#if defined(IA32)
3291+
if (size > 4 * M || LargePageSizeInBytes > 4 * M) {
32923292
WARN("JVM cannot use large pages bigger than 4mb.");
32933293
return 0;
32943294
}
3295+
#elif defined(AMD64)
3296+
if (!EnableAllLargePageSizesForWindows) {
3297+
if (size > 4 * M || LargePageSizeInBytes > 4 * M) {
3298+
WARN("JVM cannot use large pages bigger than 4mb.");
3299+
return 0;
3300+
}
3301+
}
32953302
#endif
32963303

3297-
if (LargePageSizeInBytes > 0 && LargePageSizeInBytes % size == 0) {
3298-
size = LargePageSizeInBytes;
3304+
if (LargePageSizeInBytes > 0) {
3305+
if (LargePageSizeInBytes % size == 0) {
3306+
size = LargePageSizeInBytes;
3307+
} else {
3308+
WARN("The specified large page size (%d) is not a multiple of the minimum large page size (%d), defaulting to minimum page size.", LargePageSizeInBytes, size);
3309+
}
32993310
}
33003311

33013312
#undef WARN
@@ -3308,12 +3319,23 @@ void os::large_page_init() {
33083319
return;
33093320
}
33103321

3311-
_large_page_size = large_page_init_decide_size();
3322+
_large_page_size = os::win32::large_page_init_decide_size();
33123323
const size_t default_page_size = os::vm_page_size();
33133324
if (_large_page_size > default_page_size) {
3325+
#if !defined(IA32)
3326+
if (EnableAllLargePageSizesForWindows) {
3327+
size_t min_size = GetLargePageMinimum();
3328+
3329+
// Populate _page_sizes with large page sizes less than or equal to _large_page_size, ensuring each page size is double the size of the previous one.
3330+
for (size_t page_size = min_size; page_size < _large_page_size; page_size *= 2) {
3331+
_page_sizes.add(page_size);
3332+
}
3333+
}
3334+
#endif
3335+
33143336
_page_sizes.add(_large_page_size);
33153337
}
3316-
3338+
// Set UseLargePages based on whether a large page size was successfully determined
33173339
UseLargePages = _large_page_size != 0;
33183340
}
33193341

@@ -3575,7 +3597,6 @@ static char* reserve_large_pages_aligned(size_t size, size_t alignment, bool exe
35753597
char* os::pd_reserve_memory_special(size_t bytes, size_t alignment, size_t page_size, char* addr,
35763598
bool exec) {
35773599
assert(UseLargePages, "only for large pages");
3578-
assert(page_size == os::large_page_size(), "Currently only support one large page size on Windows");
35793600
assert(is_aligned(addr, alignment), "Must be");
35803601
assert(is_aligned(addr, page_size), "Must be");
35813602

@@ -3584,11 +3605,17 @@ char* os::pd_reserve_memory_special(size_t bytes, size_t alignment, size_t page_
35843605
return nullptr;
35853606
}
35863607

3608+
// Ensure GetLargePageMinimum() returns a valid positive value
3609+
size_t large_page_min = GetLargePageMinimum();
3610+
if (large_page_min <= 0) {
3611+
return nullptr;
3612+
}
3613+
35873614
// The requested alignment can be larger than the page size, for example with G1
35883615
// the alignment is bound to the heap region size. So this reservation needs to
35893616
// ensure that the requested alignment is met. When there is a requested address
35903617
// this solves it self, since it must be properly aligned already.
3591-
if (addr == nullptr && alignment > page_size) {
3618+
if (addr == nullptr && alignment > large_page_min) {
35923619
return reserve_large_pages_aligned(bytes, alignment, exec);
35933620
}
35943621

src/hotspot/os/windows/os_windows.hpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,8 @@ class os::win32 {
6565
static void setmode_streams();
6666
static bool is_windows_11_or_greater();
6767
static bool is_windows_server_2022_or_greater();
68+
static bool request_lock_memory_privilege();
69+
static size_t large_page_init_decide_size();
6870
static int windows_major_version() {
6971
assert(_major_version > 0, "windows version not initialized.");
7072
return _major_version;

test/hotspot/gtest/runtime/test_os_windows.cpp

Lines changed: 108 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -722,6 +722,114 @@ TEST_VM(os_windows, processor_count) {
722722
}
723723
}
724724

725+
TEST_VM(os_windows, large_page_init_multiple_sizes) {
726+
// Call request_lock_memory_privilege() and check the result
727+
if (!os::win32::request_lock_memory_privilege()) {
728+
GTEST_SKIP() << "Skipping test because lock memory privilege is not granted.";
729+
}
730+
// Set globals to make sure we hit the correct code path
731+
AutoSaveRestore<bool> guardUseLargePages(UseLargePages);
732+
AutoSaveRestore<bool> guardEnableAllLargePageSizesForWindows(EnableAllLargePageSizesForWindows);
733+
AutoSaveRestore<size_t> guardLargePageSizeInBytes(LargePageSizeInBytes);
734+
FLAG_SET_CMDLINE(UseLargePages, true);
735+
FLAG_SET_CMDLINE(EnableAllLargePageSizesForWindows, true);
736+
737+
// Determine the minimum page size
738+
const size_t min_size = GetLargePageMinimum();
739+
740+
// End the test if GetLargePageMinimum returns 0
741+
if (min_size == 0) {
742+
GTEST_SKIP() << "Large pages are not supported on this system.";
743+
return;
744+
}
745+
746+
// Set LargePageSizeInBytes to 4 times the minimum page size
747+
FLAG_SET_CMDLINE(LargePageSizeInBytes, 4 * min_size); // Set a value for multiple page sizes
748+
749+
// Initialize large page settings
750+
os::large_page_init();
751+
752+
// Verify that large pages are enabled
753+
EXPECT_TRUE(UseLargePages) << "UseLargePages should be true after initialization for LargePageSizeInBytes = 4 * min_size";
754+
755+
// Verify that decided_large_page_size is greater than the default page size
756+
const size_t default_page_size = os::vm_page_size();
757+
size_t decided_large_page_size = os::win32::large_page_init_decide_size();
758+
EXPECT_GT(decided_large_page_size, default_page_size) << "Large page size should be greater than the default page size for LargePageSizeInBytes = 4 * min_size";
759+
760+
#if !defined(IA32)
761+
size_t page_size_count = 0;
762+
size_t page_size = os::page_sizes().largest();
763+
764+
do {
765+
++page_size_count;
766+
page_size = os::page_sizes().next_smaller(page_size);
767+
} while (page_size >= os::page_sizes().smallest());
768+
769+
EXPECT_GT(page_size_count, 1u) << "There should be multiple large page sizes available.";
770+
771+
size_t large_page_size = decided_large_page_size;
772+
773+
for (size_t page_size = os::page_sizes().largest(); page_size >= min_size; page_size = os::page_sizes().next_smaller(page_size)) {
774+
EXPECT_TRUE(page_size % min_size == 0) << "Each page size should be a multiple of the minimum large page size.";
775+
EXPECT_LE(page_size, large_page_size) << "Page size should not exceed the determined large page size.";
776+
}
777+
#endif
778+
}
779+
780+
TEST_VM(os_windows, large_page_init_decide_size) {
781+
// Initial setup
782+
// Call request_lock_memory_privilege() and check the result
783+
if (!os::win32::request_lock_memory_privilege()) {
784+
GTEST_SKIP() << "Skipping test because lock memory privilege is not granted.";
785+
}
786+
AutoSaveRestore<bool> guardUseLargePages(UseLargePages);
787+
AutoSaveRestore<size_t> guardLargePageSizeInBytes(LargePageSizeInBytes);
788+
FLAG_SET_CMDLINE(UseLargePages, true);
789+
FLAG_SET_CMDLINE(LargePageSizeInBytes, 0); // Reset to default
790+
791+
// Test for large page support
792+
size_t decided_size = os::win32::large_page_init_decide_size();
793+
size_t min_size = GetLargePageMinimum();
794+
if (min_size == 0) {
795+
EXPECT_EQ(decided_size, 0) << "Expected decided size to be 0 when large page is not supported by the processor";
796+
return;
797+
}
798+
799+
// Scenario 1: Test with 2MB large page size
800+
if (min_size == 2 * M) {
801+
FLAG_SET_CMDLINE(LargePageSizeInBytes, 2 * M); // Set large page size to 2MB
802+
decided_size = os::win32::large_page_init_decide_size(); // Recalculate decided size
803+
EXPECT_EQ(decided_size, 2 * M) << "Expected decided size to be 2M when large page and OS reported size are both 2M";
804+
}
805+
806+
// Scenario 2: Test with 1MB large page size
807+
if (min_size == 2 * M) {
808+
FLAG_SET_CMDLINE(LargePageSizeInBytes, 1 * M); // Set large page size to 1MB
809+
decided_size = os::win32::large_page_init_decide_size(); // Recalculate decided size
810+
EXPECT_EQ(decided_size, 2 * M) << "Expected decided size to be 2M when large page is 1M and OS reported size is 2M";
811+
}
812+
813+
#if defined(IA32) || defined(AMD64)
814+
FLAG_SET_CMDLINE(LargePageSizeInBytes, 5 * M); // Set large page size to 5MB
815+
if (!EnableAllLargePageSizesForWindows) {
816+
decided_size = os::win32::large_page_init_decide_size(); // Recalculate decided size
817+
EXPECT_EQ(decided_size, 0) << "Expected decided size to be 0 for large pages bigger than 4mb on IA32 or AMD64";
818+
}
819+
#endif
820+
821+
// Additional check for non-multiple of minimum size
822+
// Set an arbitrary large page size which is not a multiple of min_size
823+
FLAG_SET_CMDLINE(LargePageSizeInBytes, 5 * min_size + 1);
824+
825+
// Recalculate decided size
826+
decided_size = os::win32::large_page_init_decide_size();
827+
828+
// Assert that the decided size defaults to minimum page size when LargePageSizeInBytes
829+
// is not a multiple of the minimum size, assuming conditions are always met
830+
EXPECT_EQ(decided_size, 0) << "Expected decided size to default to 0 when LargePageSizeInBytes is not a multiple of minimum size";
831+
}
832+
725833
class ReserveMemorySpecialRunnable : public TestRunnable {
726834
public:
727835
void runUnitTest() const {

0 commit comments

Comments
 (0)