diff --git a/include/mimalloc/internal.h b/include/mimalloc/internal.h index 0976fc4e..1168d944 100644 --- a/include/mimalloc/internal.h +++ b/include/mimalloc/internal.h @@ -126,6 +126,7 @@ void _mi_arena_unsafe_destroy_all(mi_stats_t* stats); bool _mi_arena_segment_clear_abandoned(mi_memid_t memid); void _mi_arena_segment_mark_abandoned(mi_memid_t memid); +size_t _mi_arena_segment_abandoned_count(void); typedef struct mi_arena_field_cursor_s { // abstract mi_arena_id_t start; diff --git a/src/arena.c b/src/arena.c index a73c5f52..af68c3bd 100644 --- a/src/arena.c +++ b/src/arena.c @@ -733,14 +733,18 @@ bool _mi_arena_contains(const void* p) { This is used to atomically abandon/reclaim segments (and crosses the arena API but it is convenient to have here). Abandoned segments still have live blocks; they get reclaimed - when a thread frees in it, or when a thread needs a fresh + when a thread frees a block in it, or when a thread needs a fresh segment; these threads scan the abandoned segments through the arena bitmaps. ----------------------------------------------------------- */ -// Maintain these for debug purposes +// Maintain a count of all abandoned segments static mi_decl_cache_align _Atomic(size_t)abandoned_count; +size_t _mi_arena_segment_abandoned_count(void) { + return mi_atomic_load_relaxed(&abandoned_count); +} + // reclaim a specific abandoned segment; `true` on success. bool _mi_arena_segment_clear_abandoned(mi_memid_t memid ) { @@ -885,7 +889,7 @@ static bool mi_manage_os_memory_ex2(void* start, size_t size, bool is_large, int // consequetive bitmaps arena->blocks_dirty = &arena->blocks_inuse[fields]; // just after inuse bitmap arena->blocks_abandoned = &arena->blocks_inuse[2 * fields]; // just after dirty bitmap - arena->blocks_committed = (arena->memid.is_pinned ? NULL : &arena->blocks_inuse[3*fields]); // just after abandonde bitmap + arena->blocks_committed = (arena->memid.is_pinned ? NULL : &arena->blocks_inuse[3*fields]); // just after abandoned bitmap arena->blocks_purge = (arena->memid.is_pinned ? NULL : &arena->blocks_inuse[4*fields]); // just after committed bitmap // initialize committed bitmap? if (arena->blocks_committed != NULL && arena->memid.initially_committed) { diff --git a/src/options.c b/src/options.c index 61ed5be7..f8e928d0 100644 --- a/src/options.c +++ b/src/options.c @@ -81,7 +81,7 @@ static mi_option_desc_t options[_mi_option_last] = { 100, UNINIT, MI_OPTION(os_tag) }, // only apple specific for now but might serve more or less related purpose { 16, UNINIT, MI_OPTION(max_errors) }, // maximum errors that are output { 16, UNINIT, MI_OPTION(max_warnings) }, // maximum warnings that are output - { 16, UNINIT, MI_OPTION(max_segment_reclaim)}, // max. number of segment reclaims from the abandoned segments per try. + { 10, UNINIT, MI_OPTION(max_segment_reclaim)}, // max. percentage of the abandoned segments per try. { 0, UNINIT, MI_OPTION(destroy_on_exit)}, // release all OS memory on process exit; careful with dangling pointer or after-exit frees! #if (MI_INTPTR_SIZE>4) { 1024L * 1024L, UNINIT, MI_OPTION(arena_reserve) }, // reserve memory N KiB at a time diff --git a/src/segment.c b/src/segment.c index f7a43abf..cc5c15fa 100644 --- a/src/segment.c +++ b/src/segment.c @@ -921,7 +921,13 @@ static mi_segment_t* mi_segment_try_reclaim(mi_heap_t* heap, size_t block_size, *reclaimed = false; mi_segment_t* segment; mi_arena_field_cursor_t current; _mi_arena_field_cursor_init(heap,¤t); - long max_tries = mi_option_get_clamp(mi_option_max_segment_reclaim, 0, 1024); // limit the work to bound allocation times + + // limit the tries to 10% (default) of the abandoned segments with at least 8 tries, and at most 1024. + const size_t perc = (size_t)mi_option_get_clamp(mi_option_max_segment_reclaim, 0, 100); + if (perc <= 0) return NULL; + const size_t abandoned_count = _mi_arena_segment_abandoned_count(); + const size_t relative_count = (abandoned_count > 10000 ? (abandoned_count / 100) * perc : (abandoned_count * perc) / 100); // avoid overflow + long max_tries = (long)(relative_count < 8 ? 8 : (relative_count > 1024 ? 1024 : relative_count)); while ((max_tries-- > 0) && ((segment = _mi_arena_segment_clear_abandoned_next(¤t)) != NULL)) { segment->abandoned_visits++;