mirror of
https://github.com/microsoft/mimalloc.git
synced 2025-08-24 00:04:48 +03:00
Compare commits
16 commits
a238d2bbbd
...
a067bd42ae
Author | SHA1 | Date | |
---|---|---|---|
![]() |
a067bd42ae | ||
![]() |
d389819cc9 | ||
![]() |
aaedb58c09 | ||
![]() |
2d34956bed | ||
![]() |
317b22cdb1 | ||
![]() |
c8c325c8cd | ||
![]() |
5115ddd6e6 | ||
![]() |
7512b0ef74 | ||
![]() |
93a003e2f1 | ||
![]() |
6c3d75a355 | ||
![]() |
21425bc334 | ||
![]() |
30a17bf1b7 | ||
![]() |
08b64e71a7 | ||
![]() |
7c7ecf096f | ||
![]() |
e19c022238 | ||
![]() |
715acc0329 |
14 changed files with 166 additions and 89 deletions
|
@ -6,10 +6,8 @@
|
|||
trigger:
|
||||
branches:
|
||||
include:
|
||||
- master
|
||||
- dev3
|
||||
- dev2
|
||||
- dev
|
||||
- main
|
||||
- dev*
|
||||
tags:
|
||||
include:
|
||||
- v*
|
||||
|
@ -196,35 +194,6 @@ jobs:
|
|||
# Other OS versions (just debug mode)
|
||||
# ----------------------------------------------------------
|
||||
|
||||
- job:
|
||||
displayName: Windows 2019
|
||||
pool:
|
||||
vmImage:
|
||||
windows-2019
|
||||
strategy:
|
||||
matrix:
|
||||
Debug:
|
||||
BuildType: debug
|
||||
cmakeExtraArgs: -DCMAKE_BUILD_TYPE=Debug -DMI_DEBUG_FULL=ON
|
||||
MSBuildConfiguration: Debug
|
||||
Release:
|
||||
BuildType: release
|
||||
cmakeExtraArgs: -DCMAKE_BUILD_TYPE=Release
|
||||
MSBuildConfiguration: Release
|
||||
steps:
|
||||
- task: CMake@1
|
||||
inputs:
|
||||
workingDirectory: $(BuildType)
|
||||
cmakeArgs: .. $(cmakeExtraArgs)
|
||||
- task: MSBuild@1
|
||||
inputs:
|
||||
solution: $(BuildType)/libmimalloc.sln
|
||||
configuration: '$(MSBuildConfiguration)'
|
||||
msbuildArguments: -m
|
||||
- script: ctest --verbose --timeout 240 -C $(MSBuildConfiguration)
|
||||
workingDirectory: $(BuildType)
|
||||
displayName: CTest
|
||||
|
||||
- job:
|
||||
displayName: Ubuntu 24.04
|
||||
pool:
|
||||
|
|
|
@ -8,7 +8,6 @@ terms of the MIT license. A copy of the license can be found in the file
|
|||
#ifndef MI_INTERNAL_H
|
||||
#define MI_INTERNAL_H
|
||||
|
||||
|
||||
// --------------------------------------------------------------------------
|
||||
// This file contains the internal API's of mimalloc and various utility
|
||||
// functions and macros.
|
||||
|
@ -18,6 +17,17 @@ terms of the MIT license. A copy of the license can be found in the file
|
|||
#include "track.h"
|
||||
#include "bits.h"
|
||||
|
||||
|
||||
// --------------------------------------------------------------------------
|
||||
// Compiler defines
|
||||
// --------------------------------------------------------------------------
|
||||
|
||||
#if (MI_DEBUG>0)
|
||||
#define mi_trace_message(...) _mi_trace_message(__VA_ARGS__)
|
||||
#else
|
||||
#define mi_trace_message(...)
|
||||
#endif
|
||||
|
||||
#define mi_decl_cache_align mi_decl_align(64)
|
||||
|
||||
#if defined(_MSC_VER)
|
||||
|
@ -26,26 +36,59 @@ terms of the MIT license. A copy of the license can be found in the file
|
|||
#define mi_decl_noinline __declspec(noinline)
|
||||
#define mi_decl_thread __declspec(thread)
|
||||
#define mi_decl_align(a) __declspec(align(a))
|
||||
#define mi_decl_noreturn __declspec(noreturn)
|
||||
#define mi_decl_weak
|
||||
#define mi_decl_hidden
|
||||
#define mi_decl_cold
|
||||
#elif (defined(__GNUC__) && (__GNUC__ >= 3)) || defined(__clang__) // includes clang and icc
|
||||
#define mi_decl_noinline __attribute__((noinline))
|
||||
#define mi_decl_thread __thread
|
||||
#define mi_decl_align(a) __attribute__((aligned(a)))
|
||||
#define mi_decl_noreturn __attribute__((noreturn))
|
||||
#define mi_decl_weak __attribute__((weak))
|
||||
#define mi_decl_hidden __attribute__((visibility("hidden")))
|
||||
#if (__GNUC__ >= 4) || defined(__clang__)
|
||||
#define mi_decl_cold __attribute__((cold))
|
||||
#else
|
||||
#define mi_decl_cold
|
||||
#endif
|
||||
#elif __cplusplus >= 201103L // c++11
|
||||
#define mi_decl_noinline
|
||||
#define mi_decl_thread thread_local
|
||||
#define mi_decl_cache_align alignas(MI_CACHE_LINE)
|
||||
#define mi_decl_align(a) alignas(a)
|
||||
#define mi_decl_noreturn [[noreturn]]
|
||||
#define mi_decl_weak
|
||||
#define mi_decl_hidden
|
||||
#define mi_decl_cold
|
||||
#else
|
||||
#define mi_decl_noinline
|
||||
#define mi_decl_thread __thread // hope for the best :-)
|
||||
#define mi_decl_align(a)
|
||||
#define mi_decl_noreturn
|
||||
#define mi_decl_weak
|
||||
#define mi_decl_hidden
|
||||
#define mi_decl_cold
|
||||
#endif
|
||||
|
||||
#if defined(__GNUC__) || defined(__clang__)
|
||||
#define mi_unlikely(x) (__builtin_expect(!!(x),false))
|
||||
#define mi_likely(x) (__builtin_expect(!!(x),true))
|
||||
#elif (defined(__cplusplus) && (__cplusplus >= 202002L)) || (defined(_MSVC_LANG) && _MSVC_LANG >= 202002L)
|
||||
#define mi_unlikely(x) (x) [[unlikely]]
|
||||
#define mi_likely(x) (x) [[likely]]
|
||||
#else
|
||||
#define mi_unlikely(x) (x)
|
||||
#define mi_likely(x) (x)
|
||||
#endif
|
||||
|
||||
#ifndef __has_builtin
|
||||
#define __has_builtin(x) 0
|
||||
#endif
|
||||
|
||||
#if defined(__cplusplus)
|
||||
#define mi_decl_externc extern "C"
|
||||
#else
|
||||
#define mi_decl_externc
|
||||
#endif
|
||||
|
||||
#if (defined(__GNUC__) && (__GNUC__ >= 7)) || defined(__clang__) // includes clang and icc
|
||||
|
@ -67,11 +110,10 @@ terms of the MIT license. A copy of the license can be found in the file
|
|||
#define __wasi__
|
||||
#endif
|
||||
|
||||
#if (MI_DEBUG>0)
|
||||
#define mi_trace_message(...) _mi_trace_message(__VA_ARGS__)
|
||||
#else
|
||||
#define mi_trace_message(...)
|
||||
#endif
|
||||
|
||||
// --------------------------------------------------------------------------
|
||||
// Internal functions
|
||||
// --------------------------------------------------------------------------
|
||||
|
||||
|
||||
// "libc.c"
|
||||
|
@ -135,7 +177,7 @@ void _mi_os_init(void); // c
|
|||
void* _mi_os_alloc(size_t size, mi_memid_t* memid);
|
||||
void* _mi_os_zalloc(size_t size, mi_memid_t* memid);
|
||||
void _mi_os_free(void* p, size_t size, mi_memid_t memid);
|
||||
void _mi_os_free_ex(void* p, size_t size, bool still_committed, mi_memid_t memid);
|
||||
void _mi_os_free_ex(void* p, size_t size, bool still_committed, mi_memid_t memid, mi_subproc_t* subproc );
|
||||
|
||||
size_t _mi_os_page_size(void);
|
||||
size_t _mi_os_guard_page_size(void);
|
||||
|
@ -146,6 +188,7 @@ size_t _mi_os_virtual_address_bits(void);
|
|||
|
||||
bool _mi_os_reset(void* addr, size_t size);
|
||||
bool _mi_os_decommit(void* addr, size_t size);
|
||||
void _mi_os_reuse(void* p, size_t size);
|
||||
mi_decl_nodiscard bool _mi_os_commit(void* p, size_t size, bool* is_zero);
|
||||
mi_decl_nodiscard bool _mi_os_commit_ex(void* addr, size_t size, bool* is_zero, size_t stat_size);
|
||||
mi_decl_nodiscard bool _mi_os_protect(void* addr, size_t size);
|
||||
|
@ -200,7 +243,7 @@ void _mi_page_map_register(mi_page_t* page);
|
|||
void _mi_page_map_unregister(mi_page_t* page);
|
||||
void _mi_page_map_unregister_range(void* start, size_t size);
|
||||
mi_page_t* _mi_safe_ptr_page(const void* p);
|
||||
void _mi_page_map_unsafe_destroy(void);
|
||||
void _mi_page_map_unsafe_destroy(mi_subproc_t* subproc);
|
||||
|
||||
// "page.c"
|
||||
void* _mi_malloc_generic(mi_heap_t* heap, size_t size, bool zero, size_t huge_alignment) mi_attr_noexcept mi_attr_malloc;
|
||||
|
@ -239,6 +282,7 @@ void _mi_heap_page_reclaim(mi_heap_t* heap, mi_page_t* page);
|
|||
// "stats.c"
|
||||
void _mi_stats_init(void);
|
||||
void _mi_stats_done(mi_stats_t* stats);
|
||||
void _mi_stats_print(mi_stats_t* stats, mi_output_fun* out, void* arg) mi_attr_noexcept;
|
||||
void _mi_stats_merge_thread(mi_tld_t* tld);
|
||||
void _mi_stats_merge_from(mi_stats_t* to, mi_stats_t* from);
|
||||
mi_msecs_t _mi_clock_now(void);
|
||||
|
@ -260,14 +304,13 @@ bool _mi_page_is_valid(mi_page_t* page);
|
|||
#endif
|
||||
|
||||
|
||||
|
||||
/* -----------------------------------------------------------
|
||||
Assertions
|
||||
----------------------------------------------------------- */
|
||||
// ------------------------------------------------------
|
||||
// Assertions
|
||||
// ------------------------------------------------------
|
||||
|
||||
#if (MI_DEBUG)
|
||||
// use our own assertion to print without memory allocation
|
||||
void _mi_assert_fail(const char* assertion, const char* fname, unsigned int line, const char* func);
|
||||
mi_decl_noreturn mi_decl_cold void _mi_assert_fail(const char* assertion, const char* fname, unsigned int line, const char* func) mi_attr_noexcept;
|
||||
#define mi_assert(expr) ((expr) ? (void)0 : _mi_assert_fail(#expr,__FILE__,__LINE__,__func__))
|
||||
#else
|
||||
#define mi_assert(x)
|
||||
|
@ -285,6 +328,7 @@ void _mi_assert_fail(const char* assertion, const char* fname, unsigned int line
|
|||
#define mi_assert_expensive(x)
|
||||
#endif
|
||||
|
||||
|
||||
/* -----------------------------------------------------------
|
||||
Statistics (in `stats.c`)
|
||||
----------------------------------------------------------- */
|
||||
|
@ -343,6 +387,8 @@ typedef struct mi_option_desc_s {
|
|||
const char* legacy_name; // potential legacy option name
|
||||
} mi_option_desc_t;
|
||||
|
||||
|
||||
|
||||
/* -----------------------------------------------------------
|
||||
Inlined definitions
|
||||
----------------------------------------------------------- */
|
||||
|
|
|
@ -63,6 +63,11 @@ int _mi_prim_decommit(void* addr, size_t size, bool* needs_recommit);
|
|||
// Returns error code or 0 on success.
|
||||
int _mi_prim_reset(void* addr, size_t size);
|
||||
|
||||
// Reuse memory. This is called for memory that is already committed but
|
||||
// may have been reset (`_mi_prim_reset`) or decommitted (`_mi_prim_decommit`) where `needs_recommit` was false.
|
||||
// Returns error code or 0 on success. On most platforms this is a no-op.
|
||||
int _mi_prim_reuse(void* addr, size_t size);
|
||||
|
||||
// Protect memory. Returns error code or 0 on success.
|
||||
int _mi_prim_protect(void* addr, size_t size, bool protect);
|
||||
|
||||
|
|
|
@ -72,15 +72,14 @@ Enjoy!
|
|||
|
||||
### Branches
|
||||
|
||||
* `master`: latest stable release (still based on `dev2`).
|
||||
* `main`: latest stable release (still based on `dev2`).
|
||||
* `dev`: development branch for mimalloc v1. Use this branch for submitting PR's.
|
||||
* `dev2`: development branch for mimalloc v2. This branch is downstream of `dev`
|
||||
(and is essentially equal to `dev` except for `src/segment.c`). Uses larger sliced segments to manage
|
||||
mimalloc pages that can reduce fragmentation.
|
||||
* `dev3`: development branch for mimalloc v3-beta. This branch is downstream of `dev`. This version
|
||||
simplifies the lock-free ownership of previous versions, has no thread-local segments any more.
|
||||
This improves sharing of memory between threads, and on certain large workloads may use less memory
|
||||
with less fragmentation.
|
||||
This improves sharing of memory between threads, and on certain large workloads may use (much) less memory.
|
||||
|
||||
### Releases
|
||||
|
||||
|
|
19
src/arena.c
19
src/arena.c
|
@ -76,7 +76,7 @@ size_t mi_arena_min_alignment(void) {
|
|||
return MI_ARENA_SLICE_ALIGN;
|
||||
}
|
||||
|
||||
static bool mi_arena_commit(mi_arena_t* arena, void* start, size_t size, bool* is_zero, size_t already_committed) {
|
||||
mi_decl_nodiscard static bool mi_arena_commit(mi_arena_t* arena, void* start, size_t size, bool* is_zero, size_t already_committed) {
|
||||
if (arena != NULL && arena->commit_fun != NULL) {
|
||||
return (*arena->commit_fun)(true, start, size, is_zero, arena->commit_fun_arg);
|
||||
}
|
||||
|
@ -216,12 +216,13 @@ static mi_decl_noinline void* mi_arena_try_alloc_at(
|
|||
}
|
||||
}
|
||||
else {
|
||||
// already fully commited.
|
||||
// already fully committed.
|
||||
_mi_os_reuse(p, mi_size_of_slices(slice_count));
|
||||
// if the OS has overcommit, and this is the first time we access these pages, then
|
||||
// count the commit now (as at arena reserve we didn't count those commits as these are on-demand)
|
||||
if (_mi_os_has_overcommit() && touched_slices > 0) {
|
||||
mi_subproc_stat_increase( arena->subproc, committed, mi_size_of_slices(touched_slices));
|
||||
}
|
||||
}
|
||||
}
|
||||
// tool support
|
||||
if (memid->initially_zero) {
|
||||
|
@ -677,7 +678,10 @@ static mi_page_t* mi_arenas_page_alloc_fresh(size_t slice_count, size_t block_si
|
|||
commit_size = _mi_align_up(block_start + block_size, MI_PAGE_MIN_COMMIT_SIZE);
|
||||
if (commit_size > page_noguard_size) { commit_size = page_noguard_size; }
|
||||
bool is_zero;
|
||||
mi_arena_commit( mi_memid_arena(memid), page, commit_size, &is_zero, 0);
|
||||
if (!mi_arena_commit( mi_memid_arena(memid), page, commit_size, &is_zero, 0)) {
|
||||
_mi_arenas_free(page, alloc_size, memid);
|
||||
return NULL;
|
||||
}
|
||||
if (!memid.initially_zero && !is_zero) {
|
||||
_mi_memzero_aligned(page, commit_size);
|
||||
}
|
||||
|
@ -1074,6 +1078,7 @@ bool _mi_arenas_contain(const void* p) {
|
|||
// destroy owned arenas; this is unsafe and should only be done using `mi_option_destroy_on_exit`
|
||||
// for dynamic libraries that are unloaded and need to release all their allocated memory.
|
||||
static void mi_arenas_unsafe_destroy(mi_subproc_t* subproc) {
|
||||
mi_assert_internal(subproc != NULL);
|
||||
const size_t max_arena = mi_arenas_get_count(subproc);
|
||||
size_t new_max_arena = 0;
|
||||
for (size_t i = 0; i < max_arena; i++) {
|
||||
|
@ -1082,7 +1087,7 @@ static void mi_arenas_unsafe_destroy(mi_subproc_t* subproc) {
|
|||
// mi_lock_done(&arena->abandoned_visit_lock);
|
||||
mi_atomic_store_ptr_release(mi_arena_t, &subproc->arenas[i], NULL);
|
||||
if (mi_memkind_is_os(arena->memid.memkind)) {
|
||||
_mi_os_free(mi_arena_start(arena), mi_arena_size(arena), arena->memid);
|
||||
_mi_os_free_ex(mi_arena_start(arena), mi_arena_size(arena), true, arena->memid, subproc); // pass `subproc` to avoid accessing the heap pointer (in `_mi_subproc()`)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1096,7 +1101,7 @@ static void mi_arenas_unsafe_destroy(mi_subproc_t* subproc) {
|
|||
// destroy owned arenas; this is unsafe and should only be done using `mi_option_destroy_on_exit`
|
||||
// for dynamic libraries that are unloaded and need to release all their allocated memory.
|
||||
void _mi_arenas_unsafe_destroy_all(mi_tld_t* tld) {
|
||||
mi_arenas_unsafe_destroy(_mi_subproc());
|
||||
mi_arenas_unsafe_destroy(tld->subproc);
|
||||
_mi_arenas_collect(true /* force purge */, true /* visit all*/, tld); // purge non-owned arenas
|
||||
}
|
||||
|
||||
|
@ -1304,7 +1309,7 @@ static int mi_reserve_os_memory_ex2(mi_subproc_t* subproc, size_t size, bool com
|
|||
void* start = _mi_os_alloc_aligned(size, MI_ARENA_SLICE_ALIGN, commit, allow_large, &memid);
|
||||
if (start == NULL) return ENOMEM;
|
||||
if (!mi_manage_os_memory_ex2(subproc, start, size, -1 /* numa node */, exclusive, memid, NULL, NULL, arena_id)) {
|
||||
_mi_os_free_ex(start, size, commit, memid);
|
||||
_mi_os_free_ex(start, size, commit, memid, NULL);
|
||||
_mi_verbose_message("failed to reserve %zu KiB memory\n", _mi_divide_up(size, 1024));
|
||||
return ENOMEM;
|
||||
}
|
||||
|
|
|
@ -779,11 +779,12 @@ void mi_cdecl _mi_process_done(void) {
|
|||
mi_heap_collect(heap, true /* force */);
|
||||
_mi_heap_unsafe_destroy_all(heap); // forcefully release all memory held by all heaps (of this thread only!)
|
||||
_mi_arenas_unsafe_destroy_all(heap->tld);
|
||||
_mi_page_map_unsafe_destroy();
|
||||
_mi_page_map_unsafe_destroy(_mi_subproc_main());
|
||||
}
|
||||
//_mi_page_map_unsafe_destroy(_mi_subproc_main());
|
||||
|
||||
if (mi_option_is_enabled(mi_option_show_stats) || mi_option_is_enabled(mi_option_verbose)) {
|
||||
mi_stats_print(NULL);
|
||||
_mi_stats_print(&_mi_subproc_main()->stats, NULL, NULL); // use always main subproc at process exit to avoid dereferencing the heap (as it may be destroyed by now)
|
||||
}
|
||||
_mi_allocator_done();
|
||||
_mi_verbose_message("process done: 0x%zx\n", tld_main.thread_id);
|
||||
|
|
|
@ -405,8 +405,10 @@ void mi_register_output(mi_output_fun* out, void* arg) mi_attr_noexcept {
|
|||
// add stderr to the delayed output after the module is loaded
|
||||
static void mi_add_stderr_output(void) {
|
||||
mi_assert_internal(mi_out_default == NULL);
|
||||
mi_out_buf_flush(&mi_out_stderr, false, NULL); // flush current contents to stderr
|
||||
mi_out_default = &mi_out_buf_stderr; // and add stderr to the delayed output
|
||||
if (mi_out_default==NULL) {
|
||||
mi_out_buf_flush(&mi_out_stderr, false, NULL); // flush current contents to stderr
|
||||
mi_out_default = &mi_out_buf_stderr; // and add stderr to the delayed output
|
||||
}
|
||||
}
|
||||
|
||||
// --------------------------------------------------------
|
||||
|
@ -544,7 +546,7 @@ void _mi_warning_message(const char* fmt, ...) {
|
|||
|
||||
|
||||
#if MI_DEBUG
|
||||
void _mi_assert_fail(const char* assertion, const char* fname, unsigned line, const char* func ) {
|
||||
mi_decl_noreturn mi_decl_cold void _mi_assert_fail(const char* assertion, const char* fname, unsigned line, const char* func ) mi_attr_noexcept {
|
||||
_mi_fprintf(NULL, NULL, "mimalloc: assertion failed: at \"%s\":%u, %s\n assertion: \"%s\"\n", fname, line, (func==NULL?"":func), assertion);
|
||||
abort();
|
||||
}
|
||||
|
|
42
src/os.c
42
src/os.c
|
@ -168,22 +168,23 @@ bool _mi_os_secure_guard_page_reset_before(void* addr, mi_memid_t memid) {
|
|||
Free memory
|
||||
-------------------------------------------------------------- */
|
||||
|
||||
static void mi_os_free_huge_os_pages(void* p, size_t size);
|
||||
static void mi_os_free_huge_os_pages(void* p, size_t size, mi_subproc_t* subproc);
|
||||
|
||||
static void mi_os_prim_free(void* addr, size_t size, size_t commit_size) {
|
||||
static void mi_os_prim_free(void* addr, size_t size, size_t commit_size, mi_subproc_t* subproc) {
|
||||
mi_assert_internal((size % _mi_os_page_size()) == 0);
|
||||
if (addr == NULL) return; // || _mi_os_is_huge_reserved(addr)
|
||||
int err = _mi_prim_free(addr, size); // allow size==0 (issue #1041)
|
||||
if (err != 0) {
|
||||
_mi_warning_message("unable to free OS memory (error: %d (0x%x), size: 0x%zx bytes, address: %p)\n", err, err, size, addr);
|
||||
}
|
||||
if (subproc == NULL) { subproc = _mi_subproc(); } // from `mi_arenas_unsafe_destroy` we pass subproc_main explicitly as we can no longer use the heap pointer
|
||||
if (commit_size > 0) {
|
||||
mi_os_stat_decrease(committed, commit_size);
|
||||
mi_subproc_stat_decrease(subproc, committed, commit_size);
|
||||
}
|
||||
mi_os_stat_decrease(reserved, size);
|
||||
mi_subproc_stat_decrease(subproc, reserved, size);
|
||||
}
|
||||
|
||||
void _mi_os_free_ex(void* addr, size_t size, bool still_committed, mi_memid_t memid) {
|
||||
void _mi_os_free_ex(void* addr, size_t size, bool still_committed, mi_memid_t memid, mi_subproc_t* subproc /* can be NULL */) {
|
||||
if (mi_memkind_is_os(memid.memkind)) {
|
||||
size_t csize = memid.mem.os.size;
|
||||
if (csize==0) { csize = _mi_os_good_alloc_size(size); }
|
||||
|
@ -204,10 +205,10 @@ void _mi_os_free_ex(void* addr, size_t size, bool still_committed, mi_memid_t me
|
|||
// free it
|
||||
if (memid.memkind == MI_MEM_OS_HUGE) {
|
||||
mi_assert(memid.is_pinned);
|
||||
mi_os_free_huge_os_pages(base, csize);
|
||||
mi_os_free_huge_os_pages(base, csize, subproc);
|
||||
}
|
||||
else {
|
||||
mi_os_prim_free(base, csize, (still_committed ? commit_size : 0));
|
||||
mi_os_prim_free(base, csize, (still_committed ? commit_size : 0), subproc);
|
||||
}
|
||||
}
|
||||
else {
|
||||
|
@ -217,7 +218,7 @@ void _mi_os_free_ex(void* addr, size_t size, bool still_committed, mi_memid_t me
|
|||
}
|
||||
|
||||
void _mi_os_free(void* p, size_t size, mi_memid_t memid) {
|
||||
_mi_os_free_ex(p, size, true, memid);
|
||||
_mi_os_free_ex(p, size, true, memid, NULL);
|
||||
}
|
||||
|
||||
|
||||
|
@ -292,7 +293,7 @@ static void* mi_os_prim_alloc_aligned(size_t size, size_t alignment, bool commit
|
|||
_mi_warning_message("unable to allocate aligned OS memory directly, fall back to over-allocation (size: 0x%zx bytes, address: %p, alignment: 0x%zx, commit: %d)\n", size, p, alignment, commit);
|
||||
}
|
||||
#endif
|
||||
if (p != NULL) { mi_os_prim_free(p, size, (commit ? size : 0)); }
|
||||
if (p != NULL) { mi_os_prim_free(p, size, (commit ? size : 0), NULL); }
|
||||
if (size >= (SIZE_MAX - alignment)) return NULL; // overflow
|
||||
const size_t over_size = size + alignment;
|
||||
|
||||
|
@ -310,7 +311,7 @@ static void* mi_os_prim_alloc_aligned(size_t size, size_t alignment, bool commit
|
|||
// explicitly commit only the aligned part
|
||||
if (commit) {
|
||||
if (!_mi_os_commit(p, size, NULL)) {
|
||||
mi_os_prim_free(*base, over_size, 0);
|
||||
mi_os_prim_free(*base, over_size, 0, NULL);
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
@ -326,8 +327,8 @@ static void* mi_os_prim_alloc_aligned(size_t size, size_t alignment, bool commit
|
|||
size_t mid_size = _mi_align_up(size, _mi_os_page_size());
|
||||
size_t post_size = over_size - pre_size - mid_size;
|
||||
mi_assert_internal(pre_size < over_size&& post_size < over_size&& mid_size >= size);
|
||||
if (pre_size > 0) { mi_os_prim_free(p, pre_size, (commit ? pre_size : 0)); }
|
||||
if (post_size > 0) { mi_os_prim_free((uint8_t*)aligned_p + mid_size, post_size, (commit ? post_size : 0)); }
|
||||
if (pre_size > 0) { mi_os_prim_free(p, pre_size, (commit ? pre_size : 0), NULL); }
|
||||
if (post_size > 0) { mi_os_prim_free((uint8_t*)aligned_p + mid_size, post_size, (commit ? post_size : 0), NULL); }
|
||||
// we can return the aligned pointer on `mmap` systems
|
||||
p = aligned_p;
|
||||
*base = aligned_p; // since we freed the pre part, `*base == p`.
|
||||
|
@ -531,6 +532,17 @@ bool _mi_os_reset(void* addr, size_t size) {
|
|||
}
|
||||
|
||||
|
||||
void _mi_os_reuse( void* addr, size_t size ) {
|
||||
// page align conservatively within the range
|
||||
size_t csize = 0;
|
||||
void* const start = mi_os_page_align_area_conservative(addr, size, &csize);
|
||||
if (csize == 0) return;
|
||||
const int err = _mi_prim_reuse(start, csize);
|
||||
if (err != 0) {
|
||||
_mi_warning_message("cannot reuse OS memory (error: %d (0x%x), address: %p, size: 0x%zx bytes)\n", err, err, start, csize);
|
||||
}
|
||||
}
|
||||
|
||||
// either resets or decommits memory, returns true if the memory needs
|
||||
// to be recommitted if it is to be re-used later on.
|
||||
bool _mi_os_purge_ex(void* p, size_t size, bool allow_reset, size_t stat_size, mi_commit_fun_t* commit_fun, void* commit_fun_arg)
|
||||
|
@ -668,7 +680,7 @@ void* _mi_os_alloc_huge_os_pages(size_t pages, int numa_node, mi_msecs_t max_mse
|
|||
// no success, issue a warning and break
|
||||
if (p != NULL) {
|
||||
_mi_warning_message("could not allocate contiguous huge OS page %zu at %p\n", page, addr);
|
||||
mi_os_prim_free(p, MI_HUGE_OS_PAGE_SIZE, MI_HUGE_OS_PAGE_SIZE);
|
||||
mi_os_prim_free(p, MI_HUGE_OS_PAGE_SIZE, MI_HUGE_OS_PAGE_SIZE, NULL);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
@ -710,11 +722,11 @@ void* _mi_os_alloc_huge_os_pages(size_t pages, int numa_node, mi_msecs_t max_mse
|
|||
|
||||
// free every huge page in a range individually (as we allocated per page)
|
||||
// note: needed with VirtualAlloc but could potentially be done in one go on mmap'd systems.
|
||||
static void mi_os_free_huge_os_pages(void* p, size_t size) {
|
||||
static void mi_os_free_huge_os_pages(void* p, size_t size, mi_subproc_t* subproc) {
|
||||
if (p==NULL || size==0) return;
|
||||
uint8_t* base = (uint8_t*)p;
|
||||
while (size >= MI_HUGE_OS_PAGE_SIZE) {
|
||||
mi_os_prim_free(base, MI_HUGE_OS_PAGE_SIZE, MI_HUGE_OS_PAGE_SIZE);
|
||||
mi_os_prim_free(base, MI_HUGE_OS_PAGE_SIZE, MI_HUGE_OS_PAGE_SIZE, subproc);
|
||||
size -= MI_HUGE_OS_PAGE_SIZE;
|
||||
base += MI_HUGE_OS_PAGE_SIZE;
|
||||
}
|
||||
|
|
|
@ -78,10 +78,11 @@ bool _mi_page_map_init(void) {
|
|||
return true;
|
||||
}
|
||||
|
||||
void _mi_page_map_unsafe_destroy(void) {
|
||||
void _mi_page_map_unsafe_destroy(mi_subproc_t* subproc) {
|
||||
mi_assert_internal(subproc != NULL);
|
||||
mi_assert_internal(_mi_page_map != NULL);
|
||||
if (_mi_page_map == NULL) return;
|
||||
_mi_os_free(mi_page_map_memid.mem.os.base, mi_page_map_memid.mem.os.size, mi_page_map_memid);
|
||||
_mi_os_free_ex(mi_page_map_memid.mem.os.base, mi_page_map_memid.mem.os.size, true, mi_page_map_memid, subproc);
|
||||
_mi_page_map = NULL;
|
||||
mi_page_map_commit = NULL;
|
||||
mi_page_map_max_address = NULL;
|
||||
|
@ -265,7 +266,8 @@ bool _mi_page_map_init(void) {
|
|||
}
|
||||
|
||||
|
||||
void _mi_page_map_unsafe_destroy(void) {
|
||||
void _mi_page_map_unsafe_destroy(mi_subproc_t* subproc) {
|
||||
mi_assert_internal(subproc != NULL);
|
||||
mi_assert_internal(_mi_page_map != NULL);
|
||||
if (_mi_page_map == NULL) return;
|
||||
for (size_t idx = 1; idx < mi_page_map_count; idx++) { // skip entry 0 (as we allocate that submap at the end of the page_map)
|
||||
|
@ -274,12 +276,12 @@ void _mi_page_map_unsafe_destroy(void) {
|
|||
mi_page_t** sub = _mi_page_map_at(idx);
|
||||
if (sub != NULL) {
|
||||
mi_memid_t memid = _mi_memid_create_os(sub, MI_PAGE_MAP_SUB_SIZE, true, false, false);
|
||||
_mi_os_free(memid.mem.os.base, memid.mem.os.size, memid);
|
||||
_mi_os_free_ex(memid.mem.os.base, memid.mem.os.size, true, memid, subproc);
|
||||
mi_atomic_store_ptr_release(mi_page_t*, &_mi_page_map[idx], NULL);
|
||||
}
|
||||
}
|
||||
}
|
||||
_mi_os_free(_mi_page_map, mi_page_map_memid.mem.os.size, mi_page_map_memid);
|
||||
_mi_os_free_ex(_mi_page_map, mi_page_map_memid.mem.os.size, true, mi_page_map_memid, subproc);
|
||||
_mi_page_map = NULL;
|
||||
mi_page_map_count = 0;
|
||||
mi_page_map_memid = _mi_memid_none();
|
||||
|
|
|
@ -114,6 +114,11 @@ int _mi_prim_reset(void* addr, size_t size) {
|
|||
return 0;
|
||||
}
|
||||
|
||||
int _mi_prim_reuse(void* addr, size_t size) {
|
||||
MI_UNUSED(addr); MI_UNUSED(size);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int _mi_prim_protect(void* addr, size_t size, bool protect) {
|
||||
MI_UNUSED(addr); MI_UNUSED(size); MI_UNUSED(protect);
|
||||
return 0;
|
||||
|
|
|
@ -434,13 +434,27 @@ int _mi_prim_commit(void* start, size_t size, bool* is_zero) {
|
|||
return err;
|
||||
}
|
||||
|
||||
int _mi_prim_reuse(void* start, size_t size) {
|
||||
#if defined(__APPLE__) && defined(MADV_FREE_REUSE)
|
||||
return unix_madvise(start, size, MADV_FREE_REUSE);
|
||||
#endif
|
||||
return 0;
|
||||
}
|
||||
|
||||
int _mi_prim_decommit(void* start, size_t size, bool* needs_recommit) {
|
||||
int err = 0;
|
||||
// decommit: use MADV_DONTNEED as it decreases rss immediately (unlike MADV_FREE)
|
||||
err = unix_madvise(start, size, MADV_DONTNEED);
|
||||
#if !MI_DEBUG && MI_SECURE<=2
|
||||
*needs_recommit = false;
|
||||
#if defined(__APPLE__) && defined(MADV_FREE_REUSABLE)
|
||||
// decommit on macOS: use MADV_FREE_REUSABLE as it does immediate rss accounting (issue #1097)
|
||||
err = unix_madvise(start, size, MADV_FREE_REUSABLE);
|
||||
#else
|
||||
// decommit: use MADV_DONTNEED as it decreases rss immediately (unlike MADV_FREE)
|
||||
err = unix_madvise(start, size, MADV_DONTNEED);
|
||||
#endif
|
||||
#else
|
||||
// note: don't use MADV_FREE_REUSABLE as the range may contain protected areas
|
||||
err = unix_madvise(start, size, MADV_DONTNEED);
|
||||
*needs_recommit = true;
|
||||
mprotect(start, size, PROT_NONE);
|
||||
#endif
|
||||
|
@ -455,14 +469,21 @@ int _mi_prim_decommit(void* start, size_t size, bool* needs_recommit) {
|
|||
}
|
||||
|
||||
int _mi_prim_reset(void* start, size_t size) {
|
||||
// We try to use `MADV_FREE` as that is the fastest. A drawback though is that it
|
||||
int err = 0;
|
||||
#if defined(__APPLE__) && defined(MADV_FREE_REUSABLE)
|
||||
// on macOS we try to use MADV_FREE_REUSABLE as it seems the fastest
|
||||
err = unix_madvise(start, size, MADV_FREE_REUSABLE);
|
||||
if (err == 0) return 0;
|
||||
// fall through
|
||||
#endif
|
||||
|
||||
#if defined(MADV_FREE)
|
||||
// Otherwise, we try to use `MADV_FREE` as that is the fastest. A drawback though is that it
|
||||
// will not reduce the `rss` stats in tools like `top` even though the memory is available
|
||||
// to other processes. With the default `MIMALLOC_PURGE_DECOMMITS=1` we ensure that by
|
||||
// default `MADV_DONTNEED` is used though.
|
||||
#if defined(MADV_FREE)
|
||||
static _Atomic(size_t) advice = MI_ATOMIC_VAR_INIT(MADV_FREE);
|
||||
int oadvice = (int)mi_atomic_load_relaxed(&advice);
|
||||
int err;
|
||||
while ((err = unix_madvise(start, size, oadvice)) != 0 && errno == EAGAIN) { errno = 0; };
|
||||
if (err != 0 && errno == EINVAL && oadvice == MADV_FREE) {
|
||||
// if MADV_FREE is not supported, fall back to MADV_DONTNEED from now on
|
||||
|
@ -470,7 +491,7 @@ int _mi_prim_reset(void* start, size_t size) {
|
|||
err = unix_madvise(start, size, MADV_DONTNEED);
|
||||
}
|
||||
#else
|
||||
int err = unix_madvise(start, size, MADV_DONTNEED);
|
||||
err = unix_madvise(start, size, MADV_DONTNEED);
|
||||
#endif
|
||||
return err;
|
||||
}
|
||||
|
|
|
@ -149,6 +149,11 @@ int _mi_prim_reset(void* addr, size_t size) {
|
|||
return 0;
|
||||
}
|
||||
|
||||
int _mi_prim_reuse(void* addr, size_t size) {
|
||||
MI_UNUSED(addr); MI_UNUSED(size);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int _mi_prim_protect(void* addr, size_t size, bool protect) {
|
||||
MI_UNUSED(addr); MI_UNUSED(size); MI_UNUSED(protect);
|
||||
return 0;
|
||||
|
|
|
@ -376,6 +376,11 @@ int _mi_prim_reset(void* addr, size_t size) {
|
|||
return (p != NULL ? 0 : (int)GetLastError());
|
||||
}
|
||||
|
||||
int _mi_prim_reuse(void* addr, size_t size) {
|
||||
MI_UNUSED(addr); MI_UNUSED(size);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int _mi_prim_protect(void* addr, size_t size, bool protect) {
|
||||
DWORD oldprotect = 0;
|
||||
BOOL ok = VirtualProtect(addr, size, protect ? PAGE_NOACCESS : PAGE_READWRITE, &oldprotect);
|
||||
|
|
|
@ -310,7 +310,7 @@ static void mi_cdecl mi_buffered_out(const char* msg, void* arg) {
|
|||
// Print statistics
|
||||
//------------------------------------------------------------
|
||||
|
||||
static void _mi_stats_print(mi_stats_t* stats, mi_output_fun* out0, void* arg0) mi_attr_noexcept {
|
||||
void _mi_stats_print(mi_stats_t* stats, mi_output_fun* out0, void* arg0) mi_attr_noexcept {
|
||||
// wrap the output function to be line buffered
|
||||
char buf[256]; _mi_memzero_var(buf);
|
||||
buffered_t buffer = { out0, arg0, NULL, 0, 255 };
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue