This commit is contained in:
daanx 2025-03-31 11:25:58 -07:00
commit 78e2684cd0
19 changed files with 39 additions and 47 deletions

View file

@ -750,8 +750,8 @@ if (MI_BUILD_TESTS)
endif() endif()
target_compile_options(mimalloc-test-stress-dynamic PRIVATE ${mi_cflags}) target_compile_options(mimalloc-test-stress-dynamic PRIVATE ${mi_cflags})
target_include_directories(mimalloc-test-stress-dynamic PRIVATE include) target_include_directories(mimalloc-test-stress-dynamic PRIVATE include)
target_link_libraries(mimalloc-test-stress-dynamic PRIVATE mimalloc ${mi_libraries}) # mi_version
if(WIN32) if(WIN32)
target_link_libraries(mimalloc-test-stress-dynamic PRIVATE mimalloc ${mi_libraries}) # mi_version
add_test(NAME test-stress-dynamic COMMAND ${CMAKE_COMMAND} -E env MIMALLOC_SHOW_STATS=1 $<TARGET_FILE:mimalloc-test-stress-dynamic>) add_test(NAME test-stress-dynamic COMMAND ${CMAKE_COMMAND} -E env MIMALLOC_SHOW_STATS=1 $<TARGET_FILE:mimalloc-test-stress-dynamic>)
else() else()
if(APPLE) if(APPLE)

BIN
bin/mimalloc-redirect-arm64.dll Normal file → Executable file

Binary file not shown.

BIN
bin/mimalloc-redirect-arm64.lib Normal file → Executable file

Binary file not shown.

BIN
bin/mimalloc-redirect-arm64ec.dll Normal file → Executable file

Binary file not shown.

BIN
bin/mimalloc-redirect-arm64ec.lib Normal file → Executable file

Binary file not shown.

BIN
bin/mimalloc-redirect.dll Normal file → Executable file

Binary file not shown.

BIN
bin/mimalloc-redirect.lib Normal file → Executable file

Binary file not shown.

BIN
bin/mimalloc-redirect32.dll Normal file → Executable file

Binary file not shown.

BIN
bin/mimalloc-redirect32.lib Normal file → Executable file

Binary file not shown.

View file

@ -1,6 +1,6 @@
set(mi_version_major 3) set(mi_version_major 3)
set(mi_version_minor 0) set(mi_version_minor 0)
set(mi_version_patch 3) set(mi_version_patch 4)
set(mi_version ${mi_version_major}.${mi_version_minor}) set(mi_version ${mi_version_major}.${mi_version_minor})
set(PACKAGE_VERSION ${mi_version}) set(PACKAGE_VERSION ${mi_version})

View file

@ -8,7 +8,7 @@ terms of the MIT license. A copy of the license can be found in the file
#ifndef MIMALLOC_H #ifndef MIMALLOC_H
#define MIMALLOC_H #define MIMALLOC_H
#define MI_MALLOC_VERSION 303 // major + 2 digits minor #define MI_MALLOC_VERSION 304 // major + 2 digits minor
// ------------------------------------------------------ // ------------------------------------------------------
// Compiler specific attributes // Compiler specific attributes

View file

@ -376,8 +376,9 @@ static inline void mi_atomic_yield(void) {
_mm_pause(); _mm_pause();
} }
#elif (defined(__GNUC__) || defined(__clang__)) && \ #elif (defined(__GNUC__) || defined(__clang__)) && \
(defined(__x86_64__) || defined(__i386__) || defined(__arm__) || defined(__armel__) || defined(__ARMEL__) || \ (defined(__x86_64__) || defined(__i386__) || \
defined(__aarch64__) || defined(__powerpc__) || defined(__ppc__) || defined(__PPC__)) || defined(__POWERPC__) defined(__aarch64__) || defined(__arm__) || \
defined(__powerpc__) || defined(__ppc__) || defined(__PPC__) || defined(__POWERPC__))
#if defined(__x86_64__) || defined(__i386__) #if defined(__x86_64__) || defined(__i386__)
static inline void mi_atomic_yield(void) { static inline void mi_atomic_yield(void) {
__asm__ volatile ("pause" ::: "memory"); __asm__ volatile ("pause" ::: "memory");
@ -386,10 +387,16 @@ static inline void mi_atomic_yield(void) {
static inline void mi_atomic_yield(void) { static inline void mi_atomic_yield(void) {
__asm__ volatile("wfe"); __asm__ volatile("wfe");
} }
#elif (defined(__arm__) && __ARM_ARCH >= 7) #elif defined(__arm__)
#if __ARM_ARCH >= 7
static inline void mi_atomic_yield(void) { static inline void mi_atomic_yield(void) {
__asm__ volatile("yield" ::: "memory"); __asm__ volatile("yield" ::: "memory");
} }
#else
static inline void mi_atomic_yield(void) {
__asm__ volatile ("nop" ::: "memory");
}
#endif
#elif defined(__powerpc__) || defined(__ppc__) || defined(__PPC__) || defined(__POWERPC__) #elif defined(__powerpc__) || defined(__ppc__) || defined(__PPC__) || defined(__POWERPC__)
#ifdef __APPLE__ #ifdef __APPLE__
static inline void mi_atomic_yield(void) { static inline void mi_atomic_yield(void) {
@ -400,10 +407,6 @@ static inline void mi_atomic_yield(void) {
__asm__ __volatile__ ("or 27,27,27" ::: "memory"); __asm__ __volatile__ ("or 27,27,27" ::: "memory");
} }
#endif #endif
#elif defined(__armel__) || defined(__ARMEL__)
static inline void mi_atomic_yield(void) {
__asm__ volatile ("nop" ::: "memory");
}
#endif #endif
#elif defined(__sun) #elif defined(__sun)
// Fallback for other archs // Fallback for other archs

View file

@ -339,10 +339,6 @@ typedef struct mi_option_desc_s {
const char* legacy_name; // potential legacy option name const char* legacy_name; // potential legacy option name
} mi_option_desc_t; } mi_option_desc_t;
// the static options
extern mi_decl_hidden mi_option_desc_t mi_options[_mi_option_last];
/* ----------------------------------------------------------- /* -----------------------------------------------------------
Inlined definitions Inlined definitions
----------------------------------------------------------- */ ----------------------------------------------------------- */
@ -570,14 +566,14 @@ static inline size_t _mi_page_map_index(const void* p, size_t* sub_idx) {
static inline mi_page_t* _mi_unchecked_ptr_page(const void* p) { static inline mi_page_t* _mi_unchecked_ptr_page(const void* p) {
size_t sub_idx; size_t sub_idx;
const size_t idx = _mi_page_map_index(p, &sub_idx); const size_t idx = _mi_page_map_index(p, &sub_idx);
return _mi_page_map[idx][sub_idx]; return _mi_page_map[idx][sub_idx]; // NULL if p==NULL
} }
static inline mi_page_t* _mi_checked_ptr_page(const void* p) { static inline mi_page_t* _mi_checked_ptr_page(const void* p) {
size_t sub_idx; size_t sub_idx;
const size_t idx = _mi_page_map_index(p, &sub_idx); const size_t idx = _mi_page_map_index(p, &sub_idx);
mi_page_t** const sub = _mi_page_map[idx]; mi_page_t** const sub = _mi_page_map[idx];
if mi_unlikely(sub == NULL) return (mi_page_t*)&_mi_page_empty; if mi_unlikely(sub == NULL) return NULL;
return sub[sub_idx]; return sub[sub_idx];
} }

View file

@ -12,9 +12,9 @@ is a general purpose allocator with excellent [performance](#performance) charac
Initially developed by Daan Leijen for the runtime systems of the Initially developed by Daan Leijen for the runtime systems of the
[Koka](https://koka-lang.github.io) and [Lean](https://github.com/leanprover/lean) languages. [Koka](https://koka-lang.github.io) and [Lean](https://github.com/leanprover/lean) languages.
Latest release : `v3.0.2` (beta) (2025-03-06). Latest release : `v3.0.3` (beta) (2025-03-28).
Latest v2 release: `v2.2.2` (2025-03-06). Latest v2 release: `v2.2.3` (2025-03-28).
Latest v1 release: `v1.9.2` (2024-03-06). Latest v1 release: `v1.9.3` (2024-03-28).
mimalloc is a drop-in replacement for `malloc` and can be used in other programs mimalloc is a drop-in replacement for `malloc` and can be used in other programs
without code changes, for example, on dynamically linked ELF-based systems (Linux, BSD, etc.) you can use it as: without code changes, for example, on dynamically linked ELF-based systems (Linux, BSD, etc.) you can use it as:
@ -84,6 +84,9 @@ Enjoy!
### Releases ### Releases
* 2025-03-28, `v1.9.3`, `v2.2.3`, `v3.0.3` (beta): Various small bug and build fixes, including:
fix arm32 pre v7 builds, fix mingw build, get runtime statistics, improve statistic commit counts,
fix execution on non BMI1 x64 systems.
* 2025-03-06, `v1.9.2`, `v2.2.2`, `v3.0.2-beta`: Various small bug and build fixes. * 2025-03-06, `v1.9.2`, `v2.2.2`, `v3.0.2-beta`: Various small bug and build fixes.
Add `mi_options_print`, `mi_arenas_print`, and the experimental `mi_stat_get` and `mi_stat_get_json`. Add `mi_options_print`, `mi_arenas_print`, and the experimental `mi_stat_get` and `mi_stat_get_json`.
Add `mi_thread_set_in_threadpool` and `mi_heap_set_numa_affinity` (v3 only). Add vcpkg portfile. Add `mi_thread_set_in_threadpool` and `mi_heap_set_numa_affinity` (v3 only). Add vcpkg portfile.

View file

@ -115,6 +115,7 @@ static inline void mi_block_check_unguard(mi_page_t* page, mi_block_t* block, vo
// free a local pointer (page parameter comes first for better codegen) // free a local pointer (page parameter comes first for better codegen)
static void mi_decl_noinline mi_free_generic_local(mi_page_t* page, void* p) mi_attr_noexcept { static void mi_decl_noinline mi_free_generic_local(mi_page_t* page, void* p) mi_attr_noexcept {
mi_assert_internal(p!=NULL && page != NULL);
mi_block_t* const block = (mi_page_has_aligned(page) ? _mi_page_ptr_unalign(page, p) : (mi_block_t*)p); mi_block_t* const block = (mi_page_has_aligned(page) ? _mi_page_ptr_unalign(page, p) : (mi_block_t*)p);
mi_block_check_unguard(page, block, p); mi_block_check_unguard(page, block, p);
mi_free_block_local(page, block, true /* track stats */, true /* check for a full page */); mi_free_block_local(page, block, true /* track stats */, true /* check for a full page */);
@ -122,11 +123,7 @@ static void mi_decl_noinline mi_free_generic_local(mi_page_t* page, void* p) mi_
// free a pointer owned by another thread (page parameter comes first for better codegen) // free a pointer owned by another thread (page parameter comes first for better codegen)
static void mi_decl_noinline mi_free_generic_mt(mi_page_t* page, void* p) mi_attr_noexcept { static void mi_decl_noinline mi_free_generic_mt(mi_page_t* page, void* p) mi_attr_noexcept {
if (p==NULL) return; // a NULL pointer is seen as abandoned (tid==0) with a full flag set mi_assert_internal(p!=NULL && page != NULL);
#if !MI_PAGE_MAP_FLAT
if (page==&_mi_page_empty) return; // an invalid pointer may lead to using the empty page
#endif
mi_assert_internal(p!=NULL && page != NULL && page != &_mi_page_empty);
mi_block_t* const block = _mi_page_ptr_unalign(page, p); // don't check `has_aligned` flag to avoid a race (issue #865) mi_block_t* const block = _mi_page_ptr_unalign(page, p); // don't check `has_aligned` flag to avoid a race (issue #865)
mi_block_check_unguard(page, block, p); mi_block_check_unguard(page, block, p);
mi_free_block_mt(page, block); mi_free_block_mt(page, block);
@ -150,13 +147,8 @@ static inline mi_page_t* mi_validate_ptr_page(const void* p, const char* msg)
return NULL; return NULL;
} }
mi_page_t* page = _mi_safe_ptr_page(p); mi_page_t* page = _mi_safe_ptr_page(p);
if (page == NULL) { if (p != NULL && page == NULL) {
if (p != NULL) { _mi_error_message(EINVAL, "%s: invalid pointer: %p\n", msg, p);
_mi_error_message(EINVAL, "%s: invalid pointer: %p\n", msg, p);
}
#if !MI_PAGE_MAP_FLAT
page = (mi_page_t*)&_mi_page_empty;
#endif
} }
return page; return page;
#else #else
@ -169,12 +161,9 @@ static inline mi_page_t* mi_validate_ptr_page(const void* p, const char* msg)
void mi_free(void* p) mi_attr_noexcept void mi_free(void* p) mi_attr_noexcept
{ {
mi_page_t* const page = mi_validate_ptr_page(p,"mi_free"); mi_page_t* const page = mi_validate_ptr_page(p,"mi_free");
#if MI_PAGE_MAP_FLAT // if not flat, p==NULL leads to `_mi_page_empty` which leads to `mi_free_generic_mt`
if mi_unlikely(page==NULL) return; if mi_unlikely(page==NULL) return;
#endif mi_assert_internal(p!=NULL && page!=NULL);
mi_assert_internal(page!=NULL);
const mi_threadid_t xtid = (_mi_prim_thread_id() ^ mi_page_xthread_id(page)); const mi_threadid_t xtid = (_mi_prim_thread_id() ^ mi_page_xthread_id(page));
if mi_likely(xtid == 0) { // `tid == mi_page_thread_id(page) && mi_page_flags(page) == 0` if mi_likely(xtid == 0) { // `tid == mi_page_thread_id(page) && mi_page_flags(page) == 0`
// thread-local, aligned, and not a full page // thread-local, aligned, and not a full page

View file

@ -16,7 +16,7 @@ terms of the MIT license. A copy of the license can be found in the file
// Empty page used to initialize the small free pages array // Empty page used to initialize the small free pages array
const mi_page_t _mi_page_empty = { const mi_page_t _mi_page_empty = {
MI_ATOMIC_VAR_INIT(MI_PAGE_IN_FULL_QUEUE), // xthread_id (must set flag to catch NULL on a free) MI_ATOMIC_VAR_INIT(0), // xthread_id
NULL, // free NULL, // free
0, // used 0, // used
0, // capacity 0, // capacity

View file

@ -98,8 +98,8 @@ int mi_version(void) mi_attr_noexcept {
#endif #endif
#endif #endif
// Static options (only exposed for the debugger) // Static options
mi_option_desc_t mi_options[_mi_option_last] = static mi_option_desc_t mi_options[_mi_option_last] =
{ {
// stable options // stable options
#if MI_DEBUG || defined(MI_SHOW_ERRORS) #if MI_DEBUG || defined(MI_SHOW_ERRORS)
@ -329,7 +329,7 @@ static void mi_cdecl mi_out_stderr(const char* msg, void* arg) {
#ifndef MI_MAX_DELAY_OUTPUT #ifndef MI_MAX_DELAY_OUTPUT
#define MI_MAX_DELAY_OUTPUT ((size_t)(16*1024)) #define MI_MAX_DELAY_OUTPUT ((size_t)(16*1024))
#endif #endif
static char out_buf[MI_MAX_DELAY_OUTPUT+1]; static char mi_output_buffer[MI_MAX_DELAY_OUTPUT+1];
static _Atomic(size_t) out_len; static _Atomic(size_t) out_len;
static void mi_cdecl mi_out_buf(const char* msg, void* arg) { static void mi_cdecl mi_out_buf(const char* msg, void* arg) {
@ -345,7 +345,8 @@ static void mi_cdecl mi_out_buf(const char* msg, void* arg) {
if (start+n >= MI_MAX_DELAY_OUTPUT) { if (start+n >= MI_MAX_DELAY_OUTPUT) {
n = MI_MAX_DELAY_OUTPUT-start-1; n = MI_MAX_DELAY_OUTPUT-start-1;
} }
_mi_memcpy(&out_buf[start], msg, n); mi_assert_internal(start + n <= MI_MAX_DELAY_OUTPUT);
_mi_memcpy(&mi_output_buffer[start], msg, n);
} }
static void mi_out_buf_flush(mi_output_fun* out, bool no_more_buf, void* arg) { static void mi_out_buf_flush(mi_output_fun* out, bool no_more_buf, void* arg) {
@ -354,10 +355,10 @@ static void mi_out_buf_flush(mi_output_fun* out, bool no_more_buf, void* arg) {
size_t count = mi_atomic_add_acq_rel(&out_len, (no_more_buf ? MI_MAX_DELAY_OUTPUT : 1)); size_t count = mi_atomic_add_acq_rel(&out_len, (no_more_buf ? MI_MAX_DELAY_OUTPUT : 1));
// and output the current contents // and output the current contents
if (count>MI_MAX_DELAY_OUTPUT) count = MI_MAX_DELAY_OUTPUT; if (count>MI_MAX_DELAY_OUTPUT) count = MI_MAX_DELAY_OUTPUT;
out_buf[count] = 0; mi_output_buffer[count] = 0;
out(out_buf,arg); out(mi_output_buffer,arg);
if (!no_more_buf) { if (!no_more_buf) {
out_buf[count] = '\n'; // if continue with the buffer, insert a newline mi_output_buffer[count] = '\n'; // if continue with the buffer, insert a newline
} }
} }

View file

@ -210,9 +210,7 @@ bool _mi_page_map_init(void) {
if (!mi_page_map_memid.initially_committed) { if (!mi_page_map_memid.initially_committed) {
_mi_os_commit(_mi_page_map[0], os_page_size, NULL); // only first OS page _mi_os_commit(_mi_page_map[0], os_page_size, NULL); // only first OS page
} }
_mi_page_map[0][0] = (mi_page_t*)&_mi_page_empty; // caught in `mi_free` mi_assert_internal(_mi_ptr_page(NULL)==NULL);
mi_assert_internal(_mi_ptr_page(NULL)==&_mi_page_empty);
return true; return true;
} }

View file

@ -227,12 +227,14 @@ static void mi_stat_peak_print(const mi_stat_count_t* stat, const char* msg, int
_mi_fprintf(out, arg, "\n"); _mi_fprintf(out, arg, "\n");
} }
#if MI_STAT>1
static void mi_stat_total_print(const mi_stat_count_t* stat, const char* msg, int64_t unit, mi_output_fun* out, void* arg) { static void mi_stat_total_print(const mi_stat_count_t* stat, const char* msg, int64_t unit, mi_output_fun* out, void* arg) {
_mi_fprintf(out, arg, "%10s:", msg); _mi_fprintf(out, arg, "%10s:", msg);
_mi_fprintf(out, arg, "%12s", " "); // no peak _mi_fprintf(out, arg, "%12s", " "); // no peak
mi_print_amount(stat->total, unit, out, arg); mi_print_amount(stat->total, unit, out, arg);
_mi_fprintf(out, arg, "\n"); _mi_fprintf(out, arg, "\n");
} }
#endif
static void mi_stat_counter_print(const mi_stat_counter_t* stat, const char* msg, mi_output_fun* out, void* arg ) { static void mi_stat_counter_print(const mi_stat_counter_t* stat, const char* msg, mi_output_fun* out, void* arg ) {
_mi_fprintf(out, arg, "%10s:", msg); _mi_fprintf(out, arg, "%10s:", msg);