@@ -33,7 +33,7 @@ The arena allocation needs to be thread safe and we use an atomic bitmap to allo
3333typedef struct mi_arena_s {
3434 mi_arena_id_t id ; // arena id; 0 for non-specific
3535 mi_memid_t memid ; // memid of the memory area
36- _Atomic (uint8_t * )start ; // the start of the memory area
36+ _Atomic (uint8_t * ) start ; // the start of the memory area
3737 size_t block_count ; // size of the area in arena blocks (of `MI_ARENA_BLOCK_SIZE`)
3838 size_t field_count ; // number of bitmap fields (where `field_count * MI_BITMAP_FIELD_BITS >= block_count`)
3939 size_t meta_size ; // size of the arena structure itself (including its bitmaps)
@@ -42,12 +42,13 @@ typedef struct mi_arena_s {
4242 bool exclusive ; // only allow allocations if specifically for this arena
4343 bool is_large ; // memory area consists of large- or huge OS pages (always committed)
4444 mi_lock_t abandoned_visit_lock ; // lock is only used when abandoned segments are being visited
45- _Atomic (size_t )search_idx ; // optimization to start the search for free blocks
46- _Atomic (mi_msecs_t )purge_expire ; // expiration time when blocks should be decommitted from `blocks_decommit`.
47- mi_bitmap_field_t * blocks_dirty ; // are the blocks potentially non-zero?
48- mi_bitmap_field_t * blocks_committed ; // are the blocks committed? (can be NULL for memory that cannot be decommitted)
49- mi_bitmap_field_t * blocks_purge ; // blocks that can be (reset) decommitted. (can be NULL for memory that cannot be (reset) decommitted)
50- mi_bitmap_field_t * blocks_abandoned ; // blocks that start with an abandoned segment. (This crosses API's but it is convenient to have here)
45+ _Atomic (size_t ) search_idx ; // optimization to start the search for free blocks
46+ _Atomic (mi_msecs_t ) purge_expire ; // expiration time when blocks should be purged from `blocks_purge`.
47+
48+ mi_bitmap_field_t * blocks_dirty ; // are the blocks potentially non-zero?
49+ mi_bitmap_field_t * blocks_committed ; // are the blocks committed? (can be NULL for memory that cannot be decommitted)
50+ mi_bitmap_field_t * blocks_purge ; // blocks that can be (reset) decommitted. (can be NULL for memory that cannot be (reset) decommitted)
51+ mi_bitmap_field_t * blocks_abandoned ; // blocks that start with an abandoned segment. (This crosses API's but it is convenient to have here)
5152 mi_bitmap_field_t blocks_inuse [1 ]; // in-place bitmap of in-use blocks (of size `field_count`)
5253 // do not add further fields here as the dirty, committed, purged, and abandoned bitmaps follow the inuse bitmap fields.
5354} mi_arena_t ;
@@ -60,6 +61,7 @@ typedef struct mi_arena_s {
6061// The available arenas
6162static mi_decl_cache_align _Atomic(mi_arena_t * ) mi_arenas [MI_MAX_ARENAS ];
6263static mi_decl_cache_align _Atomic(size_t ) mi_arena_count ; // = 0
64+ static mi_decl_cache_align _Atomic(int64_t ) mi_arenas_purge_expire ; // set if there exist purgeable arenas
6365
6466#define MI_IN_ARENA_C
6567#include "arena-abandon.c"
@@ -349,11 +351,10 @@ static mi_decl_noinline void* mi_arena_try_alloc(int numa_node, size_t size, siz
349351}
350352
351353// try to reserve a fresh arena space
352- static bool mi_arena_reserve (size_t req_size , bool allow_large , mi_arena_id_t req_arena_id , mi_arena_id_t * arena_id )
354+ static bool mi_arena_reserve (size_t req_size , bool allow_large , mi_arena_id_t * arena_id )
353355{
354356 if (_mi_preloading ()) return false; // use OS only while pre loading
355- if (req_arena_id != _mi_arena_id_none ()) return false;
356-
357+
357358 const size_t arena_count = mi_atomic_load_acquire (& mi_arena_count );
358359 if (arena_count > (MI_MAX_ARENAS - 4 )) return false;
359360
@@ -403,7 +404,7 @@ void* _mi_arena_alloc_aligned(size_t size, size_t alignment, size_t align_offset
403404 // otherwise, try to first eagerly reserve a new arena
404405 if (req_arena_id == _mi_arena_id_none ()) {
405406 mi_arena_id_t arena_id = 0 ;
406- if (mi_arena_reserve (size , allow_large , req_arena_id , & arena_id )) {
407+ if (mi_arena_reserve (size , allow_large , & arena_id )) {
407408 // and try allocate in there
408409 mi_assert_internal (req_arena_id == _mi_arena_id_none ());
409410 p = mi_arena_try_alloc_at_id (arena_id , true, numa_node , size , alignment , commit , allow_large , req_arena_id , memid );
@@ -497,13 +498,16 @@ static void mi_arena_schedule_purge(mi_arena_t* arena, size_t bitmap_idx, size_t
497498 mi_arena_purge (arena , bitmap_idx , blocks );
498499 }
499500 else {
500- // schedule decommit
501- mi_msecs_t expire = mi_atomic_loadi64_relaxed (& arena -> purge_expire );
502- if (expire != 0 ) {
503- mi_atomic_addi64_acq_rel (& arena -> purge_expire , (mi_msecs_t )(delay /10 )); // add smallish extra delay
501+ // schedule purge
502+ const mi_msecs_t expire = _mi_clock_now () + delay ;
503+ mi_msecs_t expire0 = 0 ;
504+ if (mi_atomic_casi64_strong_acq_rel (& arena -> purge_expire , & expire0 , expire )) {
505+ // expiration was not yet set
506+ // maybe set the global arenas expire as well (if it wasn't set already)
507+ mi_atomic_casi64_strong_acq_rel (& mi_arenas_purge_expire , & expire0 , expire );
504508 }
505509 else {
506- mi_atomic_storei64_release ( & arena -> purge_expire , _mi_clock_now () + delay );
510+ // already an expiration was set
507511 }
508512 _mi_bitmap_claim_across (arena -> blocks_purge , arena -> field_count , blocks , bitmap_idx , NULL );
509513 }
@@ -538,14 +542,16 @@ static bool mi_arena_purge_range(mi_arena_t* arena, size_t idx, size_t startidx,
538542// returns true if anything was purged
539543static bool mi_arena_try_purge (mi_arena_t * arena , mi_msecs_t now , bool force )
540544{
541- if (arena -> memid .is_pinned || arena -> blocks_purge == NULL ) return false;
545+ // check pre-conditions
546+ if (arena -> memid .is_pinned ) return false;
547+
548+ // expired yet?
542549 mi_msecs_t expire = mi_atomic_loadi64_relaxed (& arena -> purge_expire );
543- if (expire == 0 ) return false;
544- if (!force && expire > now ) return false;
550+ if (!force && (expire == 0 || expire > now )) return false;
545551
546552 // reset expire (if not already set concurrently)
547553 mi_atomic_casi64_strong_acq_rel (& arena -> purge_expire , & expire , (mi_msecs_t )0 );
548-
554+
549555 // potential purges scheduled, walk through the bitmap
550556 bool any_purged = false;
551557 bool full_purge = true;
@@ -592,27 +598,42 @@ static bool mi_arena_try_purge(mi_arena_t* arena, mi_msecs_t now, bool force)
592598 return any_purged ;
593599}
594600
595- static void mi_arenas_try_purge ( bool force , bool visit_all ) {
601+ static void mi_arenas_try_purge ( bool force , bool visit_all )
602+ {
596603 if (_mi_preloading () || mi_arena_purge_delay () <= 0 ) return ; // nothing will be scheduled
597604
605+ // check if any arena needs purging?
606+ const mi_msecs_t now = _mi_clock_now ();
607+ mi_msecs_t arenas_expire = mi_atomic_load_acquire (& mi_arenas_purge_expire );
608+ if (!force && (arenas_expire == 0 || arenas_expire < now )) return ;
609+
598610 const size_t max_arena = mi_atomic_load_acquire (& mi_arena_count );
599611 if (max_arena == 0 ) return ;
600612
601613 // allow only one thread to purge at a time
602614 static mi_atomic_guard_t purge_guard ;
603615 mi_atomic_guard (& purge_guard )
604616 {
605- mi_msecs_t now = _mi_clock_now ();
606- size_t max_purge_count = (visit_all ? max_arena : 1 );
617+ // increase global expire: at most one purge per delay cycle
618+ mi_atomic_store_release (& mi_arenas_purge_expire , now + mi_arena_purge_delay ());
619+ size_t max_purge_count = (visit_all ? max_arena : 2 );
620+ bool all_visited = true;
607621 for (size_t i = 0 ; i < max_arena ; i ++ ) {
608622 mi_arena_t * arena = mi_atomic_load_ptr_acquire (mi_arena_t , & mi_arenas [i ]);
609623 if (arena != NULL ) {
610624 if (mi_arena_try_purge (arena , now , force )) {
611- if (max_purge_count <= 1 ) break ;
625+ if (max_purge_count <= 1 ) {
626+ all_visited = false;
627+ break ;
628+ }
612629 max_purge_count -- ;
613630 }
614631 }
615632 }
633+ if (all_visited ) {
634+ // all arena's were visited and purged: reset global expire
635+ mi_atomic_store_release (& mi_arenas_purge_expire , 0 );
636+ }
616637 }
617638}
618639
0 commit comments