collect every N generic calls

This commit is contained in:
Daan Leijen 2025-03-02 17:06:25 -08:00
parent 4f1edad4fe
commit 58d13f6a4f
6 changed files with 27 additions and 7 deletions

View file

@ -100,4 +100,4 @@ mi_decl_export char* mi_stats_get_json( size_t buf_size, char* buf ) mi_attr_noe
} }
#endif #endif
#endif // MIMALLOC_STATS_H #endif // MIMALLOC_STATS_H

View file

@ -310,7 +310,7 @@ mi_decl_export void mi_heap_guarded_set_sample_rate(mi_heap_t* heap, size_t samp
mi_decl_export void mi_heap_guarded_set_size_bound(mi_heap_t* heap, size_t min, size_t max); mi_decl_export void mi_heap_guarded_set_size_bound(mi_heap_t* heap, size_t min, size_t max);
// Experimental: communicate that the thread is part of a threadpool // Experimental: communicate that the thread is part of a threadpool
mi_decl_export void mi_thread_set_in_threadpool(void) mi_attr_noexcept; mi_decl_export void mi_thread_set_in_threadpool(void) mi_attr_noexcept;
// Experimental: create a new heap with a specified heap tag. Set `allow_destroy` to false to allow the thread // Experimental: create a new heap with a specified heap tag. Set `allow_destroy` to false to allow the thread
// to reclaim abandoned memory (with a compatible heap_tag and arena_id) but in that case `mi_heap_destroy` will // to reclaim abandoned memory (with a compatible heap_tag and arena_id) but in that case `mi_heap_destroy` will
@ -385,6 +385,7 @@ typedef enum mi_option_e {
mi_option_guarded_sample_rate, // 1 out of N allocations in the min/max range will be guarded (=1000) mi_option_guarded_sample_rate, // 1 out of N allocations in the min/max range will be guarded (=1000)
mi_option_guarded_sample_seed, // can be set to allow for a (more) deterministic re-execution when a guard page is triggered (=0) mi_option_guarded_sample_seed, // can be set to allow for a (more) deterministic re-execution when a guard page is triggered (=0)
mi_option_target_segments_per_thread, // experimental (=0) mi_option_target_segments_per_thread, // experimental (=0)
mi_option_generic_collect, // collect heaps every N (=10000) generic allocation calls
_mi_option_last, _mi_option_last,
// legacy option names // legacy option names
mi_option_large_os_pages = mi_option_allow_large_os_pages, mi_option_large_os_pages = mi_option_allow_large_os_pages,

View file

@ -202,7 +202,9 @@ typedef int32_t mi_ssize_t;
#define MI_LARGE_OBJ_WSIZE_MAX (MI_LARGE_OBJ_SIZE_MAX/MI_INTPTR_SIZE) #define MI_LARGE_OBJ_WSIZE_MAX (MI_LARGE_OBJ_SIZE_MAX/MI_INTPTR_SIZE)
// Maximum number of size classes. (spaced exponentially in 12.5% increments) // Maximum number of size classes. (spaced exponentially in 12.5% increments)
#define MI_BIN_HUGE (73U) #if MI_BIN_HUGE != 73U
#error "mimalloc internal: expecting 73 bins"
#endif
#if (MI_LARGE_OBJ_WSIZE_MAX >= 655360) #if (MI_LARGE_OBJ_WSIZE_MAX >= 655360)
#error "mimalloc internal: define more bins" #error "mimalloc internal: define more bins"
@ -501,6 +503,8 @@ struct mi_heap_s {
size_t page_count; // total number of pages in the `pages` queues. size_t page_count; // total number of pages in the `pages` queues.
size_t page_retired_min; // smallest retired index (retired pages are fully free, but still in the page queues) size_t page_retired_min; // smallest retired index (retired pages are fully free, but still in the page queues)
size_t page_retired_max; // largest retired index into the `pages` array. size_t page_retired_max; // largest retired index into the `pages` array.
long generic_count; // how often is `_mi_malloc_generic` called?
long generic_collect_count; // how often is `_mi_malloc_generic` called without collecting?
mi_heap_t* next; // list of heaps per thread mi_heap_t* next; // list of heaps per thread
bool no_reclaim; // `true` if this heap should not reclaim abandoned pages bool no_reclaim; // `true` if this heap should not reclaim abandoned pages
uint8_t tag; // custom tag, can be used for separating heaps based on the object types uint8_t tag; // custom tag, can be used for separating heaps based on the object types

View file

@ -105,6 +105,7 @@ mi_decl_hidden mi_decl_cache_align const mi_heap_t _mi_heap_empty = {
{ {0}, {0}, 0, true }, // random { {0}, {0}, 0, true }, // random
0, // page count 0, // page count
MI_BIN_FULL, 0, // page retired min/max MI_BIN_FULL, 0, // page retired min/max
0, 0, // generic count
NULL, // next NULL, // next
false, // can reclaim false, // can reclaim
0, // tag 0, // tag
@ -147,6 +148,7 @@ mi_decl_cache_align mi_heap_t _mi_heap_main = {
{ {0x846ca68b}, {0}, 0, true }, // random { {0x846ca68b}, {0}, 0, true }, // random
0, // page count 0, // page count
MI_BIN_FULL, 0, // page retired min/max MI_BIN_FULL, 0, // page retired min/max
0, 0, // generic count
NULL, // next heap NULL, // next heap
false, // can reclaim false, // can reclaim
0, // tag 0, // tag

View file

@ -162,6 +162,7 @@ static mi_option_desc_t options[_mi_option_last] =
UNINIT, MI_OPTION(guarded_sample_rate)}, // 1 out of N allocations in the min/max range will be guarded (=4000) UNINIT, MI_OPTION(guarded_sample_rate)}, // 1 out of N allocations in the min/max range will be guarded (=4000)
{ 0, UNINIT, MI_OPTION(guarded_sample_seed)}, { 0, UNINIT, MI_OPTION(guarded_sample_seed)},
{ 0, UNINIT, MI_OPTION(target_segments_per_thread) }, // abandon segments beyond this point, or 0 to disable. { 0, UNINIT, MI_OPTION(target_segments_per_thread) }, // abandon segments beyond this point, or 0 to disable.
{ 10000, UNINIT, MI_OPTION(generic_collect) }, // collect heaps every N (=10000) generic allocation calls
}; };
static void mi_option_init(mi_option_desc_t* desc); static void mi_option_init(mi_option_desc_t* desc);

View file

@ -970,11 +970,23 @@ void* _mi_malloc_generic(mi_heap_t* heap, size_t size, bool zero, size_t huge_al
} }
mi_assert_internal(mi_heap_is_initialized(heap)); mi_assert_internal(mi_heap_is_initialized(heap));
// call potential deferred free routines // do administrative tasks every N generic mallocs
_mi_deferred_free(heap, false); if mi_unlikely(++heap->generic_count >= 100) {
heap->generic_collect_count += heap->generic_count;
heap->generic_count = 0;
// call potential deferred free routines
_mi_deferred_free(heap, false);
// free delayed frees from other threads (but skip contended ones) // free delayed frees from other threads (but skip contended ones)
_mi_heap_delayed_free_partial(heap); _mi_heap_delayed_free_partial(heap);
// collect every once in a while (10000 by default)
const long generic_collect = mi_option_get_clamp(mi_option_generic_collect, 1, 1000000L);
if (heap->generic_collect_count >= generic_collect) {
heap->generic_collect_count = 0;
mi_heap_collect(heap, false /* force? */);
}
}
// find (or allocate) a page of the right size // find (or allocate) a page of the right size
mi_page_t* page = mi_find_page(heap, size, huge_alignment); mi_page_t* page = mi_find_page(heap, size, huge_alignment);