From d09b80bf98b66766f855f7140bf872578ab1851b Mon Sep 17 00:00:00 2001 From: Diogo Netto <61364108+d-netto@users.noreply.github.com> Date: Thu, 16 Nov 2023 16:15:03 -0300 Subject: [PATCH] add some instrumentation to measure page utilization per size class (#52164) One of the limitations is that it's only accurate right after the GC. Still might be helpful for observability purposes. --- src/gc.c | 34 ++++++++++++++++++++++++++++++++++ src/gc.h | 8 ++++++++ 2 files changed, 42 insertions(+) diff --git a/src/gc.c b/src/gc.c index d6ce9d5e313bd2..bbe05af6c37a97 100644 --- a/src/gc.c +++ b/src/gc.c @@ -1397,6 +1397,38 @@ int jl_gc_classify_pools(size_t sz, int *osize) // sweep phase +gc_fragmentation_stat_t gc_page_fragmentation_stats[JL_GC_N_POOLS]; + +extern gc_fragmentation_stat_t gc_page_fragmentation_stats[JL_GC_N_POOLS]; + +STATIC_INLINE void gc_update_page_fragmentation_data(jl_gc_pagemeta_t *pg) JL_NOTSAFEPOINT +{ +#ifdef GC_MEASURE_PAGE_FRAGMENTATION + gc_fragmentation_stat_t *stats = &gc_page_fragmentation_stats[pg->pool_n]; + jl_atomic_fetch_add(&stats->n_freed_objs, pg->nfree); + jl_atomic_fetch_add(&stats->n_pages_allocd, 1); +#endif +} + +STATIC_INLINE void gc_dump_page_utilization_data(void) JL_NOTSAFEPOINT +{ +#ifdef GC_MEASURE_PAGE_FRAGMENTATION + for (int i = 0; i < JL_GC_N_POOLS; i++) { + gc_fragmentation_stat_t *stats = &gc_page_fragmentation_stats[i]; + double utilization = 1.0; + size_t n_freed_objs = jl_atomic_load_relaxed(&stats->n_freed_objs); + size_t n_pages_allocd = jl_atomic_load_relaxed(&stats->n_pages_allocd); + if (n_pages_allocd != 0) { + utilization -= ((double)n_freed_objs * (double)jl_gc_sizeclasses[i]) / (double)n_pages_allocd / (double)GC_PAGE_SZ; + } + jl_safe_printf("Size class %d: %.2f%% utilization\n", jl_gc_sizeclasses[i], utilization * 100.0); + jl_atomic_store_relaxed(&stats->n_freed_objs, 0); + jl_atomic_store_relaxed(&stats->n_pages_allocd, 0); + } + jl_safe_printf("-----------------------------------------\n"); +#endif +} + int64_t buffered_pages = 0; // Returns pointer to terminal pointer of list rooted at *pfl. @@ -1505,6 +1537,7 @@ static void gc_sweep_page(jl_gc_pool_t *p, jl_gc_page_stack_t *allocd, jl_gc_pag push_lf_back(&global_page_pool_lazily_freed, pg); } } + gc_update_page_fragmentation_data(pg); gc_time_count_page(freedall, pg_skpd); jl_ptls_t ptls = gc_all_tls_states[pg->thread_n]; jl_atomic_fetch_add(&ptls->gc_num.pool_live_bytes, GC_PAGE_SZ - GC_PAGE_OFFSET - nfree * osize); @@ -1718,6 +1751,7 @@ static void gc_sweep_pool(void) #else gc_free_pages(); #endif + gc_dump_page_utilization_data(); gc_time_pool_end(current_sweep_full); } diff --git a/src/gc.h b/src/gc.h index 4f2845f26e58cf..f43d671e8791e7 100644 --- a/src/gc.h +++ b/src/gc.h @@ -219,6 +219,14 @@ STATIC_INLINE jl_gc_pagemeta_t *pop_lf_back(jl_gc_page_stack_t *pool) JL_NOTSAFE } } +// data structures for tracking fragmentation in the pool allocator +// #define GC_MEASURE_PAGE_FRAGMENTATION + +typedef struct { + _Atomic(size_t) n_freed_objs; + _Atomic(size_t) n_pages_allocd; +} gc_fragmentation_stat_t; + #ifdef _P64 #define REGION0_PG_COUNT (1 << 16) #define REGION1_PG_COUNT (1 << 16)