Skip to content
This repository was archived by the owner on Jan 23, 2023. It is now read-only.

Add Large pages support in GC #23251

Merged
merged 1 commit into from
Apr 8, 2019
Merged
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
3 changes: 2 additions & 1 deletion src/gc/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,8 @@ if(WIN32)
set (GC_LINK_LIBRARIES
${STATIC_MT_CRT_LIB}
${STATIC_MT_VCRT_LIB}
kernel32.lib)
kernel32.lib
advapi32.lib)
else()
set (GC_LINK_LIBRARIES)
endif(WIN32)
Expand Down
7 changes: 7 additions & 0 deletions src/gc/env/gcenv.os.h
Original file line number Diff line number Diff line change
Expand Up @@ -275,6 +275,13 @@ class GCToOSInterface
// true if it has succeeded, false if it has failed
static bool VirtualCommit(void *address, size_t size, uint16_t node = NUMA_NODE_UNDEFINED);

// Reserve and Commit virtual memory range for Large Pages
// Parameters:
// size - size of the virtual memory range
// Return:
// Address of the allocated memory
static void* VirtualReserveAndCommitLargePages(size_t size);

// Decomit virtual memory range.
// Parameters:
// address - starting virtual address
Expand Down
36 changes: 25 additions & 11 deletions src/gc/gc.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2403,6 +2403,7 @@ void qsort1(uint8_t** low, uint8_t** high, unsigned int depth);
#endif //USE_INTROSORT

void* virtual_alloc (size_t size);
void* virtual_alloc (size_t size, bool use_large_pages_p);
void virtual_free (void* add, size_t size);

/* per heap static initialization */
Expand Down Expand Up @@ -2818,6 +2819,7 @@ GCSpinLock gc_heap::gc_lock;

size_t gc_heap::eph_gen_starts_size = 0;
heap_segment* gc_heap::segment_standby_list;
size_t gc_heap::use_large_pages_p = 0;
size_t gc_heap::last_gc_index = 0;
#ifdef SEG_MAPPING_TABLE
size_t gc_heap::min_segment_size = 0;
Expand Down Expand Up @@ -4263,7 +4265,7 @@ typedef struct

initial_memory_details memory_details;

BOOL reserve_initial_memory (size_t normal_size, size_t large_size, size_t num_heaps)
BOOL reserve_initial_memory (size_t normal_size, size_t large_size, size_t num_heaps, bool use_large_pages_p)
{
BOOL reserve_success = FALSE;

Expand Down Expand Up @@ -4304,7 +4306,7 @@ BOOL reserve_initial_memory (size_t normal_size, size_t large_size, size_t num_h

size_t requestedMemory = memory_details.block_count * (normal_size + large_size);

uint8_t* allatonce_block = (uint8_t*)virtual_alloc (requestedMemory);
uint8_t* allatonce_block = (uint8_t*)virtual_alloc (requestedMemory, use_large_pages_p);
if (allatonce_block)
{
g_gc_lowest_address = allatonce_block;
Expand All @@ -4324,10 +4326,10 @@ BOOL reserve_initial_memory (size_t normal_size, size_t large_size, size_t num_h
// try to allocate 2 blocks
uint8_t* b1 = 0;
uint8_t* b2 = 0;
b1 = (uint8_t*)virtual_alloc (memory_details.block_count * normal_size);
b1 = (uint8_t*)virtual_alloc (memory_details.block_count * normal_size, use_large_pages_p);
if (b1)
{
b2 = (uint8_t*)virtual_alloc (memory_details.block_count * large_size);
b2 = (uint8_t*)virtual_alloc (memory_details.block_count * large_size, use_large_pages_p);
if (b2)
{
memory_details.allocation_pattern = initial_memory_details::TWO_STAGE;
Expand Down Expand Up @@ -4360,7 +4362,7 @@ BOOL reserve_initial_memory (size_t normal_size, size_t large_size, size_t num_h
memory_details.block_size_normal :
memory_details.block_size_large);
current_block->memory_base =
(uint8_t*)virtual_alloc (block_size);
(uint8_t*)virtual_alloc (block_size, use_large_pages_p);
if (current_block->memory_base == 0)
{
// Free the blocks that we've allocated so far
Expand Down Expand Up @@ -4468,6 +4470,11 @@ heap_segment* get_initial_segment (size_t size, int h_number)
}

void* virtual_alloc (size_t size)
{
return virtual_alloc(size, false);
}

void* virtual_alloc (size_t size, bool use_large_pages_p)
{
size_t requested_size = size;

Expand All @@ -4488,7 +4495,8 @@ void* virtual_alloc (size_t size)
flags = VirtualReserveFlags::WriteWatch;
}
#endif // !FEATURE_USE_SOFTWARE_WRITE_WATCH_FOR_GC_HEAP
void* prgmem = GCToOSInterface::VirtualReserve (requested_size, card_size * card_word_width, flags);

void* prgmem = use_large_pages_p ? GCToOSInterface::VirtualReserveAndCommitLargePages(requested_size) : GCToOSInterface::VirtualReserve(requested_size, card_size * card_word_width, flags);
void *aligned_mem = prgmem;

// We don't want (prgmem + size) to be right at the end of the address space
Expand Down Expand Up @@ -5466,9 +5474,10 @@ bool gc_heap::virtual_commit (void* address, size_t size, int h_number, bool* ha
}

// If it's a valid heap number it means it's commiting for memory on the GC heap.
bool commit_succeeded_p = ((h_number >= 0) ?
virtual_alloc_commit_for_heap (address, size, h_number) :
GCToOSInterface::VirtualCommit(address, size));
// In addition if large pages is enabled, we set commit_succeeded_p to true because memory is already committed.
bool commit_succeeded_p = ((h_number >= 0) ? (use_large_pages_p ? true :
virtual_alloc_commit_for_heap (address, size, h_number)) :
GCToOSInterface::VirtualCommit(address, size));

if (!commit_succeeded_p && heap_hard_limit)
{
Expand Down Expand Up @@ -9219,7 +9228,7 @@ heap_segment* gc_heap::make_heap_segment (uint8_t* new_pages, size_t size, int h
heap_segment_mem (new_segment) = start;
heap_segment_used (new_segment) = start;
heap_segment_reserved (new_segment) = new_pages + size;
heap_segment_committed (new_segment) = new_pages + initial_commit;
heap_segment_committed (new_segment) = (use_large_pages_p ? heap_segment_reserved(new_segment) : (new_pages + initial_commit));
init_heap_segment (new_segment);
dprintf (2, ("Creating heap segment %Ix", (size_t)new_segment));
return new_segment;
Expand Down Expand Up @@ -9310,6 +9319,8 @@ void gc_heap::reset_heap_segment_pages (heap_segment* seg)
void gc_heap::decommit_heap_segment_pages (heap_segment* seg,
size_t extra_space)
{
if (use_large_pages_p)
return;
uint8_t* page_start = align_on_page (heap_segment_allocated(seg));
size_t size = heap_segment_committed (seg) - page_start;
extra_space = align_on_page (extra_space);
Expand Down Expand Up @@ -10019,12 +10030,15 @@ HRESULT gc_heap::initialize_gc (size_t segment_size,
block_count = 1;
#endif //MULTIPLE_HEAPS

use_large_pages_p = false;

if (heap_hard_limit)
{
check_commit_cs.Initialize();
use_large_pages_p = GCConfig::GetGCLargePages();
}

if (!reserve_initial_memory(segment_size,heap_size,block_count))
if (!reserve_initial_memory(segment_size,heap_size,block_count,use_large_pages_p))
return E_OUTOFMEMORY;

#ifdef CARD_BUNDLE
Expand Down
1 change: 1 addition & 0 deletions src/gc/gcconfig.h
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,7 @@ class GCConfigStringHolder
"Specifies the name of the GC config log file") \
BOOL_CONFIG(GCNumaAware, "GCNumaAware", true, "Enables numa allocations in the GC") \
BOOL_CONFIG(GCCpuGroup, "GCCpuGroup", false, "Enables CPU groups in the GC") \
BOOL_CONFIG(GCLargePages, "GCLargePages", false, "Enables using Large Pages in the GC") \
INT_CONFIG(HeapVerifyLevel, "HeapVerify", HEAPVERIFY_NONE, \
"When set verifies the integrity of the managed heap on entry and exit of each GC") \
INT_CONFIG(LOHCompactionMode, "GCLOHCompact", 0, "Specifies the LOH compaction mode") \
Expand Down
4 changes: 4 additions & 0 deletions src/gc/gcpriv.h
Original file line number Diff line number Diff line change
Expand Up @@ -3145,6 +3145,10 @@ class gc_heap
PER_HEAP_ISOLATED
size_t current_total_committed_gc_own;

// This is if large pages should be used.
PER_HEAP_ISOLATED
size_t use_large_pages_p;

PER_HEAP_ISOLATED
size_t last_gc_index;

Expand Down
12 changes: 12 additions & 0 deletions src/gc/sample/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,14 @@ set(SOURCES
../softwarewritewatch.cpp
)

if(WIN32)
set (GC_LINK_LIBRARIES
${STATIC_MT_CRT_LIB}
${STATIC_MT_VCRT_LIB}
kernel32.lib
advapi32.lib)
endif(WIN32)

if(WIN32)
list(APPEND SOURCES
../windows/gcenv.windows.cpp)
Expand All @@ -36,3 +44,7 @@ endif()
_add_executable(gcsample
${SOURCES}
)

if(WIN32)
target_link_libraries(gcsample ${GC_LINK_LIBRARIES})
endif()
1 change: 1 addition & 0 deletions src/gc/unix/config.h.in
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
#cmakedefine01 HAVE_SYS_MMAN_H
#cmakedefine01 HAVE_PTHREAD_THREADID_NP
#cmakedefine01 HAVE_PTHREAD_GETTHREADID_NP
#cmakedefine01 HAVE_MAP_HUGETLB
#cmakedefine01 HAVE_SCHED_GETCPU
#cmakedefine01 HAVE_NUMA_H
#cmakedefine01 HAVE_VM_ALLOCATE
Expand Down
9 changes: 9 additions & 0 deletions src/gc/unix/configure.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,15 @@ check_cxx_source_compiles("
}
" HAVE_PTHREAD_GETTHREADID_NP)

check_cxx_source_compiles("
#include <sys/mman.h>

int main()
{
return MAP_HUGETLB;
}
" HAVE_MAP_HUGETLB)

check_cxx_source_runs("
#include <sched.h>

Expand Down
38 changes: 36 additions & 2 deletions src/gc/unix/gcenv.unix.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -379,7 +379,7 @@ void GCToOSInterface::YieldThread(uint32_t switchCount)
// flags - flags to control special settings like write watching
// Return:
// Starting virtual address of the reserved range
void* GCToOSInterface::VirtualReserve(size_t size, size_t alignment, uint32_t flags)
static void* VirtualReserveInner(size_t size, size_t alignment, uint32_t flags, uint32_t hugePagesFlag = 0)
{
assert(!(flags & VirtualReserveFlags::WriteWatch) && "WriteWatch not supported on Unix");
if (alignment == 0)
Expand All @@ -388,7 +388,7 @@ void* GCToOSInterface::VirtualReserve(size_t size, size_t alignment, uint32_t fl
}

size_t alignedSize = size + (alignment - OS_PAGE_SIZE);
void * pRetVal = mmap(nullptr, alignedSize, PROT_NONE, MAP_ANON | MAP_PRIVATE, -1, 0);
void * pRetVal = mmap(nullptr, alignedSize, PROT_NONE, MAP_ANON | MAP_PRIVATE | hugePagesFlag, -1, 0);

if (pRetVal != NULL)
{
Expand All @@ -413,6 +413,18 @@ void* GCToOSInterface::VirtualReserve(size_t size, size_t alignment, uint32_t fl
return pRetVal;
}

// Reserve virtual memory range.
// Parameters:
// size - size of the virtual memory range
// alignment - requested memory alignment, 0 means no specific alignment requested
// flags - flags to control special settings like write watching
// Return:
// Starting virtual address of the reserved range
void* GCToOSInterface::VirtualReserve(size_t size, size_t alignment, uint32_t flags)
{
return VirtualReserveInner(size, alignment, flags);
}

// Release virtual memory range previously reserved using VirtualReserve
// Parameters:
// address - starting virtual address
Expand All @@ -426,6 +438,28 @@ bool GCToOSInterface::VirtualRelease(void* address, size_t size)
return (ret == 0);
}

// Commit virtual memory range.
// Parameters:
// size - size of the virtual memory range
// Return:
// Starting virtual address of the committed range
void* GCToOSInterface::VirtualReserveAndCommitLargePages(size_t size)
{
#if HAVE_MAP_HUGETLB
uint32_t largePagesFlag = MAP_HUGETLB;
#else
uint32_t largePagesFlag = 0;
#endif

void* pRetVal = VirtualReserveInner(size, OS_PAGE_SIZE, 0, largePagesFlag);
if (VirtualCommit(pRetVal, size, NUMA_NODE_UNDEFINED))
{
return pRetVal;
}

return nullptr;
}

// Commit virtual memory range. It must be part of a range reserved using VirtualReserve.
// Parameters:
// address - starting virtual address
Expand Down
63 changes: 63 additions & 0 deletions src/gc/windows/gcenv.windows.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,8 @@ static size_t g_RestrictedPhysicalMemoryLimit = (size_t)UINTPTR_MAX;
// memory on the machine/in the container, we need to restrict by the VM.
static bool g_UseRestrictedVirtualMemory = false;

static bool g_SeLockMemoryPrivilegeAcquired = false;

static AffinitySet g_processAffinitySet;

typedef BOOL (WINAPI *PIS_PROCESS_IN_JOB)(HANDLE processHandle, HANDLE jobHandle, BOOL* result);
Expand Down Expand Up @@ -114,6 +116,42 @@ DWORD LCM(DWORD u, DWORD v)
}
#endif

bool InitLargePagesPrivilege()
{
TOKEN_PRIVILEGES tp;
LUID luid;
if (!LookupPrivilegeValueW(nullptr, SE_LOCK_MEMORY_NAME, &luid))
{
return false;
}

tp.PrivilegeCount = 1;
tp.Privileges[0].Luid = luid;
tp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;

HANDLE token;
if (!OpenProcessToken(::GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES, &token))
{
return false;
}

BOOL retVal = AdjustTokenPrivileges(token, FALSE, &tp, 0, nullptr, 0);
DWORD gls = GetLastError();
CloseHandle(token);

if (!retVal)
{
return false;
}

if (gls != 0)
{
return false;
}

return true;
}

bool InitCPUGroupInfoArray()
{
#if (defined(_TARGET_AMD64_) || defined(_TARGET_ARM64_))
Expand Down Expand Up @@ -699,6 +737,31 @@ bool GCToOSInterface::VirtualRelease(void* address, size_t size)
return !!::VirtualFree(address, 0, MEM_RELEASE);
}

// Commit virtual memory range.
// Parameters:
// size - size of the virtual memory range
// Return:
// Starting virtual address of the committed range
void* GCToOSInterface::VirtualReserveAndCommitLargePages(size_t size)
{
void* pRetVal = nullptr;

if (!g_SeLockMemoryPrivilegeAcquired)
{
if (!InitLargePagesPrivilege())
{
return nullptr;
}

g_SeLockMemoryPrivilegeAcquired = true;
}

SIZE_T largePageMinimum = GetLargePageMinimum();
size = (size + (largePageMinimum - 1)) & ~(largePageMinimum - 1);

return ::VirtualAlloc(nullptr, size, MEM_RESERVE | MEM_COMMIT | MEM_LARGE_PAGES, PAGE_READWRITE);
}

// Commit virtual memory range. It must be part of a range reserved using VirtualReserve.
// Parameters:
// address - starting virtual address
Expand Down
1 change: 1 addition & 0 deletions src/inc/clrconfigvalues.h
Original file line number Diff line number Diff line change
Expand Up @@ -330,6 +330,7 @@ RETAIL_CONFIG_STRING_INFO(EXTERNAL_GCName, W("GCName"), "")
RETAIL_CONFIG_DWORD_INFO_DIRECT_ACCESS(EXTERNAL_GCHeapHardLimit, W("GCHeapHardLimit"), "Specifies the maximum commit size for the GC heap")
RETAIL_CONFIG_DWORD_INFO_DIRECT_ACCESS(EXTERNAL_GCHeapHardLimitPercent, W("GCHeapHardLimitPercent"), "Specifies the GC heap usage as a percentage of the total memory")
RETAIL_CONFIG_STRING_INFO(EXTERNAL_GCHeapAffinitizeRanges, W("GCHeapAffinitizeRanges"), "Specifies list of processors for Server GC threads. The format is a comma separated list of processor numbers or ranges of processor numbers. Example: 1,3,5,7-9,12")
RETAIL_CONFIG_DWORD_INFO_DIRECT_ACCESS(EXTERNAL_GCLargePages, W("GCLargePages"), "Specifies whether large pages should be used when a heap hard limit is set")

///
/// IBC
Expand Down
1 change: 1 addition & 0 deletions src/pal/inc/pal.h
Original file line number Diff line number Diff line change
Expand Up @@ -2558,6 +2558,7 @@ SetErrorMode(
#define MEM_MAPPED 0x40000
#define MEM_TOP_DOWN 0x100000
#define MEM_WRITE_WATCH 0x200000
#define MEM_LARGE_PAGES 0x20000000
#define MEM_RESERVE_EXECUTABLE 0x40000000 // reserve memory using executable memory allocator

PALIMPORT
Expand Down
Loading