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

Commit d33f73f

Browse files
committed
Add Large pages support in GC
1 parent 0d581c7 commit d33f73f

File tree

13 files changed

+231
-14
lines changed

13 files changed

+231
-14
lines changed

src/gc/CMakeLists.txt

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -84,7 +84,8 @@ if(WIN32)
8484
set (GC_LINK_LIBRARIES
8585
${STATIC_MT_CRT_LIB}
8686
${STATIC_MT_VCRT_LIB}
87-
kernel32.lib)
87+
kernel32.lib
88+
advapi32.lib)
8889
else()
8990
set (GC_LINK_LIBRARIES)
9091
endif(WIN32)

src/gc/env/gcenv.os.h

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -275,6 +275,13 @@ class GCToOSInterface
275275
// true if it has succeeded, false if it has failed
276276
static bool VirtualCommit(void *address, size_t size, uint16_t node = NUMA_NODE_UNDEFINED);
277277

278+
// Reserve and Commit virtual memory range for Large Pages
279+
// Parameters:
280+
// size - size of the virtual memory range
281+
// Return:
282+
// Address of the allocated memory
283+
static void* VirtualReserveAndCommitLargePages(size_t size);
284+
278285
// Decomit virtual memory range.
279286
// Parameters:
280287
// address - starting virtual address

src/gc/gc.cpp

Lines changed: 25 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -2403,6 +2403,7 @@ void qsort1(uint8_t** low, uint8_t** high, unsigned int depth);
24032403
#endif //USE_INTROSORT
24042404

24052405
void* virtual_alloc (size_t size);
2406+
void* virtual_alloc (size_t size, bool use_large_pages_p);
24062407
void virtual_free (void* add, size_t size);
24072408

24082409
/* per heap static initialization */
@@ -2818,6 +2819,7 @@ GCSpinLock gc_heap::gc_lock;
28182819

28192820
size_t gc_heap::eph_gen_starts_size = 0;
28202821
heap_segment* gc_heap::segment_standby_list;
2822+
size_t gc_heap::use_large_pages_p = 0;
28212823
size_t gc_heap::last_gc_index = 0;
28222824
#ifdef SEG_MAPPING_TABLE
28232825
size_t gc_heap::min_segment_size = 0;
@@ -4263,7 +4265,7 @@ typedef struct
42634265

42644266
initial_memory_details memory_details;
42654267

4266-
BOOL reserve_initial_memory (size_t normal_size, size_t large_size, size_t num_heaps)
4268+
BOOL reserve_initial_memory (size_t normal_size, size_t large_size, size_t num_heaps, bool use_large_pages_p)
42674269
{
42684270
BOOL reserve_success = FALSE;
42694271

@@ -4304,7 +4306,7 @@ BOOL reserve_initial_memory (size_t normal_size, size_t large_size, size_t num_h
43044306

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

4307-
uint8_t* allatonce_block = (uint8_t*)virtual_alloc (requestedMemory);
4309+
uint8_t* allatonce_block = (uint8_t*)virtual_alloc (requestedMemory, use_large_pages_p);
43084310
if (allatonce_block)
43094311
{
43104312
g_gc_lowest_address = allatonce_block;
@@ -4324,10 +4326,10 @@ BOOL reserve_initial_memory (size_t normal_size, size_t large_size, size_t num_h
43244326
// try to allocate 2 blocks
43254327
uint8_t* b1 = 0;
43264328
uint8_t* b2 = 0;
4327-
b1 = (uint8_t*)virtual_alloc (memory_details.block_count * normal_size);
4329+
b1 = (uint8_t*)virtual_alloc (memory_details.block_count * normal_size, use_large_pages_p);
43284330
if (b1)
43294331
{
4330-
b2 = (uint8_t*)virtual_alloc (memory_details.block_count * large_size);
4332+
b2 = (uint8_t*)virtual_alloc (memory_details.block_count * large_size, use_large_pages_p);
43314333
if (b2)
43324334
{
43334335
memory_details.allocation_pattern = initial_memory_details::TWO_STAGE;
@@ -4360,7 +4362,7 @@ BOOL reserve_initial_memory (size_t normal_size, size_t large_size, size_t num_h
43604362
memory_details.block_size_normal :
43614363
memory_details.block_size_large);
43624364
current_block->memory_base =
4363-
(uint8_t*)virtual_alloc (block_size);
4365+
(uint8_t*)virtual_alloc (block_size, use_large_pages_p);
43644366
if (current_block->memory_base == 0)
43654367
{
43664368
// Free the blocks that we've allocated so far
@@ -4468,6 +4470,11 @@ heap_segment* get_initial_segment (size_t size, int h_number)
44684470
}
44694471

44704472
void* virtual_alloc (size_t size)
4473+
{
4474+
return virtual_alloc(size, false);
4475+
}
4476+
4477+
void* virtual_alloc (size_t size, bool use_large_pages_p)
44714478
{
44724479
size_t requested_size = size;
44734480

@@ -4488,7 +4495,8 @@ void* virtual_alloc (size_t size)
44884495
flags = VirtualReserveFlags::WriteWatch;
44894496
}
44904497
#endif // !FEATURE_USE_SOFTWARE_WRITE_WATCH_FOR_GC_HEAP
4491-
void* prgmem = GCToOSInterface::VirtualReserve (requested_size, card_size * card_word_width, flags);
4498+
4499+
void* prgmem = use_large_pages_p ? GCToOSInterface::VirtualReserveAndCommitLargePages(requested_size) : GCToOSInterface::VirtualReserve(requested_size, card_size * card_word_width, flags);
44924500
void *aligned_mem = prgmem;
44934501

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

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

54735482
if (!commit_succeeded_p && heap_hard_limit)
54745483
{
@@ -9219,7 +9228,7 @@ heap_segment* gc_heap::make_heap_segment (uint8_t* new_pages, size_t size, int h
92199228
heap_segment_mem (new_segment) = start;
92209229
heap_segment_used (new_segment) = start;
92219230
heap_segment_reserved (new_segment) = new_pages + size;
9222-
heap_segment_committed (new_segment) = new_pages + initial_commit;
9231+
heap_segment_committed (new_segment) = (use_large_pages_p ? heap_segment_reserved(new_segment) : (new_pages + initial_commit));
92239232
init_heap_segment (new_segment);
92249233
dprintf (2, ("Creating heap segment %Ix", (size_t)new_segment));
92259234
return new_segment;
@@ -9310,6 +9319,8 @@ void gc_heap::reset_heap_segment_pages (heap_segment* seg)
93109319
void gc_heap::decommit_heap_segment_pages (heap_segment* seg,
93119320
size_t extra_space)
93129321
{
9322+
if (use_large_pages_p)
9323+
return;
93139324
uint8_t* page_start = align_on_page (heap_segment_allocated(seg));
93149325
size_t size = heap_segment_committed (seg) - page_start;
93159326
extra_space = align_on_page (extra_space);
@@ -10019,12 +10030,15 @@ HRESULT gc_heap::initialize_gc (size_t segment_size,
1001910030
block_count = 1;
1002010031
#endif //MULTIPLE_HEAPS
1002110032

10033+
use_large_pages_p = false;
10034+
1002210035
if (heap_hard_limit)
1002310036
{
1002410037
check_commit_cs.Initialize();
10038+
use_large_pages_p = GCConfig::GetGCLargePages();
1002510039
}
1002610040

10027-
if (!reserve_initial_memory(segment_size,heap_size,block_count))
10041+
if (!reserve_initial_memory(segment_size,heap_size,block_count,use_large_pages_p))
1002810042
return E_OUTOFMEMORY;
1002910043

1003010044
#ifdef CARD_BUNDLE

src/gc/gcconfig.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,7 @@ class GCConfigStringHolder
7474
"Specifies the name of the GC config log file") \
7575
BOOL_CONFIG(GCNumaAware, "GCNumaAware", true, "Enables numa allocations in the GC") \
7676
BOOL_CONFIG(GCCpuGroup, "GCCpuGroup", false, "Enables CPU groups in the GC") \
77+
BOOL_CONFIG(GCLargePages, "GCLargePages", false, "Enables using Large Pages in the GC") \
7778
INT_CONFIG(HeapVerifyLevel, "HeapVerify", HEAPVERIFY_NONE, \
7879
"When set verifies the integrity of the managed heap on entry and exit of each GC") \
7980
INT_CONFIG(LOHCompactionMode, "GCLOHCompact", 0, "Specifies the LOH compaction mode") \

src/gc/gcpriv.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3145,6 +3145,10 @@ class gc_heap
31453145
PER_HEAP_ISOLATED
31463146
size_t current_total_committed_gc_own;
31473147

3148+
// This is if large pages should be used.
3149+
PER_HEAP_ISOLATED
3150+
size_t use_large_pages_p;
3151+
31483152
PER_HEAP_ISOLATED
31493153
size_t last_gc_index;
31503154

src/gc/sample/CMakeLists.txt

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,14 @@ set(SOURCES
2424
../softwarewritewatch.cpp
2525
)
2626

27+
if(WIN32)
28+
set (GC_LINK_LIBRARIES
29+
${STATIC_MT_CRT_LIB}
30+
${STATIC_MT_VCRT_LIB}
31+
kernel32.lib
32+
advapi32.lib)
33+
endif(WIN32)
34+
2735
if(WIN32)
2836
list(APPEND SOURCES
2937
../windows/gcenv.windows.cpp)
@@ -36,3 +44,7 @@ endif()
3644
_add_executable(gcsample
3745
${SOURCES}
3846
)
47+
48+
if(WIN32)
49+
target_link_libraries(gcsample ${GC_LINK_LIBRARIES})
50+
endif()

src/gc/unix/config.h.in

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
#cmakedefine01 HAVE_SYS_MMAN_H
1010
#cmakedefine01 HAVE_PTHREAD_THREADID_NP
1111
#cmakedefine01 HAVE_PTHREAD_GETTHREADID_NP
12+
#cmakedefine01 HAVE_MAP_HUGETLB
1213
#cmakedefine01 HAVE_SCHED_GETCPU
1314
#cmakedefine01 HAVE_NUMA_H
1415
#cmakedefine01 HAVE_VM_ALLOCATE

src/gc/unix/configure.cmake

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,15 @@ check_cxx_source_compiles("
2424
}
2525
" HAVE_PTHREAD_GETTHREADID_NP)
2626

27+
check_cxx_source_compiles("
28+
#include <sys/mman.h>
29+
30+
int main()
31+
{
32+
return MAP_HUGETLB;
33+
}
34+
" HAVE_MAP_HUGETLB)
35+
2736
check_cxx_source_runs("
2837
#include <sched.h>
2938

src/gc/unix/gcenv.unix.cpp

Lines changed: 36 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -379,7 +379,7 @@ void GCToOSInterface::YieldThread(uint32_t switchCount)
379379
// flags - flags to control special settings like write watching
380380
// Return:
381381
// Starting virtual address of the reserved range
382-
void* GCToOSInterface::VirtualReserve(size_t size, size_t alignment, uint32_t flags)
382+
static void* VirtualReserveInner(size_t size, size_t alignment, uint32_t flags, uint32_t hugePagesFlag = 0)
383383
{
384384
assert(!(flags & VirtualReserveFlags::WriteWatch) && "WriteWatch not supported on Unix");
385385
if (alignment == 0)
@@ -388,7 +388,7 @@ void* GCToOSInterface::VirtualReserve(size_t size, size_t alignment, uint32_t fl
388388
}
389389

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

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

416+
// Reserve virtual memory range.
417+
// Parameters:
418+
// size - size of the virtual memory range
419+
// alignment - requested memory alignment, 0 means no specific alignment requested
420+
// flags - flags to control special settings like write watching
421+
// Return:
422+
// Starting virtual address of the reserved range
423+
void* GCToOSInterface::VirtualReserve(size_t size, size_t alignment, uint32_t flags)
424+
{
425+
return VirtualReserveInner(size, alignment, flags);
426+
}
427+
416428
// Release virtual memory range previously reserved using VirtualReserve
417429
// Parameters:
418430
// address - starting virtual address
@@ -426,6 +438,28 @@ bool GCToOSInterface::VirtualRelease(void* address, size_t size)
426438
return (ret == 0);
427439
}
428440

441+
// Commit virtual memory range.
442+
// Parameters:
443+
// size - size of the virtual memory range
444+
// Return:
445+
// Starting virtual address of the committed range
446+
void* GCToOSInterface::VirtualReserveAndCommitLargePages(size_t size)
447+
{
448+
#if HAVE_MAP_HUGETLB
449+
uint32_t largePagesFlag = MAP_HUGETLB;
450+
#else
451+
uint32_t largePagesFlag = 0;
452+
#endif
453+
454+
void* pRetVal = VirtualReserveInner(size, OS_PAGE_SIZE, 0, largePagesFlag);
455+
if (VirtualCommit(pRetVal, size, NUMA_NODE_UNDEFINED))
456+
{
457+
return pRetVal;
458+
}
459+
460+
return nullptr;
461+
}
462+
429463
// Commit virtual memory range. It must be part of a range reserved using VirtualReserve.
430464
// Parameters:
431465
// address - starting virtual address

src/gc/windows/gcenv.windows.cpp

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,8 @@ static size_t g_RestrictedPhysicalMemoryLimit = (size_t)UINTPTR_MAX;
2727
// memory on the machine/in the container, we need to restrict by the VM.
2828
static bool g_UseRestrictedVirtualMemory = false;
2929

30+
static bool g_SeLockMemoryPrivilegeAcquired = false;
31+
3032
static AffinitySet g_processAffinitySet;
3133

3234
typedef BOOL (WINAPI *PIS_PROCESS_IN_JOB)(HANDLE processHandle, HANDLE jobHandle, BOOL* result);
@@ -114,6 +116,42 @@ DWORD LCM(DWORD u, DWORD v)
114116
}
115117
#endif
116118

119+
bool InitLargePagesPrivilege()
120+
{
121+
TOKEN_PRIVILEGES tp;
122+
LUID luid;
123+
if (!LookupPrivilegeValueW(nullptr, SE_LOCK_MEMORY_NAME, &luid))
124+
{
125+
return false;
126+
}
127+
128+
tp.PrivilegeCount = 1;
129+
tp.Privileges[0].Luid = luid;
130+
tp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
131+
132+
HANDLE token;
133+
if (!OpenProcessToken(::GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES, &token))
134+
{
135+
return false;
136+
}
137+
138+
BOOL retVal = AdjustTokenPrivileges(token, FALSE, &tp, 0, nullptr, 0);
139+
DWORD gls = GetLastError();
140+
CloseHandle(token);
141+
142+
if (!retVal)
143+
{
144+
return false;
145+
}
146+
147+
if (gls != 0)
148+
{
149+
return false;
150+
}
151+
152+
return true;
153+
}
154+
117155
bool InitCPUGroupInfoArray()
118156
{
119157
#if (defined(_TARGET_AMD64_) || defined(_TARGET_ARM64_))
@@ -699,6 +737,31 @@ bool GCToOSInterface::VirtualRelease(void* address, size_t size)
699737
return !!::VirtualFree(address, 0, MEM_RELEASE);
700738
}
701739

740+
// Commit virtual memory range.
741+
// Parameters:
742+
// size - size of the virtual memory range
743+
// Return:
744+
// Starting virtual address of the committed range
745+
void* GCToOSInterface::VirtualReserveAndCommitLargePages(size_t size)
746+
{
747+
void* pRetVal = nullptr;
748+
749+
if (!g_SeLockMemoryPrivilegeAcquired)
750+
{
751+
if (!InitLargePagesPrivilege())
752+
{
753+
return nullptr;
754+
}
755+
756+
g_SeLockMemoryPrivilegeAcquired = true;
757+
}
758+
759+
SIZE_T largePageMinimum = GetLargePageMinimum();
760+
size = (size + (largePageMinimum - 1)) & ~(largePageMinimum - 1);
761+
762+
return ::VirtualAlloc(nullptr, size, MEM_RESERVE | MEM_COMMIT | MEM_LARGE_PAGES, PAGE_READWRITE);
763+
}
764+
702765
// Commit virtual memory range. It must be part of a range reserved using VirtualReserve.
703766
// Parameters:
704767
// address - starting virtual address

src/inc/clrconfigvalues.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -330,6 +330,7 @@ RETAIL_CONFIG_STRING_INFO(EXTERNAL_GCName, W("GCName"), "")
330330
RETAIL_CONFIG_DWORD_INFO_DIRECT_ACCESS(EXTERNAL_GCHeapHardLimit, W("GCHeapHardLimit"), "Specifies the maximum commit size for the GC heap")
331331
RETAIL_CONFIG_DWORD_INFO_DIRECT_ACCESS(EXTERNAL_GCHeapHardLimitPercent, W("GCHeapHardLimitPercent"), "Specifies the GC heap usage as a percentage of the total memory")
332332
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")
333+
RETAIL_CONFIG_DWORD_INFO_DIRECT_ACCESS(EXTERNAL_GCLargePages, W("GCLargePages"), "Specifies whether large pages should be used when a heap hard limit is set")
333334

334335
///
335336
/// IBC

src/pal/inc/pal.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2558,6 +2558,7 @@ SetErrorMode(
25582558
#define MEM_MAPPED 0x40000
25592559
#define MEM_TOP_DOWN 0x100000
25602560
#define MEM_WRITE_WATCH 0x200000
2561+
#define MEM_LARGE_PAGES 0x20000000
25612562
#define MEM_RESERVE_EXECUTABLE 0x40000000 // reserve memory using executable memory allocator
25622563

25632564
PALIMPORT

0 commit comments

Comments
 (0)