Skip to content

Commit 4ded283

Browse files
Dhamoder NallaDavid Holmes
authored andcommitted
8338136: Hotspot should support multiple large page sizes on Windows
Reviewed-by: dholmes, djelinski
1 parent 10402b4 commit 4ded283

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
@@ -38,6 +38,10 @@
3838
product(bool, UseAllWindowsProcessorGroups, false, \
3939
"Use all processor groups on supported Windows versions") \
4040
\
41+
product(bool, EnableAllLargePageSizesForWindows, false, \
42+
"Enable support for multiple large page sizes on " \
43+
"Windows Server") \
44+
\
4145
product(bool, UseOSErrorReporting, false, \
4246
"Let VM fatal error propagate to the OS (ie. WER on Windows)")
4347

src/hotspot/os/windows/os_windows.cpp

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

31273127
static size_t _large_page_size = 0;
31283128

3129-
static bool request_lock_memory_privilege() {
3129+
bool os::win32::request_lock_memory_privilege() {
31303130
HANDLE hProcess = OpenProcess(PROCESS_QUERY_INFORMATION, FALSE,
31313131
os::current_process_id());
31323132

@@ -3310,14 +3310,14 @@ static char* allocate_pages_individually(size_t bytes, char* addr, DWORD flags,
33103310
return p_buf;
33113311
}
33123312

3313-
static size_t large_page_init_decide_size() {
3313+
size_t os::win32::large_page_init_decide_size() {
33143314
// print a warning if any large page related flag is specified on command line
33153315
bool warn_on_failure = !FLAG_IS_DEFAULT(UseLargePages) ||
33163316
!FLAG_IS_DEFAULT(LargePageSizeInBytes);
33173317

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

3320-
if (!request_lock_memory_privilege()) {
3320+
if (!os::win32::request_lock_memory_privilege()) {
33213321
WARN("JVM cannot use large page memory because it does not have enough privilege to lock pages in memory.");
33223322
return 0;
33233323
}
@@ -3328,15 +3328,26 @@ static size_t large_page_init_decide_size() {
33283328
return 0;
33293329
}
33303330

3331-
#if defined(IA32) || defined(AMD64)
3332-
if (size > 4*M || LargePageSizeInBytes > 4*M) {
3331+
#if defined(IA32)
3332+
if (size > 4 * M || LargePageSizeInBytes > 4 * M) {
33333333
WARN("JVM cannot use large pages bigger than 4mb.");
33343334
return 0;
33353335
}
3336+
#elif defined(AMD64)
3337+
if (!EnableAllLargePageSizesForWindows) {
3338+
if (size > 4 * M || LargePageSizeInBytes > 4 * M) {
3339+
WARN("JVM cannot use large pages bigger than 4mb.");
3340+
return 0;
3341+
}
3342+
}
33363343
#endif
33373344

3338-
if (LargePageSizeInBytes > 0 && LargePageSizeInBytes % size == 0) {
3339-
size = LargePageSizeInBytes;
3345+
if (LargePageSizeInBytes > 0) {
3346+
if (LargePageSizeInBytes % size == 0) {
3347+
size = LargePageSizeInBytes;
3348+
} else {
3349+
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);
3350+
}
33403351
}
33413352

33423353
#undef WARN
@@ -3349,12 +3360,23 @@ void os::large_page_init() {
33493360
return;
33503361
}
33513362

3352-
_large_page_size = large_page_init_decide_size();
3363+
_large_page_size = os::win32::large_page_init_decide_size();
33533364
const size_t default_page_size = os::vm_page_size();
33543365
if (_large_page_size > default_page_size) {
3366+
#if !defined(IA32)
3367+
if (EnableAllLargePageSizesForWindows) {
3368+
size_t min_size = GetLargePageMinimum();
3369+
3370+
// 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.
3371+
for (size_t page_size = min_size; page_size < _large_page_size; page_size *= 2) {
3372+
_page_sizes.add(page_size);
3373+
}
3374+
}
3375+
#endif
3376+
33553377
_page_sizes.add(_large_page_size);
33563378
}
3357-
3379+
// Set UseLargePages based on whether a large page size was successfully determined
33583380
UseLargePages = _large_page_size != 0;
33593381
}
33603382

@@ -3618,7 +3640,6 @@ static char* reserve_large_pages_aligned(size_t size, size_t alignment, bool exe
36183640
char* os::pd_reserve_memory_special(size_t bytes, size_t alignment, size_t page_size, char* addr,
36193641
bool exec) {
36203642
assert(UseLargePages, "only for large pages");
3621-
assert(page_size == os::large_page_size(), "Currently only support one large page size on Windows");
36223643
assert(is_aligned(addr, alignment), "Must be");
36233644
assert(is_aligned(addr, page_size), "Must be");
36243645

@@ -3627,11 +3648,17 @@ char* os::pd_reserve_memory_special(size_t bytes, size_t alignment, size_t page_
36273648
return nullptr;
36283649
}
36293650

3651+
// Ensure GetLargePageMinimum() returns a valid positive value
3652+
size_t large_page_min = GetLargePageMinimum();
3653+
if (large_page_min <= 0) {
3654+
return nullptr;
3655+
}
3656+
36303657
// The requested alignment can be larger than the page size, for example with G1
36313658
// the alignment is bound to the heap region size. So this reservation needs to
36323659
// ensure that the requested alignment is met. When there is a requested address
36333660
// this solves it self, since it must be properly aligned already.
3634-
if (addr == nullptr && alignment > page_size) {
3661+
if (addr == nullptr && alignment > large_page_min) {
36353662
return reserve_large_pages_aligned(bytes, alignment, exec);
36363663
}
36373664

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)