mirror of
https://github.com/microsoft/mimalloc.git
synced 2025-07-06 19:38:41 +03:00
merge from dev-exp; better abandoned reclamation
This commit is contained in:
commit
b50bec463d
25 changed files with 661 additions and 337 deletions
|
@ -32,16 +32,18 @@ static int ITER = 50; // N full iterations destructing and re-creating a
|
|||
// static int THREADS = 8; // more repeatable if THREADS <= #processors
|
||||
// static int SCALE = 100; // scaling factor
|
||||
|
||||
#define STRESS // undefine for leak test
|
||||
|
||||
static bool allow_large_objects = true; // allow very large objects?
|
||||
static size_t use_one_size = 0; // use single object size of `N * sizeof(uintptr_t)`?
|
||||
|
||||
|
||||
#ifdef USE_STD_MALLOC
|
||||
#define custom_malloc(s) malloc(s)
|
||||
#define custom_calloc(n,s) calloc(n,s)
|
||||
#define custom_realloc(p,s) realloc(p,s)
|
||||
#define custom_free(p) free(p)
|
||||
#else
|
||||
#define custom_malloc(s) mi_malloc(s)
|
||||
#define custom_calloc(n,s) mi_calloc(n,s)
|
||||
#define custom_realloc(p,s) mi_realloc(p,s)
|
||||
#define custom_free(p) mi_free(p)
|
||||
#endif
|
||||
|
@ -94,9 +96,12 @@ static void* alloc_items(size_t items, random_t r) {
|
|||
}
|
||||
if (items == 40) items++; // pthreads uses that size for stack increases
|
||||
if (use_one_size > 0) items = (use_one_size / sizeof(uintptr_t));
|
||||
uintptr_t* p = (uintptr_t*)custom_malloc(items * sizeof(uintptr_t));
|
||||
if (items==0) items = 1;
|
||||
uintptr_t* p = (uintptr_t*)custom_calloc(items,sizeof(uintptr_t));
|
||||
if (p != NULL) {
|
||||
for (uintptr_t i = 0; i < items; i++) p[i] = (items - i) ^ cookie;
|
||||
for (uintptr_t i = 0; i < items; i++) {
|
||||
p[i] = (items - i) ^ cookie;
|
||||
}
|
||||
}
|
||||
return p;
|
||||
}
|
||||
|
@ -118,7 +123,7 @@ static void free_items(void* p) {
|
|||
|
||||
static void stress(intptr_t tid) {
|
||||
//bench_start_thread();
|
||||
uintptr_t r = tid * 43;
|
||||
uintptr_t r = (tid * 43); // rand();
|
||||
const size_t max_item_shift = 5; // 128
|
||||
const size_t max_item_retained_shift = max_item_shift + 2;
|
||||
size_t allocs = 100 * ((size_t)SCALE) * (tid % 8 + 1); // some threads do more
|
||||
|
@ -126,7 +131,7 @@ static void stress(intptr_t tid) {
|
|||
void** data = NULL;
|
||||
size_t data_size = 0;
|
||||
size_t data_top = 0;
|
||||
void** retained = (void**)custom_malloc(retain * sizeof(void*));
|
||||
void** retained = (void**)custom_calloc(retain,sizeof(void*));
|
||||
size_t retain_top = 0;
|
||||
|
||||
while (allocs > 0 || retain > 0) {
|
||||
|
@ -171,7 +176,46 @@ static void stress(intptr_t tid) {
|
|||
//bench_end_thread();
|
||||
}
|
||||
|
||||
static void run_os_threads(size_t nthreads);
|
||||
static void run_os_threads(size_t nthreads, void (*entry)(intptr_t tid));
|
||||
|
||||
static void test_stress(void) {
|
||||
uintptr_t r = rand();
|
||||
for (int n = 0; n < ITER; n++) {
|
||||
run_os_threads(THREADS, &stress);
|
||||
for (int i = 0; i < TRANSFERS; i++) {
|
||||
if (chance(50, &r) || n + 1 == ITER) { // free all on last run, otherwise free half of the transfers
|
||||
void* p = atomic_exchange_ptr(&transfer[i], NULL);
|
||||
free_items(p);
|
||||
}
|
||||
}
|
||||
mi_collect(false);
|
||||
#ifndef NDEBUG
|
||||
if ((n + 1) % 10 == 0) { printf("- iterations left: %3d\n", ITER - (n + 1)); }
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
#ifndef STRESS
|
||||
static void leak(intptr_t tid) {
|
||||
uintptr_t r = rand();
|
||||
void* p = alloc_items(1 /*pick(&r)%128*/, &r);
|
||||
if (chance(50, &r)) {
|
||||
intptr_t i = (pick(&r) % TRANSFERS);
|
||||
void* q = atomic_exchange_ptr(&transfer[i], p);
|
||||
free_items(q);
|
||||
}
|
||||
}
|
||||
|
||||
static void test_leak(void) {
|
||||
for (int n = 0; n < ITER; n++) {
|
||||
run_os_threads(THREADS, &leak);
|
||||
mi_collect(false);
|
||||
#ifndef NDEBUG
|
||||
if ((n + 1) % 10 == 0) { printf("- iterations left: %3d\n", ITER - (n + 1)); }
|
||||
#endif
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
// > mimalloc-test-stress [THREADS] [SCALE] [ITER]
|
||||
|
@ -197,21 +241,13 @@ int main(int argc, char** argv) {
|
|||
//bench_start_program();
|
||||
|
||||
// Run ITER full iterations where half the objects in the transfer buffer survive to the next round.
|
||||
srand(0x7feb352d);
|
||||
mi_stats_reset();
|
||||
uintptr_t r = 43 * 43;
|
||||
for (int n = 0; n < ITER; n++) {
|
||||
run_os_threads(THREADS);
|
||||
for (int i = 0; i < TRANSFERS; i++) {
|
||||
if (chance(50, &r) || n + 1 == ITER) { // free all on last run, otherwise free half of the transfers
|
||||
void* p = atomic_exchange_ptr(&transfer[i], NULL);
|
||||
free_items(p);
|
||||
}
|
||||
}
|
||||
mi_collect(false);
|
||||
#ifndef NDEBUG
|
||||
if ((n + 1) % 10 == 0) { printf("- iterations left: %3d\n", ITER - (n + 1)); }
|
||||
#endif
|
||||
}
|
||||
#ifdef STRESS
|
||||
test_stress();
|
||||
#else
|
||||
test_leak();
|
||||
#endif
|
||||
|
||||
mi_collect(true);
|
||||
mi_stats_print(NULL);
|
||||
|
@ -220,18 +256,21 @@ int main(int argc, char** argv) {
|
|||
}
|
||||
|
||||
|
||||
static void (*thread_entry_fun)(intptr_t) = &stress;
|
||||
|
||||
#ifdef _WIN32
|
||||
|
||||
#include <windows.h>
|
||||
|
||||
static DWORD WINAPI thread_entry(LPVOID param) {
|
||||
stress((intptr_t)param);
|
||||
static DWORD WINAPI thread_entry(LPVOID param) {
|
||||
thread_entry_fun((intptr_t)param);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void run_os_threads(size_t nthreads) {
|
||||
DWORD* tids = (DWORD*)custom_malloc(nthreads * sizeof(DWORD));
|
||||
HANDLE* thandles = (HANDLE*)custom_malloc(nthreads * sizeof(HANDLE));
|
||||
static void run_os_threads(size_t nthreads, void (*fun)(intptr_t)) {
|
||||
thread_entry_fun = fun;
|
||||
DWORD* tids = (DWORD*)custom_calloc(nthreads,sizeof(DWORD));
|
||||
HANDLE* thandles = (HANDLE*)custom_calloc(nthreads,sizeof(HANDLE));
|
||||
for (uintptr_t i = 0; i < nthreads; i++) {
|
||||
thandles[i] = CreateThread(0, 4096, &thread_entry, (void*)(i), 0, &tids[i]);
|
||||
}
|
||||
|
@ -246,7 +285,7 @@ static void run_os_threads(size_t nthreads) {
|
|||
}
|
||||
|
||||
static void* atomic_exchange_ptr(volatile void** p, void* newval) {
|
||||
#if (INTPTR_MAX == UINT32_MAX)
|
||||
#if (INTPTR_MAX == INT32_MAX)
|
||||
return (void*)InterlockedExchange((volatile LONG*)p, (LONG)newval);
|
||||
#else
|
||||
return (void*)InterlockedExchange64((volatile LONG64*)p, (LONG64)newval);
|
||||
|
@ -257,12 +296,13 @@ static void* atomic_exchange_ptr(volatile void** p, void* newval) {
|
|||
#include <pthread.h>
|
||||
|
||||
static void* thread_entry(void* param) {
|
||||
stress((uintptr_t)param);
|
||||
thread_entry_fun((uintptr_t)param);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void run_os_threads(size_t nthreads) {
|
||||
pthread_t* threads = (pthread_t*)custom_malloc(nthreads * sizeof(pthread_t));
|
||||
static void run_os_threads(size_t nthreads, void (*fun)(intptr_t)) {
|
||||
thread_entry_fun = fun;
|
||||
pthread_t* threads = (pthread_t*)custom_calloc(nthreads,sizeof(pthread_t));
|
||||
memset(threads, 0, sizeof(pthread_t) * nthreads);
|
||||
//pthread_setconcurrency(nthreads);
|
||||
for (uintptr_t i = 0; i < nthreads; i++) {
|
||||
|
@ -277,12 +317,12 @@ static void run_os_threads(size_t nthreads) {
|
|||
#ifdef __cplusplus
|
||||
#include <atomic>
|
||||
static void* atomic_exchange_ptr(volatile void** p, void* newval) {
|
||||
return std::atomic_exchange_explicit((volatile std::atomic<void*>*)p, newval, std::memory_order_acquire);
|
||||
return std::atomic_exchange((volatile std::atomic<void*>*)p, newval);
|
||||
}
|
||||
#else
|
||||
#include <stdatomic.h>
|
||||
static void* atomic_exchange_ptr(volatile void** p, void* newval) {
|
||||
return atomic_exchange_explicit((volatile _Atomic(void*)*)p, newval, memory_order_acquire);
|
||||
return atomic_exchange((volatile _Atomic(void*)*)p, newval);
|
||||
}
|
||||
#endif
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue