histogram: add pow2 allocation stats via MIMALLOC_SHOW_HISTOGRAM=1

This commit is contained in:
Michael Clark 2022-01-17 10:19:38 +13:00
parent 38a03229c8
commit b797c72b2f
WARNING! Although there is a key with this ID in the database it does not verify this commit! This commit is SUSPICIOUS.
GPG key ID: 6BF1D7B357EF3E4F
6 changed files with 93 additions and 1 deletions

View file

@ -126,6 +126,8 @@ void _mi_heap_set_default_direct(mi_heap_t* heap);
// "stats.c"
void _mi_stats_done(mi_stats_t* stats);
void _mi_histogram_log(size_t size);
void _mi_histogram_print(mi_output_fun* out);
mi_msecs_t _mi_clock_now(void);
mi_msecs_t _mi_clock_end(mi_msecs_t start);

View file

@ -303,6 +303,7 @@ typedef enum mi_option_e {
// stable options
mi_option_show_errors,
mi_option_show_stats,
mi_option_show_histogram,
mi_option_verbose,
// the following options are experimental
mi_option_eager_commit,

View file

@ -98,7 +98,11 @@ extern inline mi_decl_restrict void* mi_malloc_small(size_t size) mi_attr_noexce
// The main allocation function
extern inline mi_decl_restrict void* mi_heap_malloc(mi_heap_t* heap, size_t size) mi_attr_noexcept {
if (mi_likely(size <= MI_SMALL_SIZE_MAX)) {
return mi_heap_malloc_small(heap, size);
void *p = mi_heap_malloc_small(heap, size);
#if MI_STAT>1
if (p) { _mi_histogram_log(size); }
#endif
return p;
}
else {
mi_assert(heap!=NULL);
@ -106,6 +110,9 @@ extern inline mi_decl_restrict void* mi_heap_malloc(mi_heap_t* heap, size_t size
void* const p = _mi_malloc_generic(heap, size + MI_PADDING_SIZE); // note: size can overflow but it is detected in malloc_generic
mi_assert_internal(p == NULL || mi_usable_size(p) >= size);
#if MI_STAT>1
if (p) { _mi_histogram_log(size); }
#endif
#if MI_STAT>1
if (p != NULL) {
if (!mi_heap_is_initialized(heap)) { heap = mi_get_default_heap(); }
mi_heap_stat_increase(heap, malloc, mi_usable_size(p));

View file

@ -543,6 +543,9 @@ static void mi_process_done(void) {
if (mi_option_is_enabled(mi_option_show_stats) || mi_option_is_enabled(mi_option_verbose)) {
mi_stats_print(NULL);
}
if (mi_option_is_enabled(mi_option_show_histogram)) {
_mi_histogram_print(NULL);
}
mi_allocator_done();
_mi_verbose_message("process done: 0x%zx\n", _mi_heap_main.thread_id);
os_preloading = true; // don't call the C runtime anymore

View file

@ -63,6 +63,7 @@ static mi_option_desc_t options[_mi_option_last] =
{ 0, UNINIT, MI_OPTION(show_errors) },
#endif
{ 0, UNINIT, MI_OPTION(show_stats) },
{ 0, UNINIT, MI_OPTION(show_histogram) },
{ 0, UNINIT, MI_OPTION(verbose) },
// the following options are experimental and not all combinations make sense.

View file

@ -451,6 +451,84 @@ mi_msecs_t _mi_clock_end(mi_msecs_t start) {
}
/* -----------------------------------------------------------
Histogram of allocations sizes (power of 2)
----------------------------------------------------------- */
static _Atomic(size_t) mi_hist[MI_SIZE_BITS] = { 0 };
void _mi_histogram_log(size_t size)
{
size_t bucket = MI_SIZE_BITS - 1 - mi_clz(size);
mi_atomic_increment_relaxed(mi_hist + bucket);
}
static char* _mi_make_bar(char *buf, size_t buflen, size_t value, size_t max, size_t width)
{
/* we can't dynamically detect if the terminal supports unicode block characters */
#if defined(_WIN32)
size_t v = value * width / max;
buf[0] = '\0';
while (v > 0) {
strncat(buf, "*", buflen--);
v--;
}
#else
static const char* a[] = { " ", "", "", "", "", "", "", "", "" };
size_t v = value * width * 8 / max;
buf[0] = '\0';
while (v > 8) {
strncat(buf, a[8], buflen--);
v-=8;
}
strncat(buf, a[v], buflen--);
#endif
return buf;
}
static char* _mi_format_bytes(char *buf, size_t buflen, size_t count)
{
#if MI_SIZE_BITS > 32
if (count & ~((((size_t)1) << 50) - (size_t)1)) {
snprintf(buf, buflen, "%zuPiB", (count+1) >> 50);
} else
if (count & ~((((size_t)1) << 40) - (size_t)1)) {
snprintf(buf, buflen, "%zuTiB", (count+1) >> 40);
} else
#endif
if (count & ~((((size_t)1) << 30) - (size_t)1)) {
snprintf(buf, buflen, "%zuGiB", (count+1) >> 30);
} else
if (count & ~((((size_t)1) << 20) - (size_t)1)) {
snprintf(buf, buflen, "%zuMiB", (count+1) >> 20);
} else
if (count & ~((((size_t)1) << 10) - (size_t)1)) {
snprintf(buf, buflen, "%zuKiB", (count+1) >> 10);
} else {
snprintf(buf, buflen, "%zuB", count);
}
return buf;
}
void _mi_histogram_print(mi_output_fun* out)
{
#define _NCOLS 50
char bar[_NCOLS*3+1], num1[16], num2[16];
size_t max_allocs = 0;
for (size_t i = 0; i < MI_SIZE_BITS; i++) {
if (mi_hist[i] > max_allocs) { max_allocs = mi_hist[i]; }
}
_mi_fputs(out, NULL, NULL, "\nhistogram of allocation sizes\n");
for (size_t i = 0; i < MI_SIZE_BITS; i++) {
if (mi_hist[i]) {
_mi_fprintf(out, NULL, "%9s - %-9s [ %-9zu ] %s\n",
_mi_format_bytes(num1, sizeof(num1), ((size_t)1 << i)),
_mi_format_bytes(num2, sizeof(num2), ((size_t)1 << (i+1))-1),
mi_hist[i], _mi_make_bar(bar, sizeof(bar), mi_hist[i], max_allocs, _NCOLS));
}
}
}
// --------------------------------------------------------
// Basic process statistics
// --------------------------------------------------------