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

Commit 77efbd4

Browse files
committed
Add Large pages support in GC
1 parent 10510b5 commit 77efbd4

File tree

11 files changed

+218
-15
lines changed

11 files changed

+218
-15
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
@@ -204,6 +204,13 @@ class GCToOSInterface
204204
// true if it has succeeded, false if it has failed
205205
static bool VirtualCommit(void *address, size_t size, uint32_t node = NUMA_NODE_UNDEFINED);
206206

207+
// Reserve and Commit virtual memory range for Large Pages
208+
// Parameters:
209+
// size - size of the virtual memory range
210+
// Return:
211+
// Address of the allocated memory
212+
static void* VirtualReserveAndCommitLargePages(size_t size);
213+
207214
// Decomit virtual memory range.
208215
// Parameters:
209216
// address - starting virtual address

src/gc/gc.cpp

Lines changed: 23 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -2411,6 +2411,7 @@ void qsort1(uint8_t** low, uint8_t** high, unsigned int depth);
24112411
#endif //USE_INTROSORT
24122412

24132413
void* virtual_alloc (size_t size);
2414+
void* virtual_alloc (size_t size, bool use_large_pages_p);
24142415
void virtual_free (void* add, size_t size);
24152416

24162417
/* per heap static initialization */
@@ -2826,6 +2827,7 @@ GCSpinLock gc_heap::gc_lock;
28262827

28272828
size_t gc_heap::eph_gen_starts_size = 0;
28282829
heap_segment* gc_heap::segment_standby_list;
2830+
size_t gc_heap::use_large_pages_p = 0;
28292831
size_t gc_heap::last_gc_index = 0;
28302832
#ifdef SEG_MAPPING_TABLE
28312833
size_t gc_heap::min_segment_size = 0;
@@ -4271,7 +4273,7 @@ typedef struct
42714273

42724274
initial_memory_details memory_details;
42734275

4274-
BOOL reserve_initial_memory (size_t normal_size, size_t large_size, size_t num_heaps)
4276+
BOOL reserve_initial_memory (size_t normal_size, size_t large_size, size_t num_heaps, bool use_large_pages_p)
42754277
{
42764278
BOOL reserve_success = FALSE;
42774279

@@ -4312,7 +4314,7 @@ BOOL reserve_initial_memory (size_t normal_size, size_t large_size, size_t num_h
43124314

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

4315-
uint8_t* allatonce_block = (uint8_t*)virtual_alloc (requestedMemory);
4317+
uint8_t* allatonce_block = (uint8_t*)virtual_alloc (requestedMemory, use_large_pages_p);
43164318
if (allatonce_block)
43174319
{
43184320
g_gc_lowest_address = allatonce_block;
@@ -4332,10 +4334,10 @@ BOOL reserve_initial_memory (size_t normal_size, size_t large_size, size_t num_h
43324334
// try to allocate 2 blocks
43334335
uint8_t* b1 = 0;
43344336
uint8_t* b2 = 0;
4335-
b1 = (uint8_t*)virtual_alloc (memory_details.block_count * normal_size);
4337+
b1 = (uint8_t*)virtual_alloc (memory_details.block_count * normal_size, use_large_pages_p);
43364338
if (b1)
43374339
{
4338-
b2 = (uint8_t*)virtual_alloc (memory_details.block_count * large_size);
4340+
b2 = (uint8_t*)virtual_alloc (memory_details.block_count * large_size, use_large_pages_p);
43394341
if (b2)
43404342
{
43414343
memory_details.allocation_pattern = initial_memory_details::TWO_STAGE;
@@ -4368,7 +4370,7 @@ BOOL reserve_initial_memory (size_t normal_size, size_t large_size, size_t num_h
43684370
memory_details.block_size_normal :
43694371
memory_details.block_size_large);
43704372
current_block->memory_base =
4371-
(uint8_t*)virtual_alloc (block_size);
4373+
(uint8_t*)virtual_alloc (block_size, use_large_pages_p);
43724374
if (current_block->memory_base == 0)
43734375
{
43744376
// Free the blocks that we've allocated so far
@@ -4476,6 +4478,11 @@ heap_segment* get_initial_segment (size_t size, int h_number)
44764478
}
44774479

44784480
void* virtual_alloc (size_t size)
4481+
{
4482+
return virtual_alloc(size, false);
4483+
}
4484+
4485+
void* virtual_alloc (size_t size, bool use_large_pages_p)
44794486
{
44804487
size_t requested_size = size;
44814488

@@ -4496,7 +4503,8 @@ void* virtual_alloc (size_t size)
44964503
flags = VirtualReserveFlags::WriteWatch;
44974504
}
44984505
#endif // !FEATURE_USE_SOFTWARE_WRITE_WATCH_FOR_GC_HEAP
4499-
void* prgmem = GCToOSInterface::VirtualReserve (requested_size, card_size * card_word_width, flags);
4506+
4507+
void* prgmem = use_large_pages_p ? GCToOSInterface::VirtualReserveAndCommitLargePages(requested_size) : GCToOSInterface::VirtualReserve(requested_size, card_size * card_word_width, flags);
45004508
void *aligned_mem = prgmem;
45014509

45024510
// We don't want (prgmem + size) to be right at the end of the address space
@@ -5554,10 +5562,8 @@ bool gc_heap::virtual_commit (void* address, size_t size, int h_number, bool* ha
55545562
}
55555563
}
55565564

5557-
// If it's a valid heap number it means it's commiting for memory on the GC heap.
5558-
bool commit_succeeded_p = ((h_number >= 0) ?
5559-
virtual_alloc_commit_for_heap (address, size, h_number) :
5560-
GCToOSInterface::VirtualCommit(address, size));
5565+
// If it's a valid heap number it means it's commiting for memory on the GC heap. In addition if large pages is enabled, we set commit_succeeded_p to true because memory is already committed.
5566+
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));
55615567

55625568
if (!commit_succeeded_p && heap_hard_limit)
55635569
{
@@ -9308,7 +9314,7 @@ heap_segment* gc_heap::make_heap_segment (uint8_t* new_pages, size_t size, int h
93089314
heap_segment_mem (new_segment) = start;
93099315
heap_segment_used (new_segment) = start;
93109316
heap_segment_reserved (new_segment) = new_pages + size;
9311-
heap_segment_committed (new_segment) = new_pages + initial_commit;
9317+
heap_segment_committed (new_segment) = (use_large_pages_p ? heap_segment_reserved(new_segment) : (new_pages + initial_commit));
93129318
init_heap_segment (new_segment);
93139319
dprintf (2, ("Creating heap segment %Ix", (size_t)new_segment));
93149320
return new_segment;
@@ -9399,6 +9405,8 @@ void gc_heap::reset_heap_segment_pages (heap_segment* seg)
93999405
void gc_heap::decommit_heap_segment_pages (heap_segment* seg,
94009406
size_t extra_space)
94019407
{
9408+
if (use_large_pages_p)
9409+
return;
94029410
uint8_t* page_start = align_on_page (heap_segment_allocated(seg));
94039411
size_t size = heap_segment_committed (seg) - page_start;
94049412
extra_space = align_on_page (extra_space);
@@ -10108,12 +10116,15 @@ HRESULT gc_heap::initialize_gc (size_t segment_size,
1010810116
block_count = 1;
1010910117
#endif //MULTIPLE_HEAPS
1011010118

10119+
use_large_pages_p = false;
10120+
1011110121
if (heap_hard_limit)
1011210122
{
1011310123
check_commit_cs.Initialize();
10124+
use_large_pages_p = GCConfig::GetGCLargePages();
1011410125
}
1011510126

10116-
if (!reserve_initial_memory(segment_size,heap_size,block_count))
10127+
if (!reserve_initial_memory(segment_size,heap_size,block_count,use_large_pages_p))
1011710128
return E_OUTOFMEMORY;
1011810129

1011910130
#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/gcenv.unix.cpp

Lines changed: 36 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -271,7 +271,7 @@ void GCToOSInterface::YieldThread(uint32_t switchCount)
271271
// flags - flags to control special settings like write watching
272272
// Return:
273273
// Starting virtual address of the reserved range
274-
void* GCToOSInterface::VirtualReserve(size_t size, size_t alignment, uint32_t flags)
274+
static void* VirtualReserveInner(size_t size, size_t alignment, uint32_t flags, uint32_t hugePagesFlag = 0)
275275
{
276276
assert(!(flags & VirtualReserveFlags::WriteWatch) && "WriteWatch not supported on Unix");
277277
if (alignment == 0)
@@ -280,7 +280,7 @@ void* GCToOSInterface::VirtualReserve(size_t size, size_t alignment, uint32_t fl
280280
}
281281

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

285285
if (pRetVal != NULL)
286286
{
@@ -305,6 +305,18 @@ void* GCToOSInterface::VirtualReserve(size_t size, size_t alignment, uint32_t fl
305305
return pRetVal;
306306
}
307307

308+
// Reserve virtual memory range.
309+
// Parameters:
310+
// size - size of the virtual memory range
311+
// alignment - requested memory alignment, 0 means no specific alignment requested
312+
// flags - flags to control special settings like write watching
313+
// Return:
314+
// Starting virtual address of the reserved range
315+
void* GCToOSInterface::VirtualReserve(size_t size, size_t alignment, uint32_t flags)
316+
{
317+
return VirtualReserveInner(size, alignment, flags);
318+
}
319+
308320
// Release virtual memory range previously reserved using VirtualReserve
309321
// Parameters:
310322
// address - starting virtual address
@@ -318,6 +330,28 @@ bool GCToOSInterface::VirtualRelease(void* address, size_t size)
318330
return (ret == 0);
319331
}
320332

333+
// Commit virtual memory range.
334+
// Parameters:
335+
// size - size of the virtual memory range
336+
// Return:
337+
// Starting virtual address of the committed range
338+
void* GCToOSInterface::VirtualReserveAndCommitLargePages(size_t size)
339+
{
340+
#ifdef __linux__
341+
uint32_t largePagesFlag = MAP_HUGETLB;
342+
#else
343+
uint32_t largePagesFlag = 0;
344+
#endif
345+
346+
void* pRetVal = VirtualReserveInner(size, OS_PAGE_SIZE, 0, largePagesFlag);
347+
if (VirtualCommit(pRetVal, size, NUMA_NODE_UNDEFINED))
348+
{
349+
return pRetVal;
350+
}
351+
352+
return nullptr;
353+
}
354+
321355
// Commit virtual memory range. It must be part of a range reserved using VirtualReserve.
322356
// Parameters:
323357
// 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
typedef BOOL (WINAPI *PIS_PROCESS_IN_JOB)(HANDLE processHandle, HANDLE jobHandle, BOOL* result);
3133
typedef BOOL (WINAPI *PQUERY_INFORMATION_JOB_OBJECT)(HANDLE jobHandle, JOBOBJECTINFOCLASS jobObjectInfoClass, void* lpJobObjectInfo, DWORD cbJobObjectInfoLength, LPDWORD lpReturnLength);
3234

@@ -89,6 +91,42 @@ DWORD LCM(DWORD u, DWORD v)
8991
}
9092
#endif
9193

94+
bool InitLargePagesPrivilege()
95+
{
96+
TOKEN_PRIVILEGES tp;
97+
LUID luid;
98+
if (!LookupPrivilegeValueW(nullptr, SE_LOCK_MEMORY_NAME, &luid))
99+
{
100+
return false;
101+
}
102+
103+
tp.PrivilegeCount = 1;
104+
tp.Privileges[0].Luid = luid;
105+
tp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
106+
107+
HANDLE token;
108+
if (!OpenProcessToken(::GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES, &token))
109+
{
110+
return false;
111+
}
112+
113+
auto retVal = AdjustTokenPrivileges(token, FALSE, &tp, 0, nullptr, 0);
114+
auto gls = GetLastError();
115+
CloseHandle(token);
116+
117+
if (!retVal)
118+
{
119+
return false;
120+
}
121+
122+
if (gls != 0)
123+
{
124+
return false;
125+
}
126+
127+
return true;
128+
}
129+
92130
bool InitCPUGroupInfoArray()
93131
{
94132
#if (defined(_TARGET_AMD64_) || defined(_TARGET_ARM64_))
@@ -599,6 +637,31 @@ bool GCToOSInterface::VirtualRelease(void* address, size_t size)
599637
return !!::VirtualFree(address, 0, MEM_RELEASE);
600638
}
601639

640+
// Commit virtual memory range.
641+
// Parameters:
642+
// size - size of the virtual memory range
643+
// Return:
644+
// Starting virtual address of the committed range
645+
void* GCToOSInterface::VirtualReserveAndCommitLargePages(size_t size)
646+
{
647+
void* pRetVal = nullptr;
648+
649+
if (!g_SeLockMemoryPrivilegeAcquired)
650+
{
651+
if (!InitLargePagesPrivilege())
652+
{
653+
return nullptr;
654+
}
655+
656+
g_SeLockMemoryPrivilegeAcquired = true;
657+
}
658+
659+
auto largePageMinimum = GetLargePageMinimum();
660+
size = (size + (largePageMinimum - 1)) & ~(largePageMinimum - 1);
661+
662+
return ::VirtualAlloc(nullptr, size, MEM_RESERVE | MEM_COMMIT | MEM_LARGE_PAGES, PAGE_READWRITE);
663+
}
664+
602665
// Commit virtual memory range. It must be part of a range reserved using VirtualReserve.
603666
// Parameters:
604667
// address - starting virtual address

src/inc/clrconfigvalues.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -329,6 +329,7 @@ RETAIL_CONFIG_DWORD_INFO(EXTERNAL_GCHighMemPercent, W("GCHighMemPercent"), 0, "S
329329
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")
332+
RETAIL_CONFIG_DWORD_INFO_DIRECT_ACCESS(EXTERNAL_GCLargePages, W("GCLargePages"), "Specifies whether large pages should be used when a heap hard limit is set")
332333

333334
///
334335
/// IBC

src/pal/inc/pal.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2550,6 +2550,7 @@ SetErrorMode(
25502550
#define MEM_MAPPED 0x40000
25512551
#define MEM_TOP_DOWN 0x100000
25522552
#define MEM_WRITE_WATCH 0x200000
2553+
#define MEM_LARGE_PAGES 0x20000000
25532554
#define MEM_RESERVE_EXECUTABLE 0x40000000 // reserve memory using executable memory allocator
25542555

25552556
PALIMPORT

0 commit comments

Comments
 (0)