diff --git a/doc/mimalloc-doc.h b/doc/mimalloc-doc.h index 4cf8c2c3..6078f415 100644 --- a/doc/mimalloc-doc.h +++ b/doc/mimalloc-doc.h @@ -811,9 +811,8 @@ typedef enum mi_option_e { mi_option_segment_cache, ///< The number of segments per thread to keep cached. mi_option_page_reset, ///< Reset page memory after \a mi_option_reset_delay milliseconds when it becomes free. mi_option_segment_reset, ///< Experimental - mi_option_reset_delay, ///< Delay in milli-seconds before resetting a page (100ms by default) + mi_option_decommit_delay, ///< Delay in milli-seconds before decommitting currently unused reserved memory (25ms by default) mi_option_use_numa_nodes, ///< Pretend there are at most N NUMA nodes - mi_option_reset_decommits, ///< Experimental mi_option_eager_commit_delay, ///< Experimental mi_option_os_tag, ///< OS tag to assign to mimalloc'd memory _mi_option_last @@ -1068,7 +1067,7 @@ or via environment variables. - `MIMALLOC_PAGE_RESET=0`: by default, mimalloc will reset (or purge) OS pages when not in use to signal to the OS that the underlying physical memory can be reused. This can reduce memory fragmentation in long running (server) programs. By setting it to `0` no such page resets will be done which can improve performance for programs that are not long - running. As an alternative, the `MIMALLOC_RESET_DELAY=` can be set higher (100ms by default) to make the page + running. As an alternative, the `MIMALLOC_DECOMMIT_DELAY=` can be set higher (100ms by default) to make the page reset occur less frequently instead of turning it off completely. - `MIMALLOC_LARGE_OS_PAGES=1`: use large OS pages (2MiB) when available; for some workloads this can significantly improve performance. Use `MIMALLOC_VERBOSE` to check if the large OS pages are enabled -- usually one needs diff --git a/include/mimalloc.h b/include/mimalloc.h index 98689d28..650948ea 100644 --- a/include/mimalloc.h +++ b/include/mimalloc.h @@ -306,27 +306,28 @@ typedef enum mi_option_e { mi_option_show_errors, mi_option_show_stats, mi_option_verbose, - // the following options are experimental + // some of the following options are experimental + // (deprecated options are kept for binary backward compatibility with v1.x versions) mi_option_eager_commit, - mi_option_eager_region_commit, - mi_option_reset_decommits, - mi_option_large_os_pages, // implies eager commit + mi_option_deprecated_eager_region_commit, + mi_option_deprecated_reset_decommits, + mi_option_large_os_pages, // implies eager commit mi_option_reserve_huge_os_pages, mi_option_reserve_huge_os_pages_at, mi_option_reserve_os_memory, mi_option_segment_cache, mi_option_page_reset, - mi_option_abandoned_page_reset, - mi_option_segment_reset, + mi_option_abandoned_page_decommit, + mi_option_deprecated_segment_reset, mi_option_eager_commit_delay, - mi_option_allow_decommit, - mi_option_reset_delay, - mi_option_segment_decommit_delay, + mi_option_decommit_delay, mi_option_use_numa_nodes, mi_option_limit_os_alloc, mi_option_os_tag, mi_option_max_errors, mi_option_max_warnings, + mi_option_allow_decommit, + mi_option_segment_decommit_delay, _mi_option_last } mi_option_t; diff --git a/src/options.c b/src/options.c index b8bac750..388be2e6 100644 --- a/src/options.c +++ b/src/options.c @@ -49,54 +49,50 @@ typedef struct mi_option_desc_s { mi_init_t init; // is it initialized yet? (from the environment) mi_option_t option; // for debugging: the option index should match the option const char* name; // option name without `mimalloc_` prefix + const char* legacy_name; // potential legacy v1.x option name } mi_option_desc_t; -#define MI_OPTION(opt) mi_option_##opt, #opt -#define MI_OPTION_DESC(opt) {0, UNINIT, MI_OPTION(opt) } +#define MI_OPTION(opt) mi_option_##opt, #opt, NULL +#define MI_OPTION_LEGACY(opt,legacy) mi_option_##opt, #opt, #legacy static mi_option_desc_t options[_mi_option_last] = { // stable options -#if MI_DEBUG || defined(MI_SHOW_ERRORS) + #if MI_DEBUG || defined(MI_SHOW_ERRORS) { 1, UNINIT, MI_OPTION(show_errors) }, -#else + #else { 0, UNINIT, MI_OPTION(show_errors) }, -#endif + #endif { 0, UNINIT, MI_OPTION(show_stats) }, { 0, UNINIT, MI_OPTION(verbose) }, - // the following options are experimental and not all combinations make sense. + // Some of the following options are experimental and not all combinations are valid. Use with care. { 1, UNINIT, MI_OPTION(eager_commit) }, // commit per segment directly (8MiB) (but see also `eager_commit_delay`) - #if defined(_WIN32) || (MI_INTPTR_SIZE <= 4) // and other OS's without overcommit? - { 0, UNINIT, MI_OPTION(eager_region_commit) }, - { 0, UNINIT, MI_OPTION(reset_decommits) }, // reset decommits memory - #else - { 1, UNINIT, MI_OPTION(eager_region_commit) }, - { 0, UNINIT, MI_OPTION(reset_decommits) }, // legacy; ignored now and reset always uses MADV_FREE/MADV_DONTNEED (issue #518) - #endif + { 0, UNINIT, MI_OPTION(deprecated_eager_region_commit) }, + { 0, UNINIT, MI_OPTION(deprecated_reset_decommits) }, { 0, UNINIT, MI_OPTION(large_os_pages) }, // use large OS pages, use only with eager commit to prevent fragmentation of VMA's { 0, UNINIT, MI_OPTION(reserve_huge_os_pages) }, // per 1GiB huge pages { -1, UNINIT, MI_OPTION(reserve_huge_os_pages_at) }, // reserve huge pages at node N { 0, UNINIT, MI_OPTION(reserve_os_memory) }, { 0, UNINIT, MI_OPTION(segment_cache) }, // cache N segments per thread { 0, UNINIT, MI_OPTION(page_reset) }, // reset page memory on free - { 0, UNINIT, MI_OPTION(abandoned_page_reset) },// reset free page memory when a thread terminates - { 0, UNINIT, MI_OPTION(segment_reset) }, // reset segment memory on free (needs eager commit) -#if defined(__NetBSD__) + { 0, UNINIT, MI_OPTION_LEGACY(abandoned_page_decommit, abandoned_page_reset) },// decommit free page memory when a thread terminates + { 0, UNINIT, MI_OPTION(deprecated_segment_reset) }, + #if defined(__NetBSD__) { 0, UNINIT, MI_OPTION(eager_commit_delay) }, // the first N segments per thread are not eagerly committed -#elif defined(_WIN32) + #elif defined(_WIN32) { 4, UNINIT, MI_OPTION(eager_commit_delay) }, // the first N segments per thread are not eagerly committed (but per page in the segment on demand) -#else + #else { 1, UNINIT, MI_OPTION(eager_commit_delay) }, // the first N segments per thread are not eagerly committed (but per page in the segment on demand) -#endif - { 1, UNINIT, MI_OPTION(allow_decommit) }, // decommit slices when no longer used (after reset_delay milli-seconds) - { 25, UNINIT, MI_OPTION(reset_delay) }, // page reset delay in milli-seconds (= decommit) - { 500, UNINIT, MI_OPTION(segment_decommit_delay) },// decommit delay in milli-seconds for freed segments + #endif + { 25, UNINIT, MI_OPTION_LEGACY(decommit_delay, reset_delay) }, // page decommit delay in milli-seconds { 0, UNINIT, MI_OPTION(use_numa_nodes) }, // 0 = use available numa nodes, otherwise use at most N nodes. { 0, UNINIT, MI_OPTION(limit_os_alloc) }, // 1 = do not use OS memory for allocation (but only reserved arenas) { 100, UNINIT, MI_OPTION(os_tag) }, // only apple specific for now but might serve more or less related purpose { 16, UNINIT, MI_OPTION(max_errors) }, // maximum errors that are output - { 16, UNINIT, MI_OPTION(max_warnings) } // maximum warnings that are output + { 16, UNINIT, MI_OPTION(max_warnings) }, // maximum warnings that are output + { 1, UNINIT, MI_OPTION(allow_decommit) }, // decommit slices when no longer used (after decommit_delay milli-seconds) + { 500, UNINIT, MI_OPTION(segment_decommit_delay) } // decommit delay in milli-seconds for freed segments }; static void mi_option_init(mi_option_desc_t* desc); @@ -526,11 +522,21 @@ static bool mi_getenv(const char* name, char* result, size_t result_size) { static void mi_option_init(mi_option_desc_t* desc) { // Read option value from the environment + char s[64+1]; char buf[64+1]; mi_strlcpy(buf, "mimalloc_", sizeof(buf)); mi_strlcat(buf, desc->name, sizeof(buf)); - char s[64+1]; - if (mi_getenv(buf, s, sizeof(s))) { + bool found = mi_getenv(buf,s,sizeof(s)); + if (!found && desc->legacy_name != NULL) { + mi_strlcpy(buf, "mimalloc_", sizeof(buf)); + mi_strlcat(buf, desc->legacy_name, sizeof(buf)); + found = mi_getenv(buf,s,sizeof(s)); + if (found) { + _mi_warning_message("environment option \"mimalloc_%s\" is deprecated -- use \"mimalloc_%s\" instead.\n", desc->legacy_name, desc->name ); + } + } + + if (found) { size_t len = strlen(s); if (len >= sizeof(buf)) len = sizeof(buf) - 1; for (size_t i = 0; i < len; i++) { diff --git a/src/segment.c b/src/segment.c index 980ca439..94c2f184 100644 --- a/src/segment.c +++ b/src/segment.c @@ -538,7 +538,7 @@ static bool mi_segment_commitx(mi_segment_t* segment, bool commit, uint8_t* p, s } // increase expiration of reusing part of the delayed decommit if (commit && mi_commit_mask_any_set(&segment->decommit_mask, &mask)) { - segment->decommit_expire = _mi_clock_now() + mi_option_get(mi_option_reset_delay); + segment->decommit_expire = _mi_clock_now() + mi_option_get(mi_option_decommit_delay); } // always undo delayed decommits mi_commit_mask_clear(&segment->decommit_mask, &mask); @@ -554,7 +554,7 @@ static bool mi_segment_ensure_committed(mi_segment_t* segment, uint8_t* p, size_ static void mi_segment_perhaps_decommit(mi_segment_t* segment, uint8_t* p, size_t size, mi_stats_t* stats) { if (!segment->allow_decommit) return; - if (mi_option_get(mi_option_reset_delay) == 0) { + if (mi_option_get(mi_option_decommit_delay) == 0) { mi_segment_commitx(segment, false, p, size, stats); } else { @@ -569,21 +569,21 @@ static void mi_segment_perhaps_decommit(mi_segment_t* segment, uint8_t* p, size_ mi_commit_mask_t cmask; mi_commit_mask_create_intersect(&segment->commit_mask, &mask, &cmask); // only decommit what is committed; span_free may try to decommit more mi_commit_mask_set(&segment->decommit_mask, &cmask); - segment->decommit_expire = _mi_clock_now() + mi_option_get(mi_option_reset_delay); + segment->decommit_expire = _mi_clock_now() + mi_option_get(mi_option_decommit_delay); mi_msecs_t now = _mi_clock_now(); if (segment->decommit_expire == 0) { // no previous decommits, initialize now mi_assert_internal(mi_commit_mask_is_empty(&segment->decommit_mask)); - segment->decommit_expire = now + mi_option_get(mi_option_reset_delay); + segment->decommit_expire = now + mi_option_get(mi_option_decommit_delay); } else if (segment->decommit_expire <= now) { // previous decommit mask already expired // mi_segment_delayed_decommit(segment, true, stats); - segment->decommit_expire = now + (mi_option_get(mi_option_reset_delay) / 8); // wait a tiny bit longer in case there is a series of free's + segment->decommit_expire = now + (mi_option_get(mi_option_decommit_delay) / 8); // wait a tiny bit longer in case there is a series of free's } else { // previous decommit mask is not yet expired - // segment->decommit_expire += 2; // = now + mi_option_get(mi_option_reset_delay); + // segment->decommit_expire += 2; // = now + mi_option_get(mi_option_decommit_delay); } } } @@ -877,7 +877,7 @@ static mi_segment_t* mi_segment_init(mi_segment_t* segment, size_t required, mi_ segment->commit_mask = commit_mask; // on lazy commit, the initial part is always committed segment->allow_decommit = (mi_option_is_enabled(mi_option_allow_decommit) && !segment->mem_is_pinned && !segment->mem_is_large); if (segment->allow_decommit) { - segment->decommit_expire = _mi_clock_now() + mi_option_get(mi_option_reset_delay); + segment->decommit_expire = _mi_clock_now() + mi_option_get(mi_option_decommit_delay); segment->decommit_mask = decommit_mask; mi_assert_internal(mi_commit_mask_all_set(&segment->commit_mask, &segment->decommit_mask)); #if MI_DEBUG>2 @@ -1245,7 +1245,7 @@ static void mi_segment_abandon(mi_segment_t* segment, mi_segments_tld_t* tld) { } // perform delayed decommits - mi_segment_delayed_decommit(segment, mi_option_is_enabled(mi_option_abandoned_page_reset) /* force? */, tld->stats); + mi_segment_delayed_decommit(segment, mi_option_is_enabled(mi_option_abandoned_page_decommit) /* force? */, tld->stats); // all pages in the segment are abandoned; add it to the abandoned list _mi_stat_increase(&tld->stats->segments_abandoned, 1);