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

Commit 86b01c7

Browse files
committed
Add Large pages support in GC
1 parent da6ed11 commit 86b01c7

File tree

11 files changed

+236
-11
lines changed

11 files changed

+236
-11
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: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -204,6 +204,14 @@ 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+
// Commit virtual memory range. It must be part of a range reserved using VirtualReserve.
208+
// Parameters:
209+
// address - starting virtual address
210+
// size - size of the virtual memory range
211+
// Return:
212+
// true if it has succeeded, false if it has failed
213+
static void* VirtualReserveAndCommitLargePages(size_t size, uint32_t node = NUMA_NODE_UNDEFINED);
214+
207215
// Decomit virtual memory range.
208216
// Parameters:
209217
// address - starting virtual address

src/gc/gc.cpp

Lines changed: 21 additions & 8 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, card_size * card_word_width) : 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
@@ -9308,7 +9316,7 @@ heap_segment* gc_heap::make_heap_segment (uint8_t* new_pages, size_t size, int h
93089316
heap_segment_mem (new_segment) = start;
93099317
heap_segment_used (new_segment) = start;
93109318
heap_segment_reserved (new_segment) = new_pages + size;
9311-
heap_segment_committed (new_segment) = new_pages + initial_commit;
9319+
heap_segment_committed (new_segment) = (use_large_pages_p ? heap_segment_reserved(new_segment) : (new_pages + initial_commit));
93129320
init_heap_segment (new_segment);
93139321
dprintf (2, ("Creating heap segment %Ix", (size_t)new_segment));
93149322
return new_segment;
@@ -9399,6 +9407,8 @@ void gc_heap::reset_heap_segment_pages (heap_segment* seg)
93999407
void gc_heap::decommit_heap_segment_pages (heap_segment* seg,
94009408
size_t extra_space)
94019409
{
9410+
if (use_large_pages_p)
9411+
return;
94029412
uint8_t* page_start = align_on_page (heap_segment_allocated(seg));
94039413
size_t size = heap_segment_committed (seg) - page_start;
94049414
extra_space = align_on_page (extra_space);
@@ -10108,12 +10118,15 @@ HRESULT gc_heap::initialize_gc (size_t segment_size,
1010810118
block_count = 1;
1010910119
#endif //MULTIPLE_HEAPS
1011010120

10121+
use_large_pages_p = false;
10122+
1011110123
if (heap_hard_limit)
1011210124
{
1011310125
check_commit_cs.Initialize();
10126+
use_large_pages_p = GCConfig::GetGCLargePages();
1011410127
}
1011510128

10116-
if (!reserve_initial_memory(segment_size,heap_size,block_count))
10129+
if (!reserve_initial_memory(segment_size,heap_size,block_count,use_large_pages_p))
1011710130
return E_OUTOFMEMORY;
1011810131

1011910132
#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: 33 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 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,25 @@ 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+
// alignment - requested memory alignment, 0 means no specific alignment requested
337+
// flags - flags to control special settings like write watching
338+
// node - NUMA node
339+
// Return:
340+
// Starting virtual address of the committed range
341+
void* GCToOSInterface::VirtualReserveAndCommitLargePages(size_t size, uint32_t node)
342+
{
343+
void* pRetVal = VirtualReserveInner(size, alignment, 0, MAP_HUGETLB);
344+
if (VirtualCommit(pRetVal, size, node))
345+
{
346+
return pRetVal;
347+
}
348+
349+
return nullptr;
350+
}
351+
321352
// Commit virtual memory range. It must be part of a range reserved using VirtualReserve.
322353
// Parameters:
323354
// address - starting virtual address

src/gc/windows/gcenv.windows.cpp

Lines changed: 75 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,41 @@ 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+
CloseHandle(token);
115+
116+
if (!retVal)
117+
{
118+
return false;
119+
}
120+
121+
if (GetLastError() != 0)
122+
{
123+
return false;
124+
}
125+
126+
return true;
127+
}
128+
92129
bool InitCPUGroupInfoArray()
93130
{
94131
#if (defined(_TARGET_AMD64_) || defined(_TARGET_ARM64_))
@@ -599,6 +636,44 @@ bool GCToOSInterface::VirtualRelease(void* address, size_t size)
599636
return !!::VirtualFree(address, 0, MEM_RELEASE);
600637
}
601638

639+
// Commit virtual memory range.
640+
// Parameters:
641+
// size - size of the virtual memory range
642+
// alignment - requested memory alignment, 0 means no specific alignment requested
643+
// flags - flags to control special settings like write watching
644+
// node - NUMA node
645+
// Return:
646+
// Starting virtual address of the committed range
647+
void* GCToOSInterface::VirtualReserveAndCommitLargePages(size_t size, uint32_t node)
648+
{
649+
void* pRetVal = nullptr;
650+
651+
if (!g_SeLockMemoryPrivilegeAcquired)
652+
{
653+
if (!InitLargePagesPrivilege())
654+
{
655+
return nullptr;
656+
}
657+
658+
g_SeLockMemoryPrivilegeAcquired = true;
659+
}
660+
661+
auto largePageMinimum = GetLargePageMinimum();
662+
size = (size + (largePageMinimum - 1)) & ~(largePageMinimum - 1);
663+
664+
if (node == NUMA_NODE_UNDEFINED)
665+
{
666+
pRetVal = ::VirtualAlloc(nullptr, size, MEM_RESERVE | MEM_COMMIT | MEM_LARGE_PAGES, PAGE_READWRITE);
667+
}
668+
else
669+
{
670+
assert(g_fEnableGCNumaAware);
671+
pRetVal = ::VirtualAllocExNuma(::GetCurrentProcess(), nullptr, size, MEM_RESERVE | MEM_COMMIT | MEM_LARGE_PAGES, PAGE_READWRITE, node);
672+
}
673+
674+
return pRetVal;
675+
}
676+
602677
// Commit virtual memory range. It must be part of a range reserved using VirtualReserve.
603678
// Parameters:
604679
// address - starting virtual address

src/inc/clrconfigvalues.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -327,6 +327,7 @@ RETAIL_CONFIG_DWORD_INFO(EXTERNAL_GCHighMemPercent, W("GCHighMemPercent"), 0, "S
327327
RETAIL_CONFIG_STRING_INFO(EXTERNAL_GCName, W("GCName"), "")
328328
RETAIL_CONFIG_DWORD_INFO_DIRECT_ACCESS(EXTERNAL_GCHeapHardLimit, W("GCHeapHardLimit"), "Specifies the maximum commit size for the GC heap")
329329
RETAIL_CONFIG_DWORD_INFO_DIRECT_ACCESS(EXTERNAL_GCHeapHardLimitPercent, W("GCHeapHardLimitPercent"), "Specifies the GC heap usage as a percentage of the total memory")
330+
RETAIL_CONFIG_DWORD_INFO_DIRECT_ACCESS(EXTERNAL_GCLargePages, W("GCLargePages"), "Specifies whether large pages should be used when a heap hard limit is set")
330331

331332
///
332333
/// 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)