From 0dd4eb2c6d16fa0daa6e91817a144ce9289e25bd Mon Sep 17 00:00:00 2001 From: jm4games Date: Tue, 28 Jan 2025 14:22:27 -0800 Subject: [PATCH] exposing heap metrics --- CMakeLists.txt | 9 ++++ include/mimalloc-statistics.h | 18 ++++++++ include/mimalloc/stats.h | 84 +++++++++++++++++++++++++++++++++++ include/mimalloc/types.h | 62 +++----------------------- src/stats.c | 42 +++++++++++++++++- 5 files changed, 157 insertions(+), 58 deletions(-) create mode 100644 include/mimalloc-statistics.h create mode 100644 include/mimalloc/stats.h diff --git a/CMakeLists.txt b/CMakeLists.txt index 05b97fc9f9..1790371486 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -39,6 +39,7 @@ option(MI_NO_PADDING "Force no use of padding even in DEBUG mode etc." OF option(MI_INSTALL_TOPLEVEL "Install directly into $CMAKE_INSTALL_PREFIX instead of PREFIX/lib/mimalloc-version" OFF) option(MI_NO_THP "Disable transparent huge pages support on Linux/Android for the mimalloc process only" OFF) option(MI_EXTRA_CPPDEFS "Extra pre-processor definitions (use as `-DMI_EXTRA_CPPDEFS=\"opt1=val1;opt2=val2\"`)" "") +option(MI_STAT_LEVEL "Optionally override stats level to 1 or 2 when debug not enable" 0) # deprecated options option(MI_WIN_USE_FLS "Use Fiber local storage on Windows to detect thread termination (deprecated)" OFF) @@ -545,6 +546,10 @@ message(STATUS "Link libraries : ${mi_libraries}") message(STATUS "Build targets : ${mi_build_targets}") message(STATUS "") +if (MI_STAT_LEVEL GREATER 0) + add_compile_definitions(MI_STAT=${MI_STAT_LEVEL}) +endif () + # ----------------------------------------------------------------------------- # Main targets # ----------------------------------------------------------------------------- @@ -618,6 +623,10 @@ install(FILES include/mimalloc-new-delete.h DESTINATION ${mi_install_incdir}) install(FILES cmake/mimalloc-config.cmake DESTINATION ${mi_install_cmakedir}) install(FILES cmake/mimalloc-config-version.cmake DESTINATION ${mi_install_cmakedir}) +if (MI_STAT_LEVEL GREATER 0 OR CMAKE_BUILD_TYPE MATCHES "Debug") + install(FILES include/mimalloc-statistics.h DESTINATION ${mi_install_incdir}) + install(FILES include/mimalloc/stats.h DESTINATION ${mi_install_incdir}/mimalloc) +endif () # single object file for more predictable static overriding if (MI_BUILD_OBJECT) diff --git a/include/mimalloc-statistics.h b/include/mimalloc-statistics.h new file mode 100644 index 0000000000..1d190b6233 --- /dev/null +++ b/include/mimalloc-statistics.h @@ -0,0 +1,18 @@ +/* ---------------------------------------------------------------------------- +Copyright (c) 2018-2020 Microsoft Research, Daan Leijen +This is free software; you can redistribute it and/or modify it under the +terms of the MIT license. A copy of the license can be found in the file +"LICENSE" at the root of this distribution. +-----------------------------------------------------------------------------*/ +#pragma once +#ifndef MIMALLOC_STATISTICS_H +#define MIMALLOC_STATISTICS_H + +#include "mimalloc/stats.h" +#include "mimalloc.h" + +mi_decl_export const mi_stats_t* mi_thread_stats(void) mi_attr_noexcept; +mi_decl_export const mi_stats_t* mi_thread_heap_stats(const mi_heap_t* heap) mi_attr_noexcept; +mi_decl_export mi_os_stats_t mi_os_stats(void) mi_attr_noexcept; + +#endif diff --git a/include/mimalloc/stats.h b/include/mimalloc/stats.h new file mode 100644 index 0000000000..70fa2abf6f --- /dev/null +++ b/include/mimalloc/stats.h @@ -0,0 +1,84 @@ +/* ---------------------------------------------------------------------------- +Copyright (c) 2018-2024, Microsoft Research, Daan Leijen +This is free software; you can redistribute it and/or modify it under the +terms of the MIT license. A copy of the license can be found in the file +"LICENSE" at the root of this distribution. +-----------------------------------------------------------------------------*/ +#pragma once +#ifndef MIMALLOC_STATS_H +#define MIMALLOC_STATS_H + +#include // ptrdiff_t +#include // uintptr_t, uint16_t, etc + +#ifndef MI_STAT +#if (MI_DEBUG>0) +#define MI_STAT 2 +#else +#define MI_STAT 0 +#endif +#endif + +// Maximum number of size classes. (spaced exponentially in 12.5% increments) +#define MI_BIN_HUGE (73U) +#define MI_BIN_FULL (MI_BIN_HUGE+1) +#define MI_BIN_COUNT (MI_BIN_FULL+1) + +typedef struct mi_stat_count_s { + int64_t allocated; + int64_t freed; + int64_t peak; + int64_t current; +} mi_stat_count_t; + +typedef struct mi_stat_counter_s { + int64_t total; + int64_t count; +} mi_stat_counter_t; + +typedef struct mi_stats_s { + mi_stat_count_t pages; + mi_stat_count_t reserved; + mi_stat_count_t committed; + mi_stat_count_t reset; + mi_stat_count_t purged; + mi_stat_count_t page_committed; + mi_stat_count_t pages_abandoned; + mi_stat_count_t threads; + mi_stat_count_t normal; + mi_stat_count_t huge; + mi_stat_count_t giant; + mi_stat_count_t malloc; + mi_stat_counter_t pages_extended; + mi_stat_counter_t pages_reclaim_on_alloc; + mi_stat_counter_t pages_reclaim_on_free; + mi_stat_counter_t pages_reabandon_full; + mi_stat_counter_t pages_unabandon_busy_wait; + mi_stat_counter_t mmap_calls; + mi_stat_counter_t commit_calls; + mi_stat_counter_t reset_calls; + mi_stat_counter_t purge_calls; + mi_stat_counter_t arena_purges; + mi_stat_counter_t page_no_retire; + mi_stat_counter_t searches; + mi_stat_counter_t normal_count; + mi_stat_counter_t huge_count; + mi_stat_counter_t arena_count; + mi_stat_counter_t guarded_alloc_count; +#if MI_STAT>1 + mi_stat_count_t normal_bins[MI_BIN_COUNT]; +#endif +} mi_stats_t; + +typedef struct mi_os_stats_s { + mi_stat_count_t reserved; + mi_stat_count_t committed; + mi_stat_count_t reset; + mi_stat_count_t purged; + mi_stat_counter_t mmap_calls; + mi_stat_counter_t commit_calls; + mi_stat_counter_t reset_calls; + mi_stat_counter_t purge_calls; +} mi_os_stats_t; + +#endif diff --git a/include/mimalloc/types.h b/include/mimalloc/types.h index ec4144d16e..f688a394d5 100644 --- a/include/mimalloc/types.h +++ b/include/mimalloc/types.h @@ -24,6 +24,7 @@ terms of the MIT license. A copy of the license can be found in the file #include // error codes #include "bits.h" // size defines (MI_INTPTR_SIZE etc), bit operations #include "atomic.h" // _Atomic primitives +#include "stats.h" // Minimal alignment necessary. On most platforms 16 bytes are needed // due to SSE registers for example. This must be at least `sizeof(void*)` @@ -136,11 +137,13 @@ terms of the MIT license. A copy of the license can be found in the file #define MI_LARGE_PAGE_SIZE (MI_SIZE_SIZE*MI_MEDIUM_PAGE_SIZE) // 4 MiB (=word in the bchunk bitmap) -// Maximum number of size classes. (spaced exponentially in 12.5% increments) #define MI_BIN_HUGE (73U) #define MI_BIN_FULL (MI_BIN_HUGE+1) #define MI_BIN_COUNT (MI_BIN_FULL+1) +#if (MI_MEDIUM_OBJ_WSIZE_MAX >= 655360) +#error "mimalloc internal: define more bins" +#endif // We never allocate more than PTRDIFF_MAX (see also ) #define MI_MAX_ALLOC_SIZE PTRDIFF_MAX @@ -438,63 +441,9 @@ struct mi_heap_s { // ------------------------------------------------------ // Statistics +// declare statistics functions here (not stats.h) to avoid exposing these to library consumers // ------------------------------------------------------ -#ifndef MI_STAT -#if (MI_DEBUG>0) -#define MI_STAT 2 -#else -#define MI_STAT 0 -#endif -#endif - -typedef struct mi_stat_count_s { - int64_t allocated; - int64_t freed; - int64_t peak; - int64_t current; -} mi_stat_count_t; - -typedef struct mi_stat_counter_s { - int64_t total; - int64_t count; -} mi_stat_counter_t; - -typedef struct mi_stats_s { - mi_stat_count_t pages; - mi_stat_count_t reserved; - mi_stat_count_t committed; - mi_stat_count_t reset; - mi_stat_count_t purged; - mi_stat_count_t page_committed; - mi_stat_count_t pages_abandoned; - mi_stat_count_t threads; - mi_stat_count_t normal; - mi_stat_count_t huge; - mi_stat_count_t giant; - mi_stat_count_t malloc; - mi_stat_counter_t pages_extended; - mi_stat_counter_t pages_reclaim_on_alloc; - mi_stat_counter_t pages_reclaim_on_free; - mi_stat_counter_t pages_reabandon_full; - mi_stat_counter_t pages_unabandon_busy_wait; - mi_stat_counter_t mmap_calls; - mi_stat_counter_t commit_calls; - mi_stat_counter_t reset_calls; - mi_stat_counter_t purge_calls; - mi_stat_counter_t arena_purges; - mi_stat_counter_t page_no_retire; - mi_stat_counter_t searches; - mi_stat_counter_t normal_count; - mi_stat_counter_t huge_count; - mi_stat_counter_t arena_count; - mi_stat_counter_t guarded_alloc_count; -#if MI_STAT>1 - mi_stat_count_t normal_bins[MI_BIN_COUNT]; -#endif -} mi_stats_t; - - // add to stat keeping track of the peak void __mi_stat_increase(mi_stat_count_t* stat, size_t amount); void __mi_stat_decrease(mi_stat_count_t* stat, size_t amount); @@ -547,7 +496,6 @@ void __mi_stat_counter_increase_mt(mi_stat_counter_t* stat, size_t amount); #define mi_debug_heap_stat_increase(heap,stat,amount) mi_debug_stat_increase( (heap)->tld->stats.stat, amount) #define mi_debug_heap_stat_decrease(heap,stat,amount) mi_debug_stat_decrease( (heap)->tld->stats.stat, amount) - // ------------------------------------------------------ // Sub processes use separate arena's and no heaps/pages/blocks // are shared between sub processes. diff --git a/src/stats.c b/src/stats.c index 057dc0938b..f31586dae9 100644 --- a/src/stats.c +++ b/src/stats.c @@ -105,6 +105,21 @@ void __mi_stat_adjust_decrease(mi_stat_count_t* stat, size_t amount, bool on_all mi_stat_adjust(stat, -((int64_t)amount), on_alloc); } +static void mi_stat_atomic_copy(mi_stat_count_t* dst, const mi_stat_count_t* src) { + if (dst==src) return; + if (src->allocated==0 && src->freed==0) return; + dst->allocated = (mi_atomic_loadi64_relaxed((_Atomic(int64_t)*)(&src->allocated))); + dst->current = (mi_atomic_loadi64_relaxed((_Atomic(int64_t)*)(&src->current))); + dst->freed = (mi_atomic_loadi64_relaxed((_Atomic(int64_t)*)(&src->freed))); + dst->peak = (mi_atomic_loadi64_relaxed((_Atomic(int64_t)*)(&src->peak))); +} + +static void mi_stat_counter_atomic_copy(mi_stat_counter_t* dst, const mi_stat_counter_t* src) { + if (dst==src) return; + if (src->count==0) return; + dst->count = (mi_atomic_loadi64_relaxed((_Atomic(int64_t)*)(&src->count))); + dst->total = (mi_atomic_loadi64_relaxed((_Atomic(int64_t)*)(&src->total))); +} // must be thread safe as it is called from stats_merge static void mi_stat_add(mi_stat_count_t* stat, const mi_stat_count_t* src, int64_t unit) { @@ -448,6 +463,32 @@ void mi_thread_stats_print_out(mi_output_fun* out, void* arg) mi_attr_noexcept { _mi_stats_print(mi_get_tld_stats(), out, arg); } +const mi_stats_t* mi_thread_heap_stats(const mi_heap_t* heap) mi_attr_noexcept { + return &heap->tld->stats; +} + +const mi_stats_t* mi_thread_stats(void) mi_attr_noexcept { + return mi_thread_heap_stats(mi_heap_get_default()); +} + +mi_os_stats_t mi_os_stats(void) mi_attr_noexcept { + mi_os_stats_t stats; + memset(&stats, 0, sizeof(mi_os_stats_t)); + + mi_subproc_t* subproc = _mi_subproc(); + + mi_stat_atomic_copy(&stats.reserved, &subproc->stats.reserved); + mi_stat_atomic_copy(&stats.committed, &subproc->stats.committed); + mi_stat_atomic_copy(&stats.reset, &subproc->stats.reset); + mi_stat_atomic_copy(&stats.purged, &subproc->stats.purged); + + mi_stat_counter_atomic_copy(&stats.mmap_calls, &subproc->stats.mmap_calls); + mi_stat_counter_atomic_copy(&stats.commit_calls, &subproc->stats.commit_calls); + mi_stat_counter_atomic_copy(&stats.reset_calls, &subproc->stats.reset_calls); + mi_stat_counter_atomic_copy(&stats.purge_calls, &subproc->stats.purge_calls); + + return stats; +} // ---------------------------------------------------------------- // Basic timer for convenience; use milli-seconds to avoid doubles @@ -472,7 +513,6 @@ mi_msecs_t _mi_clock_end(mi_msecs_t start) { return (end - start - mi_clock_diff); } - // -------------------------------------------------------- // Basic process statistics // -------------------------------------------------------- -- 2.45.2