Skip to content

Commit

Permalink
8338136: Hotspot should support multiple large page sizes on Windows
Browse files Browse the repository at this point in the history
Reviewed-by: dholmes, djelinski
  • Loading branch information
Dhamoder Nalla authored and David Holmes committed Oct 4, 2024
1 parent 10402b4 commit 4ded283
Show file tree
Hide file tree
Showing 4 changed files with 153 additions and 12 deletions.
4 changes: 4 additions & 0 deletions src/hotspot/os/windows/globals_windows.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,10 @@
product(bool, UseAllWindowsProcessorGroups, false, \
"Use all processor groups on supported Windows versions") \
\
product(bool, EnableAllLargePageSizesForWindows, false, \
"Enable support for multiple large page sizes on " \
"Windows Server") \
\
product(bool, UseOSErrorReporting, false, \
"Let VM fatal error propagate to the OS (ie. WER on Windows)")

Expand Down
51 changes: 39 additions & 12 deletions src/hotspot/os/windows/os_windows.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3126,7 +3126,7 @@ class NUMANodeListHolder {

static size_t _large_page_size = 0;

static bool request_lock_memory_privilege() {
bool os::win32::request_lock_memory_privilege() {
HANDLE hProcess = OpenProcess(PROCESS_QUERY_INFORMATION, FALSE,
os::current_process_id());

Expand Down Expand Up @@ -3310,14 +3310,14 @@ static char* allocate_pages_individually(size_t bytes, char* addr, DWORD flags,
return p_buf;
}

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

#define WARN(msg) if (warn_on_failure) { warning(msg); }
#define WARN(...) if (warn_on_failure) { warning(__VA_ARGS__); }

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

#if defined(IA32) || defined(AMD64)
if (size > 4*M || LargePageSizeInBytes > 4*M) {
#if defined(IA32)
if (size > 4 * M || LargePageSizeInBytes > 4 * M) {
WARN("JVM cannot use large pages bigger than 4mb.");
return 0;
}
#elif defined(AMD64)
if (!EnableAllLargePageSizesForWindows) {
if (size > 4 * M || LargePageSizeInBytes > 4 * M) {
WARN("JVM cannot use large pages bigger than 4mb.");
return 0;
}
}
#endif

if (LargePageSizeInBytes > 0 && LargePageSizeInBytes % size == 0) {
size = LargePageSizeInBytes;
if (LargePageSizeInBytes > 0) {
if (LargePageSizeInBytes % size == 0) {
size = LargePageSizeInBytes;
} else {
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);
}
}

#undef WARN
Expand All @@ -3349,12 +3360,23 @@ void os::large_page_init() {
return;
}

_large_page_size = large_page_init_decide_size();
_large_page_size = os::win32::large_page_init_decide_size();
const size_t default_page_size = os::vm_page_size();
if (_large_page_size > default_page_size) {
#if !defined(IA32)
if (EnableAllLargePageSizesForWindows) {
size_t min_size = GetLargePageMinimum();

// 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.
for (size_t page_size = min_size; page_size < _large_page_size; page_size *= 2) {
_page_sizes.add(page_size);
}
}
#endif

_page_sizes.add(_large_page_size);
}

// Set UseLargePages based on whether a large page size was successfully determined
UseLargePages = _large_page_size != 0;
}

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

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

// Ensure GetLargePageMinimum() returns a valid positive value
size_t large_page_min = GetLargePageMinimum();
if (large_page_min <= 0) {
return nullptr;
}

// The requested alignment can be larger than the page size, for example with G1
// the alignment is bound to the heap region size. So this reservation needs to
// ensure that the requested alignment is met. When there is a requested address
// this solves it self, since it must be properly aligned already.
if (addr == nullptr && alignment > page_size) {
if (addr == nullptr && alignment > large_page_min) {
return reserve_large_pages_aligned(bytes, alignment, exec);
}

Expand Down
2 changes: 2 additions & 0 deletions src/hotspot/os/windows/os_windows.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,8 @@ class os::win32 {
static void setmode_streams();
static bool is_windows_11_or_greater();
static bool is_windows_server_2022_or_greater();
static bool request_lock_memory_privilege();
static size_t large_page_init_decide_size();
static int windows_major_version() {
assert(_major_version > 0, "windows version not initialized.");
return _major_version;
Expand Down
108 changes: 108 additions & 0 deletions test/hotspot/gtest/runtime/test_os_windows.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -722,6 +722,114 @@ TEST_VM(os_windows, processor_count) {
}
}

TEST_VM(os_windows, large_page_init_multiple_sizes) {
// Call request_lock_memory_privilege() and check the result
if (!os::win32::request_lock_memory_privilege()) {
GTEST_SKIP() << "Skipping test because lock memory privilege is not granted.";
}
// Set globals to make sure we hit the correct code path
AutoSaveRestore<bool> guardUseLargePages(UseLargePages);
AutoSaveRestore<bool> guardEnableAllLargePageSizesForWindows(EnableAllLargePageSizesForWindows);
AutoSaveRestore<size_t> guardLargePageSizeInBytes(LargePageSizeInBytes);
FLAG_SET_CMDLINE(UseLargePages, true);
FLAG_SET_CMDLINE(EnableAllLargePageSizesForWindows, true);

// Determine the minimum page size
const size_t min_size = GetLargePageMinimum();

// End the test if GetLargePageMinimum returns 0
if (min_size == 0) {
GTEST_SKIP() << "Large pages are not supported on this system.";
return;
}

// Set LargePageSizeInBytes to 4 times the minimum page size
FLAG_SET_CMDLINE(LargePageSizeInBytes, 4 * min_size); // Set a value for multiple page sizes

// Initialize large page settings
os::large_page_init();

// Verify that large pages are enabled
EXPECT_TRUE(UseLargePages) << "UseLargePages should be true after initialization for LargePageSizeInBytes = 4 * min_size";

// Verify that decided_large_page_size is greater than the default page size
const size_t default_page_size = os::vm_page_size();
size_t decided_large_page_size = os::win32::large_page_init_decide_size();
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";

#if !defined(IA32)
size_t page_size_count = 0;
size_t page_size = os::page_sizes().largest();

do {
++page_size_count;
page_size = os::page_sizes().next_smaller(page_size);
} while (page_size >= os::page_sizes().smallest());

EXPECT_GT(page_size_count, 1u) << "There should be multiple large page sizes available.";

size_t large_page_size = decided_large_page_size;

for (size_t page_size = os::page_sizes().largest(); page_size >= min_size; page_size = os::page_sizes().next_smaller(page_size)) {
EXPECT_TRUE(page_size % min_size == 0) << "Each page size should be a multiple of the minimum large page size.";
EXPECT_LE(page_size, large_page_size) << "Page size should not exceed the determined large page size.";
}
#endif
}

TEST_VM(os_windows, large_page_init_decide_size) {
// Initial setup
// Call request_lock_memory_privilege() and check the result
if (!os::win32::request_lock_memory_privilege()) {
GTEST_SKIP() << "Skipping test because lock memory privilege is not granted.";
}
AutoSaveRestore<bool> guardUseLargePages(UseLargePages);
AutoSaveRestore<size_t> guardLargePageSizeInBytes(LargePageSizeInBytes);
FLAG_SET_CMDLINE(UseLargePages, true);
FLAG_SET_CMDLINE(LargePageSizeInBytes, 0); // Reset to default

// Test for large page support
size_t decided_size = os::win32::large_page_init_decide_size();
size_t min_size = GetLargePageMinimum();
if (min_size == 0) {
EXPECT_EQ(decided_size, 0) << "Expected decided size to be 0 when large page is not supported by the processor";
return;
}

// Scenario 1: Test with 2MB large page size
if (min_size == 2 * M) {
FLAG_SET_CMDLINE(LargePageSizeInBytes, 2 * M); // Set large page size to 2MB
decided_size = os::win32::large_page_init_decide_size(); // Recalculate decided size
EXPECT_EQ(decided_size, 2 * M) << "Expected decided size to be 2M when large page and OS reported size are both 2M";
}

// Scenario 2: Test with 1MB large page size
if (min_size == 2 * M) {
FLAG_SET_CMDLINE(LargePageSizeInBytes, 1 * M); // Set large page size to 1MB
decided_size = os::win32::large_page_init_decide_size(); // Recalculate decided size
EXPECT_EQ(decided_size, 2 * M) << "Expected decided size to be 2M when large page is 1M and OS reported size is 2M";
}

#if defined(IA32) || defined(AMD64)
FLAG_SET_CMDLINE(LargePageSizeInBytes, 5 * M); // Set large page size to 5MB
if (!EnableAllLargePageSizesForWindows) {
decided_size = os::win32::large_page_init_decide_size(); // Recalculate decided size
EXPECT_EQ(decided_size, 0) << "Expected decided size to be 0 for large pages bigger than 4mb on IA32 or AMD64";
}
#endif

// Additional check for non-multiple of minimum size
// Set an arbitrary large page size which is not a multiple of min_size
FLAG_SET_CMDLINE(LargePageSizeInBytes, 5 * min_size + 1);

// Recalculate decided size
decided_size = os::win32::large_page_init_decide_size();

// Assert that the decided size defaults to minimum page size when LargePageSizeInBytes
// is not a multiple of the minimum size, assuming conditions are always met
EXPECT_EQ(decided_size, 0) << "Expected decided size to default to 0 when LargePageSizeInBytes is not a multiple of minimum size";
}

class ReserveMemorySpecialRunnable : public TestRunnable {
public:
void runUnitTest() const {
Expand Down

1 comment on commit 4ded283

@openjdk-notifier
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please sign in to comment.