make reclaim tries a percentage of the abandoned count

This commit is contained in:
daanx 2024-03-01 14:57:46 -08:00
parent cf8f73098e
commit c4f1f2e079
4 changed files with 16 additions and 5 deletions

View file

@ -126,6 +126,7 @@ void _mi_arena_unsafe_destroy_all(mi_stats_t* stats);
bool _mi_arena_segment_clear_abandoned(mi_memid_t memid); bool _mi_arena_segment_clear_abandoned(mi_memid_t memid);
void _mi_arena_segment_mark_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 typedef struct mi_arena_field_cursor_s { // abstract
mi_arena_id_t start; mi_arena_id_t start;

View file

@ -733,14 +733,18 @@ bool _mi_arena_contains(const void* p) {
This is used to atomically abandon/reclaim segments This is used to atomically abandon/reclaim segments
(and crosses the arena API but it is convenient to have here). (and crosses the arena API but it is convenient to have here).
Abandoned segments still have live blocks; they get reclaimed 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 segment; these threads scan the abandoned segments through
the arena bitmaps. 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; 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. // reclaim a specific abandoned segment; `true` on success.
bool _mi_arena_segment_clear_abandoned(mi_memid_t memid ) 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 // consequetive bitmaps
arena->blocks_dirty = &arena->blocks_inuse[fields]; // just after inuse bitmap 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_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 arena->blocks_purge = (arena->memid.is_pinned ? NULL : &arena->blocks_inuse[4*fields]); // just after committed bitmap
// initialize committed bitmap? // initialize committed bitmap?
if (arena->blocks_committed != NULL && arena->memid.initially_committed) { if (arena->blocks_committed != NULL && arena->memid.initially_committed) {

View file

@ -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 { 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_errors) }, // maximum errors that are output
{ 16, UNINIT, MI_OPTION(max_warnings) }, // maximum warnings 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! { 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) #if (MI_INTPTR_SIZE>4)
{ 1024L * 1024L, UNINIT, MI_OPTION(arena_reserve) }, // reserve memory N KiB at a time { 1024L * 1024L, UNINIT, MI_OPTION(arena_reserve) }, // reserve memory N KiB at a time

View file

@ -921,7 +921,13 @@ static mi_segment_t* mi_segment_try_reclaim(mi_heap_t* heap, size_t block_size,
*reclaimed = false; *reclaimed = false;
mi_segment_t* segment; mi_segment_t* segment;
mi_arena_field_cursor_t current; _mi_arena_field_cursor_init(heap,&current); mi_arena_field_cursor_t current; _mi_arena_field_cursor_init(heap,&current);
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(&current)) != NULL)) while ((max_tries-- > 0) && ((segment = _mi_arena_segment_clear_abandoned_next(&current)) != NULL))
{ {
segment->abandoned_visits++; segment->abandoned_visits++;