diff options
author | Giovanni Campagna <gcampagn@cs.stanford.edu> | 2015-07-17 11:55:49 +0100 |
---|---|---|
committer | Simon Marlow <marlowsd@gmail.com> | 2015-07-22 17:50:02 +0100 |
commit | 0d1a8d09f452977aadef7897aa12a8d41c7a4af0 (patch) | |
tree | 3e8404c7f37c77b67ca913521e6890d6491f4721 /rts/win32 | |
parent | b949c96b4960168a3b399fe14485b24a2167b982 (diff) | |
download | haskell-0d1a8d09f452977aadef7897aa12a8d41c7a4af0.tar.gz |
Two step allocator for 64-bit systems
Summary:
The current OS memory allocator conflates the concepts of allocating
address space and allocating memory, which makes the HEAP_ALLOCED()
implementation excessively complicated (as the only thing it cares
about is address space layout) and slow. Instead, what we want
is to allocate a single insanely large contiguous block of address
space (to make HEAP_ALLOCED() checks fast), and then commit subportions
of that in 1MB blocks as we did before.
This is currently behind a flag, USE_LARGE_ADDRESS_SPACE, that is only enabled for
certain OSes.
Test Plan: validate
Reviewers: simonmar, ezyang, austin
Subscribers: thomie, carter
Differential Revision: https://phabricator.haskell.org/D524
GHC Trac Issues: #9706
Diffstat (limited to 'rts/win32')
-rw-r--r-- | rts/win32/OSMem.c | 77 |
1 files changed, 72 insertions, 5 deletions
diff --git a/rts/win32/OSMem.c b/rts/win32/OSMem.c index afa5113638..716171b3fc 100644 --- a/rts/win32/OSMem.c +++ b/rts/win32/OSMem.c @@ -8,6 +8,7 @@ #include "Rts.h" #include "sm/OSMem.h" +#include "sm/HeapAlloc.h" #include "RtsUtils.h" #if HAVE_WINDOWS_H @@ -28,7 +29,11 @@ typedef struct block_rec_ { /* allocs are kept in ascending order, and are the memory regions as returned by the OS as we need to have matching VirtualAlloc and - VirtualFree calls. */ + VirtualFree calls. + + If USE_LARGE_ADDRESS_SPACE is defined, this list will contain only + one element. +*/ static alloc_rec* allocs = NULL; /* free_blocks are kept in ascending order, and adjacent blocks are merged */ @@ -207,12 +212,9 @@ osGetMBlocks(nat n) { return ret; } -void osFreeMBlocks(char *addr, nat n) +static void decommitBlocks(char *addr, W_ nBytes) { alloc_rec *p; - W_ nBytes = (W_)n * MBLOCK_SIZE; - - insertFree(addr, nBytes); p = allocs; while ((p != NULL) && (addr >= (p->base + p->size))) { @@ -243,6 +245,14 @@ void osFreeMBlocks(char *addr, nat n) } } +void osFreeMBlocks(char *addr, nat n) +{ + W_ nBytes = (W_)n * MBLOCK_SIZE; + + insertFree(addr, nBytes); + decommitBlocks(addr, nBytes); +} + void osReleaseFreeMemory(void) { alloc_rec *prev_a, *a; @@ -414,3 +424,60 @@ void setExecutable (void *p, W_ len, rtsBool exec) stg_exit(EXIT_FAILURE); } } + +#ifdef USE_LARGE_ADDRESS_SPACE + +static void* heap_base = NULL; + +void *osReserveHeapMemory (void) +{ + void *start; + + heap_base = VirtualAlloc(NULL, MBLOCK_SPACE_SIZE + MBLOCK_SIZE, + MEM_RESERVE, PAGE_READWRITE); + if (heap_base == NULL) { + if (GetLastError() == ERROR_NOT_ENOUGH_MEMORY) { + errorBelch("out of memory"); + } else { + sysErrorBelch( + "osReserveHeapMemory: VirtualAlloc MEM_RESERVE %llu bytes failed", + MBLOCK_SPACE_SIZE + MBLOCK_SIZE); + } + stg_exit(EXIT_FAILURE); + } + + // VirtualFree MEM_RELEASE must always match a + // previous MEM_RESERVE call, in address and size + // so we necessarily leak some address space here, + // before and after the aligned area + // It is not a huge problem because we never commit + // that memory + start = MBLOCK_ROUND_UP(heap_base); + + return start; +} + +void osCommitMemory (void *at, W_ size) +{ + void *temp; + temp = VirtualAlloc(at, size, MEM_COMMIT, PAGE_READWRITE); + if (temp == NULL) { + sysErrorBelch("osCommitMemory: VirtualAlloc MEM_COMMIT failed"); + stg_exit(EXIT_FAILURE); + } +} + +void osDecommitMemory (void *at, W_ size) +{ + if (!VirtualFree(at, size, MEM_DECOMMIT)) { + sysErrorBelch("osDecommitMemory: VirtualFree MEM_DECOMMIT failed"); + stg_exit(EXIT_FAILURE); + } +} + +void osReleaseHeapMemory (void) +{ + VirtualFree(heap_base, 0, MEM_RELEASE); +} + +#endif |