8338136: Hotspot should support multiple large page sizes on Windows
Reviewed-by: dholmes, djelinski
This commit is contained in:
parent
10402b43c7
commit
4ded28380b
@ -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)")
|
||||
|
||||
|
@ -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());
|
||||
|
||||
@ -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;
|
||||
}
|
||||
@ -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
|
||||
@ -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;
|
||||
}
|
||||
|
||||
@ -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");
|
||||
|
||||
@ -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);
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
|
@ -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 {
|
||||
|
Loading…
Reference in New Issue
Block a user