From 12d3297db53b19072c50ed89f635d56f0e732c1a Mon Sep 17 00:00:00 2001 From: daan Date: Wed, 10 Jul 2019 21:52:28 -0700 Subject: [PATCH] add allocator stress test to the test targets --- CMakeLists.txt | 21 +++--- test/test-stress.c | 166 +++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 179 insertions(+), 8 deletions(-) create mode 100644 test/test-stress.c diff --git a/CMakeLists.txt b/CMakeLists.txt index d6216384..c6f7abb6 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -172,15 +172,21 @@ install(FILES $ # ----------------------------------------------------------------------------- # API surface testing # ----------------------------------------------------------------------------- -add_executable(mimalloc-test test/test-api.c) -target_compile_definitions(mimalloc-test PRIVATE ${mi_defines}) -target_compile_options(mimalloc-test PRIVATE ${mi_cflags}) -target_include_directories(mimalloc-test PRIVATE include) -target_link_libraries(mimalloc-test PRIVATE mimalloc-static) +add_executable(mimalloc-test-api test/test-api.c) +target_compile_definitions(mimalloc-test-api PRIVATE ${mi_defines}) +target_compile_options(mimalloc-test-api PRIVATE ${mi_cflags}) +target_include_directories(mimalloc-test-api PRIVATE include) +target_link_libraries(mimalloc-test-api PRIVATE mimalloc-static) + +add_executable(mimalloc-test-stress test/test-stress.c) +target_compile_definitions(mimalloc-test-stress PRIVATE ${mi_defines}) +target_compile_options(mimalloc-test-stress PRIVATE ${mi_cflags}) +target_include_directories(mimalloc-test-stress PRIVATE include) +target_link_libraries(mimalloc-test-stress PRIVATE mimalloc-static) enable_testing() -add_test(test_api, mimalloc-test) - +add_test(test_api, mimalloc-test-api) +add_test(test_stress, mimalloc-test-stress) # ----------------------------------------------------------------------------- # Set override properties @@ -191,6 +197,5 @@ if (MI_OVERRIDE MATCHES "ON") # It is only possible to override malloc on Windows when building as a DLL. (src/alloc-override.c) target_compile_definitions(mimalloc-static PRIVATE MI_MALLOC_OVERRIDE) target_compile_definitions(mimalloc-obj PRIVATE MI_MALLOC_OVERRIDE) - target_compile_definitions(mimalloc-test PRIVATE MI_MALLOC_OVERRIDE) endif() endif() diff --git a/test/test-stress.c b/test/test-stress.c new file mode 100644 index 00000000..11a66a82 --- /dev/null +++ b/test/test-stress.c @@ -0,0 +1,166 @@ +/* ---------------------------------------------------------------------------- +Copyright (c) 2018,2019 Microsoft Research, Daan Leijen +This is free software; you can redistribute it and/or modify it under the +terms of the MIT license. A copy of the license can be found in the file +"LICENSE" at the root of this distribution. +-----------------------------------------------------------------------------*/ + +/* This is a stress test for the allocator, using multiple threads and + transferring objects between threads. This is not a typical workload + but uses a random size distribution. Do not use this test as a benchmark! + Note: pthreads uses mimalloc to allocate stacks and thus not all + memory is freed at the end. (usually the 320 byte chunks). +*/ + +#include +#include +#include +#include "mimalloc.h" +#include "mimalloc-internal.h" +#include "mimalloc-atomic.h" + +const size_t N = 10; // scaling factor +const size_t THREADS = 32; + +#define transfer_size (1000) +static volatile void* transfer[transfer_size]; + +#if (MI_INTPTR_SIZE==8) +const uintptr_t cookie = 0xbf58476d1ce4e5b9UL; +#else +const uintptr_t cookie = 0x1ce4e5b9UL; +#endif + + +static void* alloc_items(size_t items) { + if ((rand()%100) == 0) items *= 100; // 1% huge objects; + if (items==40) items++; // pthreads uses that size for stack increases + uintptr_t* p = mi_mallocn_tp(uintptr_t,items); + for (uintptr_t i = 0; i < items; i++) p[i] = (items - i) ^ cookie; + return p; +} + +static void free_items(void* p) { + if (p != NULL) { + uintptr_t* q = (uintptr_t*)p; + uintptr_t items = (q[0] ^ cookie); + for (uintptr_t i = 0; i < items; i++) { + if ((q[i]^cookie) != items - i) { + fprintf(stderr,"memory corruption at block %p at %zu\n", p, i); + abort(); + } + } + } + mi_free(p); +} + + +static void stress(intptr_t tid) { + const size_t max_item = 128; // in words + const size_t max_item_retained = 10*max_item; + size_t allocs = 80*N*(tid%8 + 1); // some threads do more + size_t retain = allocs/2; + void** data = NULL; + size_t data_size = 0; + size_t data_top = 0; + void** retained = mi_mallocn_tp(void*,retain); + size_t retain_top = 0; + + while (allocs>0 || retain>0) { + if (retain == 0 || ((rand()%4 == 0) && allocs > 0)) { + // 75% alloc + allocs--; + if (data_top >= data_size) { + data_size += 100000; + data = mi_reallocn_tp(data, void*, data_size); + } + data[data_top++] = alloc_items((rand() % max_item) + 1); + } + else { + // 25% retain + retained[retain_top++] = alloc_items( 10*((rand() % max_item_retained) + 1) ); + retain--; + } + if ((rand()%3)!=0 && data_top > 0) { + // 66% free previous alloc + size_t idx = rand() % data_top; + free_items(data[idx]); + data[idx]=NULL; + } + if ((tid%2)==0 && (rand()%4)==0 && data_top > 0) { + // 25% transfer-swap of half the threads + size_t data_idx = rand() % data_top; + size_t transfer_idx = rand() % transfer_size; + void* p = data[data_idx]; + void* q = mi_atomic_exchange_ptr(&transfer[transfer_idx],p); + data[data_idx] = q; + } + } + // free everything that is left + for (size_t i = 0; i < retain_top; i++) { + free_items(retained[i]); + } + for (size_t i = 0; i < data_top; i++) { + free_items(data[i]); + } + mi_free(retained); + mi_free(data); +} + +static void run_os_threads(); + +int main() { + srand(42); + memset(transfer,0,transfer_size*sizeof(void*)); + run_os_threads(); + for (int i = 0; i < transfer_size; i++) { + free_items((void*)transfer[i]); + } + mi_collect(false); + mi_stats_print(NULL); + return 0; +} + + +#ifdef _WIN32 + +#include + +static DWORD WINAPI thread_entry(LPVOID param) { + stress((intptr_t)param); + return 0; +} + +static void run_os_threads() { + DWORD tids[THREADS]; + HANDLE thandles[THREADS]; + for(intptr_t i = 0; i < THREADS; i++) { + thandles[i] = CreateThread(0,4096,&thread_entry,(void*)(i),0,&tids[i]); + } + for (int i = 0; i < THREADS; i++) { + WaitForSingleObject(thandles[i], INFINITE); + } +} + +#else + +#include + +static void* thread_entry( void* param ) { + stress((uintptr_t)param); + return NULL; +} + +static void run_os_threads() { + pthread_t threads[THREADS]; + memset(threads,0,sizeof(pthread_t)*THREADS); + //pthread_setconcurrency(THREADS); + for(uintptr_t i = 0; i < THREADS; i++) { + pthread_create(&threads[i], NULL, &thread_entry, (void*)i); + } + for (size_t i = 0; i < THREADS; i++) { + pthread_join(threads[i], NULL); + } +} + +#endif