add mi_stats_get_bin_size and chunk counters

This commit is contained in:
Daan 2025-03-13 13:22:58 -07:00
parent f4f060543b
commit 004a093ec7
7 changed files with 32 additions and 17 deletions

View file

@ -11,7 +11,7 @@ terms of the MIT license. A copy of the license can be found in the file
#include <mimalloc.h> #include <mimalloc.h>
#include <stdint.h> #include <stdint.h>
#define MI_STAT_VERSION 1 // increased on every backward incompatible change #define MI_STAT_VERSION 2 // increased on every backward incompatible change
// count allocation over time // count allocation over time
typedef struct mi_stat_count_s { typedef struct mi_stat_count_s {
@ -65,6 +65,17 @@ typedef struct mi_stat_counter_s {
MI_STAT_COUNTER(pages_unabandon_busy_wait) \ MI_STAT_COUNTER(pages_unabandon_busy_wait) \
// Size bins for chunks
typedef enum mi_bbin_e {
MI_BBIN_SMALL, // slice_count == 1
MI_BBIN_OTHER, // slice_count: any other from the other bins, and 1 <= slice_count <= MI_BCHUNK_BITS
MI_BBIN_MEDIUM, // slice_count == 8
MI_BBIN_LARGE, // slice_count == MI_SIZE_BITS (only used if MI_ENABLE_LARGE_PAGES is 1)
MI_BBIN_NONE, // no bin assigned yet (the chunk is completely free)
MI_BBIN_COUNT
} mi_bbin_t;
// Define the statistics structure // Define the statistics structure
#define MI_BIN_HUGE (73U) // see types.h #define MI_BIN_HUGE (73U) // see types.h
#define MI_STAT_COUNT(stat) mi_stat_count_t stat; #define MI_STAT_COUNT(stat) mi_stat_count_t stat;
@ -83,6 +94,7 @@ typedef struct mi_stats_s
// size segregated statistics // size segregated statistics
mi_stat_count_t malloc_bins[MI_BIN_HUGE+1]; // allocation per size bin mi_stat_count_t malloc_bins[MI_BIN_HUGE+1]; // allocation per size bin
mi_stat_count_t page_bins[MI_BIN_HUGE+1]; // pages allocated per size bin mi_stat_count_t page_bins[MI_BIN_HUGE+1]; // pages allocated per size bin
mi_stat_count_t chunk_bins[MI_BBIN_COUNT]; // chunks per page sizes
} mi_stats_t; } mi_stats_t;
#undef MI_STAT_COUNT #undef MI_STAT_COUNT
@ -93,8 +105,9 @@ typedef struct mi_stats_s
extern "C" { extern "C" {
#endif #endif
mi_decl_export void mi_stats_get( size_t stats_size, mi_stats_t* stats ) mi_attr_noexcept; mi_decl_export void mi_stats_get( size_t stats_size, mi_stats_t* stats ) mi_attr_noexcept;
mi_decl_export char* mi_stats_get_json( size_t buf_size, char* buf ) mi_attr_noexcept; // use mi_free to free the result if the input buf == NULL mi_decl_export char* mi_stats_get_json( size_t buf_size, char* buf ) mi_attr_noexcept; // use mi_free to free the result if the input buf == NULL
mi_decl_export size_t mi_stats_get_bin_size(size_t bin) mi_attr_noexcept;
#ifdef __cplusplus #ifdef __cplusplus
} }

View file

@ -299,7 +299,9 @@ void _mi_assert_fail(const char* assertion, const char* fname, unsigned int line
#define MI_INIT64(x) MI_INIT32(x),MI_INIT32(x) #define MI_INIT64(x) MI_INIT32(x),MI_INIT32(x)
#define MI_INIT128(x) MI_INIT64(x),MI_INIT64(x) #define MI_INIT128(x) MI_INIT64(x),MI_INIT64(x)
#define MI_INIT256(x) MI_INIT128(x),MI_INIT128(x) #define MI_INIT256(x) MI_INIT128(x),MI_INIT128(x)
#define MI_INIT74(x) MI_INIT64(x),MI_INIT8(x),x(),x() #define MI_INIT74(x) MI_INIT64(x),MI_INIT8(x),x(),x()
#define MI_INIT5(x) MI_INIT4(x),x()
#include <string.h> #include <string.h>
// initialize a local variable to zero; use memset as compilers optimize constant sized memset's // initialize a local variable to zero; use memset as compilers optimize constant sized memset's

View file

@ -1415,10 +1415,12 @@ static void mi_bbitmap_set_chunk_bin(mi_bbitmap_t* bbitmap, size_t chunk_idx, mi
mi_assert_internal(chunk_idx < mi_bbitmap_chunk_count(bbitmap)); mi_assert_internal(chunk_idx < mi_bbitmap_chunk_count(bbitmap));
for (mi_bbin_t ibin = MI_BBIN_SMALL; ibin < MI_BBIN_NONE; ibin = mi_bbin_inc(ibin)) { for (mi_bbin_t ibin = MI_BBIN_SMALL; ibin < MI_BBIN_NONE; ibin = mi_bbin_inc(ibin)) {
if (ibin == bin) { if (ibin == bin) {
mi_bchunk_set(& bbitmap->chunkmap_bins[ibin], chunk_idx, NULL); const bool was_clear = mi_bchunk_set(& bbitmap->chunkmap_bins[ibin], chunk_idx, NULL);
if (was_clear) { mi_os_stat_increase(chunk_bins[ibin],1); }
} }
else { else {
mi_bchunk_clear(&bbitmap->chunkmap_bins[ibin], chunk_idx, NULL); const bool was_set = mi_bchunk_clear(&bbitmap->chunkmap_bins[ibin], chunk_idx, NULL);
if (was_set) { mi_os_stat_decrease(chunk_bins[ibin],1); }
} }
} }
} }

View file

@ -212,16 +212,7 @@ bool _mi_bitmap_forall_setc_ranges(mi_bitmap_t* bitmap, mi_forall_set_fun_t* vis
much fragmentation since we keep chunks for larger blocks separate. much fragmentation since we keep chunks for larger blocks separate.
---------------------------------------------------------------------------- */ ---------------------------------------------------------------------------- */
// Size bins; larger bins are allowed to go into smaller bins. // mi_bbin_t is defined in mimalloc-stats.h
// SMALL can only be in small (and NONE), so they cannot fragment the larger bins.
typedef enum mi_bbin_e {
MI_BBIN_SMALL, // slice_count == 1
MI_BBIN_OTHER, // slice_count: any other from the other bins, and 1 <= slice_count <= MI_BCHUNK_BITS
MI_BBIN_MEDIUM, // slice_count == 8
MI_BBIN_LARGE, // slice_count == MI_BFIELD_BITS -- only used if MI_ENABLE_LARGE_PAGES is 1
MI_BBIN_NONE, // no bin assigned yet (the chunk is completely free)
MI_BBIN_COUNT
} mi_bbin_t;
static inline mi_bbin_t mi_bbin_inc(mi_bbin_t bbin) { static inline mi_bbin_t mi_bbin_inc(mi_bbin_t bbin) {
mi_assert_internal(bbin < MI_BBIN_COUNT); mi_assert_internal(bbin < MI_BBIN_COUNT);

View file

@ -83,7 +83,8 @@ const mi_page_t _mi_page_empty = {
{ { 0 }, { 0 }, { 0 }, { 0 } }, \ { { 0 }, { 0 }, { 0 }, { 0 } }, \
\ \
{ MI_INIT74(MI_STAT_COUNT_NULL) }, \ { MI_INIT74(MI_STAT_COUNT_NULL) }, \
{ MI_INIT74(MI_STAT_COUNT_NULL) } { MI_INIT74(MI_STAT_COUNT_NULL) }, \
{ MI_INIT5(MI_STAT_COUNT_NULL) }
// -------------------------------------------------------- // --------------------------------------------------------
// Statically allocate an empty heap as the initial // Statically allocate an empty heap as the initial

View file

@ -106,6 +106,7 @@ size_t _mi_bin(size_t size) {
} }
size_t _mi_bin_size(size_t bin) { size_t _mi_bin_size(size_t bin) {
mi_assert_internal(bin <= MI_BIN_HUGE);
return _mi_heap_empty.pages[bin].block_size; return _mi_heap_empty.pages[bin].block_size;
} }

View file

@ -479,6 +479,11 @@ mi_decl_export void mi_process_info(size_t* elapsed_msecs, size_t* user_msecs, s
// Return statistics // Return statistics
// -------------------------------------------------------- // --------------------------------------------------------
size_t mi_stats_get_bin_size(size_t bin) mi_attr_noexcept {
if (bin > MI_BIN_HUGE) return 0;
return _mi_bin_size(bin);
}
void mi_stats_get(size_t stats_size, mi_stats_t* stats) mi_attr_noexcept { void mi_stats_get(size_t stats_size, mi_stats_t* stats) mi_attr_noexcept {
if (stats == NULL || stats_size == 0) return; if (stats == NULL || stats_size == 0) return;
_mi_memzero(stats, stats_size); _mi_memzero(stats, stats_size);
@ -529,7 +534,7 @@ static void mi_heap_buf_print(mi_heap_buf_t* hbuf, const char* msg) {
} }
static void mi_heap_buf_print_count_bin(mi_heap_buf_t* hbuf, const char* prefix, mi_stat_count_t* stat, size_t bin, bool add_comma) { static void mi_heap_buf_print_count_bin(mi_heap_buf_t* hbuf, const char* prefix, mi_stat_count_t* stat, size_t bin, bool add_comma) {
const size_t binsize = _mi_bin_size(bin); const size_t binsize = mi_stats_get_bin_size(bin);
const size_t pagesize = (binsize <= MI_SMALL_MAX_OBJ_SIZE ? MI_SMALL_PAGE_SIZE : const size_t pagesize = (binsize <= MI_SMALL_MAX_OBJ_SIZE ? MI_SMALL_PAGE_SIZE :
(binsize <= MI_MEDIUM_MAX_OBJ_SIZE ? MI_MEDIUM_PAGE_SIZE : (binsize <= MI_MEDIUM_MAX_OBJ_SIZE ? MI_MEDIUM_PAGE_SIZE :
(binsize <= MI_LARGE_MAX_OBJ_SIZE ? MI_LARGE_PAGE_SIZE : 0))); (binsize <= MI_LARGE_MAX_OBJ_SIZE ? MI_LARGE_PAGE_SIZE : 0)));