Skip to content
Open
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions include/mimalloc-internal.h
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
1 change: 1 addition & 0 deletions include/mimalloc.h
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
9 changes: 8 additions & 1 deletion src/alloc.c
Original file line number Diff line number Diff line change
Expand Up @@ -98,14 +98,21 @@ 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);
mi_assert(heap->thread_id == 0 || heap->thread_id == _mi_thread_id()); // heaps are thread local
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));
Expand Down
5 changes: 5 additions & 0 deletions src/init.c
Original file line number Diff line number Diff line change
Expand Up @@ -543,6 +543,11 @@ 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_STAT>1
if (mi_option_is_enabled(mi_option_show_histogram)) {
_mi_histogram_print(NULL);
}
#endif
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
Expand Down
1 change: 1 addition & 0 deletions src/options.c
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
79 changes: 79 additions & 0 deletions src/stats.c
Original file line number Diff line number Diff line change
Expand Up @@ -451,6 +451,85 @@ 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)
{
if (mi_unlikely(size == 0)) return;
size_t bucket = mi_bsr(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
// --------------------------------------------------------
Expand Down