| FazBrowse GitHub Viewer | Trending | | Home |
| Tools: [Download Repo ZIP] [Original HTTPS Page] |
1 parent 568dacd commit 2e001e5
16 files changed
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -493,8 +493,8 @@ typedef enum mi_option_e { | |||
| 493 | 493 | mi_option_deprecated_visit_abandoned, // allow visiting theap blocks from abandoned threads (=0) | |
| 494 | 494 | mi_option_guarded_min, // only used when building with MI_GUARDED: minimal rounded object size for guarded objects (=0) | |
| 495 | 495 | mi_option_guarded_max, // only used when building with MI_GUARDED: maximal rounded object size for guarded objects (=0) | |
| 496 | - mi_option_guarded_precise, // disregard minimal alignment requirement to always place guarded blocks exactly in front of a guard page (=0) | ||
| 497 | - mi_option_guarded_sample_rate, // 1 out of N allocations in the min/max range will be guarded (=1000) | ||
| 496 | + mi_option_unsafe_guarded_precise, // disregard minimal alignment requirement to always place guarded blocks exactly in front of a guard page (=0) | ||
| 497 | + mi_option_guarded_sample_rate, // guard an object in the min/max range about every N requested bytes (=0) | ||
| 498 | 498 | mi_option_guarded_sample_seed, // can be set to allow for a (more) deterministic re-execution when a guard page is triggered (=0) | |
| 499 | 499 | mi_option_generic_collect, // collect theaps every N (=10000) generic allocation calls | |
| 500 | 500 | mi_option_page_reclaim_on_free, // heap-creation default for reclaim on free: -1 disallow, 0 originating theap only, 1 allow across theaps | |
@@ -510,10 +510,10 @@ typedef enum mi_option_e { | |||
| 510 | 510 | mi_option_arena_max_object_size, // set maximal object size that can be allocated in an arena (in KiB) (=2GiB on 64-bit). | |
| 511 | 511 | mi_option_arena_is_numa_local, // experimental: associate local numa node with an initial arena allocation | |
| 512 | 512 | mi_option_stats_merge_on_collect, // on each theap collection, the theap stats are merged automatically with the parent heap | |
| 513 | - mi_option_profile_alloc_interval, // number of allocated bytes between automatic dumps of the `MIMALLOC_PROFILE` heap profiler (internally, this value is in KiB; use `mi_option_get_size`) (=0, disabled) | ||
| 514 | - mi_option_profile_inuse_interval, // number of in-use bytes grown between automatic dumps of the `MIMALLOC_PROFILE` heap profiler (internally, this value is in KiB; use `mi_option_get_size`) (=0, disabled) | ||
| 515 | - mi_option_profile_time_interval, // number of seconds between automatic dumps of the `MIMALLOC_PROFILE` heap profiler (=0, disabled) | ||
| 516 | - mi_option_profile_sample_rate, // sample rate (in bytes) for the `MIMALLOC_PROFILE` heap profiler (internally, this value is in KiB; use `mi_option_get_size`) (=16 KiB) | ||
| 513 | + mi_option_profile_alloc_interval, // number of allocated bytes between automatic snapshots of the heap profiler (internally, this value is in KiB; use `mi_option_get_size`) (=1GiB, disabled) | ||
| 514 | + mi_option_profile_inuse_interval, // number of in-use bytes grown between automatic snapshots of the heap profiler (internally, this value is in KiB; use `mi_option_get_size`) (=0, disabled) | ||
| 515 | + mi_option_profile_time_interval, // number of seconds between automatic snapshots of the heap profiler (=0, disabled) | ||
| 516 | + mi_option_profile_sample_rate, // sample rate (in bytes) for heap profiler (internally, this value is in KiB; use `mi_option_get_size`) (=512 KiB) | ||
| 517 | 517 | mi_option_stats_merge_threshold, // on a page stats update, merge theap stats into the parent heap if N bytes were freed/allocated (=0, disabled) | |
| 518 | 518 | mi_option_arena_purge_immediate_size, // ranges above this size are purged immediately | |
| 519 | 519 | _mi_option_last, | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -28,7 +28,7 @@ mi_decl_nodiscard bool _mi_page_init(mi_theap_t* theap, mi_page_t* page); | |||
| 28 | 28 | void _mi_page_update_stats(mi_page_t* page); | |
| 29 | 29 | ||
| 30 | 30 | void _mi_theap_collect_retired(mi_theap_t* theap, bool force); | |
| 31 | - void _mi_theap_page_reclaim(mi_theap_t* theap, mi_page_t* page); | ||
| 31 | + bool _mi_theap_page_reclaim(mi_theap_t* theap, mi_page_t* page); // true if an empty page was freed | ||
| 32 | 32 | #if MI_DEBUG>1 | |
| 33 | 33 | bool _mi_page_is_valid(mi_page_t* page); | |
| 34 | 34 | #endif | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -73,14 +73,22 @@ terms of the MIT license. A copy of the license can be found in the file | |||
| 73 | 73 | #endif | |
| 74 | 74 | ||
| 75 | 75 | #if defined(__GNUC__) || defined(__clang__) | |
| 76 | - #define mi_unlikely(x) (__builtin_expect(!!(x),false)) | ||
| 77 | - #define mi_likely(x) (__builtin_expect(!!(x),true)) | ||
| 76 | + #define mi_unlikely(x) (__builtin_expect(!!(x),false)) | ||
| 77 | + #define mi_likely(x) (__builtin_expect(!!(x),true)) | ||
| 78 | 78 | #elif (defined(__cplusplus) && (__cplusplus >= 202002L)) || (defined(_MSVC_LANG) && _MSVC_LANG >= 202002L) | |
| 79 | - #define mi_unlikely(x) (x) [[unlikely]] | ||
| 80 | - #define mi_likely(x) (x) [[likely]] | ||
| 79 | + #define mi_unlikely(x) (x) [[unlikely]] | ||
| 80 | + #define mi_likely(x) (x) [[likely]] | ||
| 81 | 81 | #else | |
| 82 | - #define mi_unlikely(x) (x) | ||
| 83 | - #define mi_likely(x) (x) | ||
| 82 | + #define mi_unlikely(x) (x) | ||
| 83 | + #define mi_likely(x) (x) | ||
| 84 | + #endif | ||
| 85 | + | ||
| 86 | + #if (defined(__GNUC__) && (__GNUC__ >= 4)) || defined(__clang__) | ||
| 87 | + #define mi_prefetch_write(p) __builtin_prefetch(p, 1, 3) | ||
| 88 | + #define mi_prefetch_read(p) __builtin_prefetch(p, 0, 3) | ||
| 89 | + #else | ||
| 90 | + #define mi_prefetch_write(p) | ||
| 91 | + #define mi_prefetch_read(p) | ||
| 84 | 92 | #endif | |
| 85 | 93 | ||
| 86 | 94 | #if (defined(__GNUC__) && (__GNUC__ >= 7)) || defined(__clang__) | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -21,7 +21,7 @@ size_t _mi_os_guard_page_size(void); | |||
| 21 | 21 | size_t _mi_os_good_alloc_size(size_t size); | |
| 22 | 22 | bool _mi_os_has_overcommit(void); | |
| 23 | 23 | bool _mi_os_has_virtual_reserve(void); | |
| 24 | - bool _mi_os_canuse_thp(void); | ||
| 24 | + bool _mi_os_has_thp(void); | ||
| 25 | 25 | size_t _mi_os_virtual_address_bits(void); | |
| 26 | 26 | size_t _mi_os_minimal_purge_size(void); | |
| 27 | 27 | ||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -25,6 +25,7 @@ char* _mi_strnstr(char* s, size_t max_len, const char* pat); | |||
| 25 | 25 | const char* _mi_strchr(const char* s, char c); | |
| 26 | 26 | const char* _mi_strrchr(const char* s, char c); | |
| 27 | 27 | bool _mi_streq(const char* s, const char* t); | |
| 28 | + bool _mi_strntol(const char* s, size_t max_len, const char** end, long* result); // parses at most max_len bytes; no terminator required | ||
| 28 | 29 | int _mi_getenv(const char* name, char* result, size_t result_size); | |
| 29 | 30 | void _mi_detect_cpu_features(void); | |
| 30 | 31 | ||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -17,6 +17,9 @@ | |||
| 17 | 17 | // CPython never uses mimalloc's C++ operator new APIs, so don't define a weak | |
| 18 | 18 | // std::get_new_handler() (_ZSt15get_new_handlerv) in libpython. | |
| 19 | 19 | # define MI_NO_CXX_NEW_HANDLER 1 | |
| 20 | + // The aligned OS allocation fallback is benign, but its warning is printed to | ||
| 21 | + // stderr and breaks tests that check output (e.g. test_cmd_line under ASan). | ||
| 22 | + # define MI_NO_ALIGN_FALLBACK_WARNING 1 | ||
| 20 | 23 | # ifdef MS_WINDOWS | |
| 21 | 24 | // Initialize from a CRT initializer and use FLS for thread cleanup instead | |
| 22 | 25 | // of TLS callbacks, which run during DLL attach before the CRT is set up. | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -107,7 +107,13 @@ void mi_free_block_mt(mi_page_t* page, mi_block_t* block, bool was_guarded, bool | |||
| 107 | 107 | do { | |
| 108 | 108 | mi_block_set_next(page, block, mi_tf_block(tf_old)); | |
| 109 | 109 | tf_new = mi_tf_create(block, true); | |
| 110 | - } while (!mi_atomic_cas_weak_acq_rel(&page->xthread_free, &tf_old, tf_new)); | ||
| 110 | + if (mi_tf_is_owned(tf_old)) { | ||
| 111 | + if (mi_atomic_cas_weak_release(&page->xthread_free, &tf_old, tf_new)) break; // release is enough if we don't change ownership | ||
| 112 | + } | ||
| 113 | + else { | ||
| 114 | + if (mi_atomic_cas_weak_acq_rel(&page->xthread_free, &tf_old, tf_new)) break; // otherwise we need acquire-release | ||
| 115 | + } | ||
| 116 | + } while(true); | ||
| 111 | 117 | ||
| 112 | 118 | // and atomically try to collect the page if it was abandoned | |
| 113 | 119 | const bool is_newly_owned = mi_tf_is_owned(tf_new) && !mi_tf_is_owned(tf_old); | |
@@ -216,7 +222,7 @@ static mi_decl_forceinline bool mi_ptr_page_is_valid_ex(const void* p, const cha | |||
| 216 | 222 | { | |
| 217 | 223 | MI_UNUSED_RELEASE(msg); MI_UNUSED(free_small); | |
| 218 | 224 | #if MI_DEBUG | |
| 219 | - if mi_unlikely(!mi_is_aligned(p,MI_SIZE_SIZE) && !mi_option_is_enabled(mi_option_guarded_precise)) { | ||
| 225 | + if mi_unlikely(!mi_is_aligned(p,MI_SIZE_SIZE) && !mi_option_is_enabled(mi_option_unsafe_guarded_precise)) { | ||
| 220 | 226 | _mi_error_message(EINVAL, "%s: invalid (unaligned) pointer: %p\n", msg, p); | |
| 221 | 227 | return false; | |
| 222 | 228 | } | |
@@ -466,19 +472,29 @@ bool mi_cfree(void* p) mi_attr_noexcept { | |||
| 466 | 472 | // 3. if it went to < 7/8th used, re-abandon to be mapped (so it can be found by theaps looking for free pages) | |
| 467 | 473 | // -------------------------------------------------------------------------------------------- | |
| 468 | 474 | ||
| 469 | - // Consume an empty page, either freeing it or leaving it abandoned until its grace period ends. | ||
| 475 | + // Consume an empty page, reclaiming delayed pages if this thread has a theap for their heap. | ||
| 470 | 476 | static bool mi_abandoned_page_collect_empty(mi_page_t* page) | |
| 471 | 477 | { | |
| 472 | 478 | if (!mi_page_all_free(page)) return false; | |
| 473 | 479 | const bool should_delay = _mi_page_qsbr_should_delay(page); | |
| 474 | 480 | // first remove it from the abandoned pages in the arena (if mapped, this might wait for any readers to finish) | |
| 475 | 481 | _mi_arenas_page_unabandon(page,NULL); | |
| 476 | - if (!should_delay) { | ||
| 482 | + if mi_likely(!should_delay) { | ||
| 477 | 483 | _mi_page_qsbr_clear(page); | |
| 478 | 484 | _mi_arenas_page_free(page,NULL); | |
| 479 | 485 | } | |
| 480 | 486 | else { | |
| 481 | - _mi_arenas_page_abandon(page,NULL); | ||
| 487 | + mi_heap_t* const heap = mi_page_heap(page); | ||
| 488 | + mi_theap_t* const theap = mi_heap_theap(heap); | ||
| 489 | + if (theap != NULL) { | ||
| 490 | + // reclaim such that it gets part of the theap's qsbr list. | ||
| 491 | + _mi_theap_page_reclaim(theap, page); | ||
| 492 | + } | ||
| 493 | + else { | ||
| 494 | + mi_assert_internal(false); | ||
| 495 | + // abandon as empty; it can only be collected by _mi_heap_collect_abandoned at this point | ||
| 496 | + _mi_arenas_page_abandon(page,NULL); | ||
| 497 | + } | ||
| 482 | 498 | } | |
| 483 | 499 | return true; | |
| 484 | 500 | } | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -450,7 +450,7 @@ mi_block_t* _mi_page_free_collect_partly(mi_page_t* page, mi_block_t* head) { | |||
| 450 | 450 | ----------------------------------------------------------- */ | |
| 451 | 451 | ||
| 452 | 452 | // called from `mi_free` on a reclaim, and fresh_alloc if we get an abandoned page | |
| 453 | - void _mi_theap_page_reclaim(mi_theap_t* theap, mi_page_t* page) | ||
| 453 | + bool _mi_theap_page_reclaim(mi_theap_t* theap, mi_page_t* page) | ||
| 454 | 454 | { | |
| 455 | 455 | mi_assert_internal(mi_is_aligned(mi_page_slice_start(page), MI_PAGE_ALIGN)); | |
| 456 | 456 | mi_assert_internal(mi_ptr_page(mi_page_start(page))==page); | |
@@ -463,8 +463,8 @@ void _mi_theap_page_reclaim(mi_theap_t* theap, mi_page_t* page) | |||
| 463 | 463 | mi_page_queue_t* pq = mi_theap_page_queue_of(theap, page); | |
| 464 | 464 | mi_page_queue_push_at_end(theap, pq, page); | |
| 465 | 465 | if (!mi_page_all_free(page)) { _mi_page_qsbr_clear(page); } | |
| 466 | - else if (!_mi_page_qsbr_is_clear(page)) { mi_page_qsbr_enqueue(page, theap); } | ||
| 467 | 466 | mi_assert_expensive(_mi_page_is_valid(page)); | |
| 467 | + return (mi_page_all_free(page) && _mi_page_free(page, pq)); | ||
| 468 | 468 | } | |
| 469 | 469 | ||
| 470 | 470 | void _mi_page_abandon(mi_page_t* page, mi_page_queue_t* pq) { | |
@@ -488,13 +488,17 @@ mi_page_t* _mi_page_fresh_alloc(mi_theap_t* theap, mi_page_queue_t* pq, size_t b | |||
| 488 | 488 | mi_assert_internal(mi_theap_contains_queue(theap, pq)); | |
| 489 | 489 | mi_assert_internal(page_alignment > 0 || block_size > MI_LARGE_MAX_OBJ_SIZE || block_size == pq->block_size); | |
| 490 | 490 | #endif | |
| 491 | - mi_page_t* page = _mi_arenas_page_alloc(theap, block_size, page_alignment); | ||
| 492 | - if (page == NULL) { | ||
| 493 | - // out-of-memory | ||
| 494 | - return NULL; | ||
| 495 | - } | ||
| 496 | - if (mi_page_is_abandoned(page)) { | ||
| 497 | - _mi_theap_page_reclaim(theap, page); | ||
| 491 | + mi_page_t* page; | ||
| 492 | + bool reclaimed; | ||
| 493 | + do { | ||
| 494 | + page = _mi_arenas_page_alloc(theap, block_size, page_alignment); | ||
| 495 | + if (page == NULL) { | ||
| 496 | + // out-of-memory | ||
| 497 | + return NULL; | ||
| 498 | + } | ||
| 499 | + reclaimed = mi_page_is_abandoned(page); | ||
| 500 | + } while (reclaimed && _mi_theap_page_reclaim(theap, page)); | ||
| 501 | + if (reclaimed) { | ||
| 498 | 502 | if (!mi_page_immediate_available(page)) { | |
| 499 | 503 | if (mi_page_is_expandable(page)) { | |
| 500 | 504 | if (!_mi_page_extend_free(theap, page)) { | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -8,7 +8,7 @@ terms of the MIT license. A copy of the license can be found in the file | |||
| 8 | 8 | #ifndef MI_PAGE_H | |
| 9 | 9 | #define MI_PAGE_H | |
| 10 | 10 | ||
| 11 | - #include <mimalloc.h> | ||
| 11 | + #include "mimalloc.h" | ||
| 12 | 12 | #include "page-queue.h" | |
| 13 | 13 | ||
| 14 | 14 | bool _mi_page_extend_free(mi_theap_t* theap, mi_page_t* page); | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -220,7 +220,7 @@ mi_theap_t* mi_theap_set_default(mi_theap_t* theap) { | |||
| 220 | 220 | ||
| 221 | 221 | static void mi_theap_options_init(mi_theap_t* theap, mi_heap_t* heap) { | |
| 222 | 222 | theap->allow_page_reclaim = (mi_heap_reclaim_on_free(heap) >= 0); | |
| 223 | - theap->allow_page_abandon = (mi_option_get(mi_option_page_full_retain) >= 0 && !mi_heap_qsbr_is_enabled(heap)); | ||
| 223 | + theap->allow_page_abandon = (mi_option_get(mi_option_page_full_retain) >= 0); // nowadays qsbr pages can be abandoned: && !mi_heap_qsbr_is_enabled(heap)); | ||
| 224 | 224 | theap->page_full_retain = mi_option_get_clamp(mi_option_page_full_retain, -1, 32); | |
| 225 | 225 | theap->is_detached = (theap->tld->thread_id == MI_THREADID_DETACHED); | |
| 226 | 226 | } | |
| Back | FazBrowse Home | New Git URL |
0 commit comments