summaryrefslogtreecommitdiff
path: root/rts/win32
diff options
context:
space:
mode:
authorGiovanni Campagna <gcampagn@cs.stanford.edu>2015-07-17 11:55:49 +0100
committerSimon Marlow <marlowsd@gmail.com>2015-07-22 17:50:02 +0100
commit0d1a8d09f452977aadef7897aa12a8d41c7a4af0 (patch)
tree3e8404c7f37c77b67ca913521e6890d6491f4721 /rts/win32
parentb949c96b4960168a3b399fe14485b24a2167b982 (diff)
downloadhaskell-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.c77
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