Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

8338136: Hotspot should support multiple large page sizes on Windows #219

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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 @@ -3104,7 +3104,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 @@ -3288,14 +3288,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 @@ -3306,15 +3306,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 @@ -3327,12 +3338,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 @@ -3596,7 +3618,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 @@ -3605,11 +3626,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