From 89b7ffa8ed141c696ef8fccd0b007bdf420013ee Mon Sep 17 00:00:00 2001 From: daan Date: Sat, 13 Jul 2019 08:12:16 -0700 Subject: [PATCH 1/2] link DiscardVirtualMemory dynamically as it is not supported on every windows --- src/os.c | 29 ++++++++++++++--------------- 1 file changed, 14 insertions(+), 15 deletions(-) diff --git a/src/os.c b/src/os.c index 959f25ce..bb080cf2 100644 --- a/src/os.c +++ b/src/os.c @@ -82,8 +82,11 @@ static size_t mi_os_good_alloc_size(size_t size, size_t alignment) { #if defined(_WIN32) // We use VirtualAlloc2 for aligned allocation, but it is only supported on Windows 10 and Windows Server 2016. // So, we need to look it up dynamically to run on older systems. (use __stdcall for 32-bit compatibility) -typedef PVOID(__stdcall *VirtualAlloc2Ptr)(HANDLE, PVOID, SIZE_T, ULONG, ULONG, MEM_EXTENDED_PARAMETER*, ULONG); -static VirtualAlloc2Ptr pVirtualAlloc2 = NULL; +// Same for DiscardVirtualMemory +typedef PVOID(__stdcall *PVirtualAlloc2)(HANDLE, PVOID, SIZE_T, ULONG, ULONG, MEM_EXTENDED_PARAMETER*, ULONG); +typedef DWORD(__stdcall *PDiscardVirtualMemory)(PVOID,SIZE_T); +static PVirtualAlloc2 pVirtualAlloc2 = NULL; +static PDiscardVirtualMemory pDiscardVirtualMemory = NULL; void _mi_os_init(void) { // get the page size @@ -95,8 +98,10 @@ void _mi_os_init(void) { HINSTANCE hDll; hDll = LoadLibrary(TEXT("kernelbase.dll")); if (hDll != NULL) { - // use VirtualAlloc2FromApp as it is available to Windows store apps - pVirtualAlloc2 = (VirtualAlloc2Ptr)GetProcAddress(hDll, "VirtualAlloc2FromApp"); + // use VirtualAlloc2FromApp if possible as it is available to Windows store apps + pVirtualAlloc2 = (PVirtualAlloc2)GetProcAddress(hDll, "VirtualAlloc2FromApp"); + if (pVirtualAlloc2==NULL) pVirtualAlloc2 = (PVirtualAlloc2)GetProcAddress(hDll, "VirtualAlloc2"); + pDiscardVirtualMemory = (PDiscardVirtualMemory)GetProcAddress(hDll, "DiscardVirtualMemory"); FreeLibrary(hDll); } // Try to see if large OS pages are supported @@ -460,22 +465,16 @@ static bool mi_os_resetx(void* addr, size_t size, bool reset, mi_stats_t* stats) #if defined(_WIN32) // Testing shows that for us (on `malloc-large`) MEM_RESET is 2x faster than DiscardVirtualMemory // (but this is for an access pattern that immediately reuses the memory) - if (mi_option_is_enabled(mi_option_reset_discards)) { - DWORD ok = DiscardVirtualMemory(start, csize); - mi_assert_internal(ok == 0); - if (ok != 0) return false; + if (mi_option_is_enabled(mi_option_reset_discards) && pDiscardVirtualMemory != NULL) { + DWORD ok = (*pDiscardVirtualMemory)(start, csize); + mi_assert_internal(ok == ERROR_SUCCESS); + if (ok != ERROR_SUCCESS) return false; } else { void* p = VirtualAlloc(start, csize, MEM_RESET, PAGE_READWRITE); mi_assert_internal(p == start); if (p != start) return false; - } - /* - // VirtualUnlock removes the memory eagerly from the current working set (which MEM_RESET does lazily on demand) - // TODO: put this behind an option? - DWORD ok = VirtualUnlock(start, csize); - if (ok != 0) return false; - */ + } #else #if defined(MADV_FREE) static int advice = MADV_FREE; From e60a5db9085fd84f60e7f158684e834b111ca875 Mon Sep 17 00:00:00 2001 From: daan Date: Sun, 14 Jul 2019 12:52:58 -0700 Subject: [PATCH 2/2] fix cache eviction of smaller segments --- src/segment.c | 22 +++++++++++++++++----- test/test-stress.c | 3 ++- 2 files changed, 19 insertions(+), 6 deletions(-) diff --git a/src/segment.c b/src/segment.c index f766bf77..33175544 100644 --- a/src/segment.c +++ b/src/segment.c @@ -235,6 +235,11 @@ static void mi_segment_os_free(mi_segment_t* segment, size_t segment_size, mi_se #define MI_SEGMENT_CACHE_MAX (32) #define MI_SEGMENT_CACHE_FRACTION (8) +static void mi_segment_cache_remove(mi_segment_t* segment, mi_segments_tld_t* tld) { + tld->cache_count--; + tld->cache_size -= segment->segment_size; + mi_segment_queue_remove(&tld->cache, segment); +} // Get a segment of at least `required` size. // If `required == MI_SEGMENT_SIZE` the `segment_size` will match exactly @@ -242,10 +247,17 @@ static mi_segment_t* _mi_segment_cache_findx(mi_segments_tld_t* tld, size_t requ mi_assert_internal(required % _mi_os_page_size() == 0); mi_segment_t* segment = (reverse ? tld->cache.last : tld->cache.first); while (segment != NULL) { - if (segment->segment_size >= required) { - tld->cache_count--; - tld->cache_size -= segment->segment_size; - mi_segment_queue_remove(&tld->cache, segment); + mi_segment_t* next = (reverse ? segment->prev : segment->next); // remember in case we remove it from the cach + if (segment->segment_size < MI_SEGMENT_SIZE && segment->segment_size < required) { + // to prevent irregular sized smallish segments to stay in the cache forever, we purge them eagerly + mi_segment_cache_remove(segment,tld); + mi_segment_os_free(segment, segment->segment_size, tld); + // and look further... + } + else if (segment->segment_size >= required) { + // always remove it from the cache + mi_segment_cache_remove(segment, tld); + // exact size match? if (required==0 || segment->segment_size == required) { return segment; @@ -272,7 +284,7 @@ static mi_segment_t* _mi_segment_cache_findx(mi_segments_tld_t* tld, size_t requ } } } - segment = (reverse ? segment->prev : segment->next); + segment = next; } return NULL; } diff --git a/test/test-stress.c b/test/test-stress.c index 6afde306..55d8dd9c 100644 --- a/test/test-stress.c +++ b/test/test-stress.c @@ -116,7 +116,8 @@ int main() { for (int i = 0; i < TRANSFERS; i++) { free_items((void*)transfer[i]); } - mi_collect(false); + mi_collect(false); // ensures abandoned segments are reclaimed + mi_collect(true); // frees everything mi_stats_print(NULL); return 0; }