28 #include "precompiled.h"
73 return VirtualAlloc(p, size, allocationType, protect);
83 if(!pGetCurrentProcessorNumber)
85 if(!pGetNumaProcessorNode)
87 if(!pVirtualAllocExNuma)
104 void NotifyLargePageCommit()
109 void NotifySmallPageCommit()
114 intptr_t largePageCommits;
115 intptr_t smallPageCommits;
124 size_t smallPageCommits = 0;
125 size_t largePageCommits = 0;
126 uintptr_t processorsWithNoCommits = 0;
129 const Statistics& s = statistics[processor];
130 if(s.smallPageCommits == 0 && s.largePageCommits == 0)
131 processorsWithNoCommits |= Bit<uintptr_t>(processor);
132 smallPageCommits += s.smallPageCommits;
133 largePageCommits += s.largePageCommits;
136 const size_t totalCommits = smallPageCommits+largePageCommits;
137 if(totalCommits == 0)
140 const size_t largePageRatio = totalCommits? largePageCommits*100/totalCommits : 0;
141 debug_printf(L
"%d commits (%d, i.e. %d%% of them via large pages)\n", totalCommits, largePageCommits, largePageRatio);
142 if(processorsWithNoCommits != 0)
143 debug_printf(L
" processors with no commits: %x\n", processorsWithNoCommits);
163 if((allocationType & MEM_COMMIT) == 0)
208 const DWORD processor = pGetCurrentProcessorNumber();
209 WARN_IF_FALSE(pGetNumaProcessorNode((UCHAR)processor, &node));
215 const uintptr_t alignedAddress =
round_down(address, largePageSize);
216 const size_t alignedSize =
round_up(size+largePageSize-1, largePageSize);
221 void* largePages = pVirtualAllocExNuma(hProcess,
LPVOID(alignedAddress), alignedSize, allocationType|MEM_LARGE_PAGES, protect, node);
223 if(elapsedTime > 0.5)
227 if((allocationType & MEM_COMMIT) != 0)
228 statistics[processor].NotifyLargePageCommit();
234 void* smallPages = pVirtualAllocExNuma(hProcess,
LPVOID(address), size, allocationType, protect, node);
237 if((allocationType & MEM_COMMIT) != 0)
238 statistics[processor].NotifySmallPageCommit();
243 MEMORY_BASIC_INFORMATION mbi = {0};
244 (void)VirtualQuery(LPCVOID(address), &mbi,
sizeof(mbi));
245 debug_printf(L
"Allocation failed: base=%p allocBase=%p allocProt=%d size=%d state=%d prot=%d type=%d\n", mbi.BaseAddress, mbi.AllocationBase, mbi.AllocationProtect, mbi.RegionSize, mbi.State, mbi.Protect, mbi.Type);
277 if(base != 0 || !
cpu_CAS(&base, intptr_t(0), intptr_t(
this)))
284 this->commitSize = commitSize;
285 this->pageType = pageType;
289 totalSize =
round_up(size+alignment-1, alignment);
296 debug_printf(L
"AllocateLargeOrSmallPages of %lld failed\n", (
u64)totalSize);
301 alignedBase =
round_up(uintptr_t(base), alignment);
302 alignedEnd = alignedBase +
round_up(size, alignment);
309 alignment = alignedBase = alignedEnd = 0;
315 bool Contains(uintptr_t address)
const
319 ENSURE(!(uintptr_t(base) <= address && address < alignedBase));
321 return (alignedBase <= address && address < alignedEnd);
324 bool Commit(uintptr_t address)
327 const uintptr_t alignedAddress =
round_down(address, alignment);
328 ENSURE(alignedBase <= alignedAddress && alignedAddress+commitSize <= alignedEnd);
329 return vm::Commit(alignedAddress, commitSize, pageType, prot);
337 uintptr_t alignedBase;
338 uintptr_t alignedEnd;
342 volatile intptr_t base;
354 return L
"Out of address space (64-bit OS may help)";
361 return L
"Out of address space (Windows only provides 8 TiB)";
363 return L
"Out of address space";
376 AddressRangeDescriptor& d =
ranges[idxRange];
377 if(d.Contains(address))
389 Status ret =
ranges[idxRange].Allocate(size, commitSize, pageType, prot);
391 return (
void*)
ranges[idxRange].alignedBase;
414 debug_printf(L
"No AddressRangeDescriptor contains %P\n", p);
425 bool Commit(uintptr_t address,
size_t size,
PageType pageType,
int prot)
433 bool Decommit(uintptr_t address,
size_t size)
435 return VirtualFree(
LPVOID(address), size, MEM_DECOMMIT) != FALSE;
439 bool Protect(uintptr_t address,
size_t size,
int prot)
443 const BOOL ok = VirtualProtect(
LPVOID(address), size, protect, &oldProtect);
458 const BOOL ok = VirtualFree(p, 0, MEM_RELEASE);
471 const PEXCEPTION_RECORD er = ep->ExceptionRecord;
476 if(er->ExceptionCode != EXCEPTION_ACCESS_VIOLATION)
477 return EXCEPTION_CONTINUE_SEARCH;
485 const uintptr_t address = (uintptr_t)er->ExceptionInformation[1];
489 if(address == ~uintptr_t(0))
490 return EXCEPTION_CONTINUE_SEARCH;
496 return EXCEPTION_CONTINUE_SEARCH;
501 const uintptr_t alignedAddress =
round_down(address, d->alignment);
502 bool ok = d->Commit(alignedAddress);
505 debug_printf(L
"VectoredHandler: Commit(0x%p) failed; address=0x%p\n", alignedAddress, address);
507 return EXCEPTION_CONTINUE_SEARCH;
511 return EXCEPTION_CONTINUE_EXECUTION;
530 const ULONG ret = RemoveVectoredExceptionHandler(
handler);
static BOOL WINAPI EmulateGetNumaProcessorNode(UCHAR processor, PUCHAR node)
static const size_t pageSize
#define UNUSED(param)
mark a function parameter as unused and avoid the corresponding compiler warning. ...
static AddressRangeDescriptor * FindDescriptor(uintptr_t address)
void BeginOnDemandCommits()
install a handler that attempts to commit memory whenever a read/write page fault is encountered...
#define COMPILER_FENCE
prevent the compiler from reordering loads or stores across this point.
#define WUTIL_FUNC(varName, ret, params)
static const size_t os_cpu_MaxProcessors
maximum number of processors supported by the OS (determined by the number of bits in an affinity mas...
CACHE_ALIGNED(struct Statistics)
const wchar_t * ErrorString(int err)
static bool largePageAllocationTookTooLong
TIMER_ADD_CLIENT(tc_commit)
static Status InitHandler()
T round_up(T n, T multiple)
round number up/down to the next given multiple.
static DWORD WINAPI EmulateGetCurrentProcessorNumber(VOID)
#define ASSERT(expr)
same as ENSURE in debug mode, does nothing in release mode.
LIB_API double numa_Factor()
intptr_t cpu_AtomicAdd(volatile intptr_t *location, intptr_t increment)
add a signed value to a variable without the possibility of interference from other threads/CPUs...
size_t os_cpu_NumProcessors()
size_t os_cpu_LargePageSize()
size_t ProcessorFromApicId(ApicId apicId)
bool Commit(uintptr_t address, size_t size, PageType pageType, int prot)
map physical memory to previously reserved address space.
static bool ShouldUseLargePages(size_t allocationSize, DWORD allocationType, PageType pageType)
static LONG CALLBACK VectoredHandler(const PEXCEPTION_POINTERS ep)
static AddressRangeDescriptor ranges[2 *os_cpu_MaxProcessors]
bool IsAligned(T t, uintptr_t multiple)
#define TIMER_ACCRUE_ATOMIC(client)
static volatile intptr_t references
#define ENSURE(expr)
ensure the expression <expr> evaluates to non-zero.
intptr_t ModuleInitState
initialization state of a module (class, source file, etc.) must be initialized to zero (e...
#define WINIT_REGISTER_CRITICAL_INIT(func)
static ModuleInitState initState
size_t os_cpu_MemoryAvailable()
T round_down(T n, T multiple)
void EndOnDemandCommits()
decrements the reference count begun by BeginOnDemandCommit and removes the page fault handler when i...
bool Decommit(uintptr_t address, size_t size)
unmap physical memory.
bool Protect(uintptr_t address, size_t size, int prot)
set the memory protection flags for all pages that intersect the given interval.
i64 Status
Error handling system.
#define DEBUG_WARN_ERR(status)
display the error dialog with text corresponding to the given error code.
Status ModuleShutdown(volatile ModuleInitState *initState, void(*shutdown)())
calls a user-defined shutdown function if initState is "initialized".
void ReleaseAddressSpace(void *p, size_t size)
release address space and decommit any memory.
LIB_API size_t numa_NumNodes()
static void ShutdownHandler()
bool cpu_CAS(volatile intptr_t *location, intptr_t expected, intptr_t newValue)
atomic "compare and swap".
#define WARN_IF_FALSE(expression)
static const size_t largePageSize
unsigned MemoryProtectionFromPosix(int prot)
#define DEBUG_DISPLAY_ERROR(description)
void * Allocate(size_t size, PageType pageType, int prot)
reserve address space and commit memory.
static void * AllocateLargeOrSmallPages(uintptr_t address, size_t size, DWORD allocationType, PageType pageType=kDefault, int prot=PROT_READ|PROT_WRITE)
void * ReserveAddressSpace(size_t size, size_t commitSize, PageType pageType, int prot)
reserve address space and set the parameters for any later on-demand commits.
static LPVOID WINAPI EmulateVirtualAllocExNuma(HANDLE hProcess, LPVOID p, SIZE_T size, DWORD allocationType, DWORD protect, DWORD node)
static const size_t cacheLineSize
Status ModuleInit(volatile ModuleInitState *initState, Status(*init)())
calls a user-defined init function if initState is zero.
void Free(void *p, size_t size)
decommit memory and release address space.
const size_t WVERSION_VISTA
#define WUTIL_IMPORT_KERNEL32(procName, varName)
void debug_printf(const wchar_t *fmt,...)
write a formatted string to the debug channel, subject to filtering (see below).