Add experimental API: smallocx_return_t smallocx(size, flags)
--- Motivation: This new experimental memory-allocaction API returns a pointer to the allocation as well as the usable size of the allocated memory region. The `s` in `smallocx` stands for `sized`-`mallocx`, attempting to convey that this API returns the size of the allocated memory region. It should allow C++ P0901r0 [0] and Rust Alloc::alloc_excess to make use of it. The main purpose of these APIs is to improve telemetry. It is more accurate to register `smallocx(size, flags)` than `smallocx(nallocx(size), flags)`, for example. The latter will always line up perfectly with the existing size classes, causing a loss of telemetry information about the internal fragmentation induced by potentially poor size-classes choices. Instrumenting `nallocx` does not help much since user code can cache its result and use it repeatedly. --- Implementation: The implementation adds a new `usize` option to `static_opts_s` and an `usize` variable to `dynamic_opts_s`. These are then used to cache the result of `sz_index2size` and similar functions in the code paths in which they are unconditionally invoked. In the code-paths in which these functions are not unconditionally invoked, `smallocx` calls, as opposed to `mallocx`, these functions explicitly. --- [0]: http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2018/p0901r0.html
This commit is contained in:
parent
325e3305fc
commit
08260a6b94
19
configure.ac
19
configure.ac
@ -850,7 +850,7 @@ AC_ARG_WITH([export],
|
|||||||
fi]
|
fi]
|
||||||
)
|
)
|
||||||
|
|
||||||
public_syms="aligned_alloc calloc dallocx free mallctl mallctlbymib mallctlnametomib malloc malloc_conf malloc_message malloc_stats_print malloc_usable_size mallocx nallocx posix_memalign rallocx realloc sallocx sdallocx xallocx"
|
public_syms="aligned_alloc calloc dallocx free mallctl mallctlbymib mallctlnametomib malloc malloc_conf malloc_message malloc_stats_print malloc_usable_size mallocx smallocx nallocx posix_memalign rallocx realloc sallocx sdallocx xallocx"
|
||||||
dnl Check for additional platform-specific public API functions.
|
dnl Check for additional platform-specific public API functions.
|
||||||
AC_CHECK_FUNC([memalign],
|
AC_CHECK_FUNC([memalign],
|
||||||
[AC_DEFINE([JEMALLOC_OVERRIDE_MEMALIGN], [ ])
|
[AC_DEFINE([JEMALLOC_OVERRIDE_MEMALIGN], [ ])
|
||||||
@ -1043,6 +1043,22 @@ if test "x$enable_stats" = "x1" ; then
|
|||||||
fi
|
fi
|
||||||
AC_SUBST([enable_stats])
|
AC_SUBST([enable_stats])
|
||||||
|
|
||||||
|
dnl Do not enable smallocx by default.
|
||||||
|
AC_ARG_ENABLE([experimental_smallocx],
|
||||||
|
[AS_HELP_STRING([--enable-experimental-smallocx], [Enable experimental smallocx API])],
|
||||||
|
[if test "x$enable_experimental_smallocx" = "xno" ; then
|
||||||
|
enable_experimental_smallocx="0"
|
||||||
|
else
|
||||||
|
enable_experimental_smallocx="1"
|
||||||
|
fi
|
||||||
|
],
|
||||||
|
[enable_experimental_smallocx="0"]
|
||||||
|
)
|
||||||
|
if test "x$enable_experimental_smallocx" = "x1" ; then
|
||||||
|
AC_DEFINE([JEMALLOC_EXPERIMENTAL_SMALLOCX_API])
|
||||||
|
fi
|
||||||
|
AC_SUBST([enable_experimental_smallocx])
|
||||||
|
|
||||||
dnl Do not enable profiling by default.
|
dnl Do not enable profiling by default.
|
||||||
AC_ARG_ENABLE([prof],
|
AC_ARG_ENABLE([prof],
|
||||||
[AS_HELP_STRING([--enable-prof], [Enable allocation profiling])],
|
[AS_HELP_STRING([--enable-prof], [Enable allocation profiling])],
|
||||||
@ -2281,6 +2297,7 @@ AC_MSG_RESULT([malloc_conf : ${config_malloc_conf}])
|
|||||||
AC_MSG_RESULT([autogen : ${enable_autogen}])
|
AC_MSG_RESULT([autogen : ${enable_autogen}])
|
||||||
AC_MSG_RESULT([debug : ${enable_debug}])
|
AC_MSG_RESULT([debug : ${enable_debug}])
|
||||||
AC_MSG_RESULT([stats : ${enable_stats}])
|
AC_MSG_RESULT([stats : ${enable_stats}])
|
||||||
|
AC_MSG_RESULT([experimetal_smallocx : ${enable_experimental_smallocx}])
|
||||||
AC_MSG_RESULT([prof : ${enable_prof}])
|
AC_MSG_RESULT([prof : ${enable_prof}])
|
||||||
AC_MSG_RESULT([prof-libunwind : ${enable_prof_libunwind}])
|
AC_MSG_RESULT([prof-libunwind : ${enable_prof_libunwind}])
|
||||||
AC_MSG_RESULT([prof-libgcc : ${enable_prof_libgcc}])
|
AC_MSG_RESULT([prof-libgcc : ${enable_prof_libgcc}])
|
||||||
|
@ -153,6 +153,9 @@
|
|||||||
/* JEMALLOC_STATS enables statistics calculation. */
|
/* JEMALLOC_STATS enables statistics calculation. */
|
||||||
#undef JEMALLOC_STATS
|
#undef JEMALLOC_STATS
|
||||||
|
|
||||||
|
/* JEMALLOC_EXPERIMENTAL_SMALLOCX_API enables experimental smallocx API. */
|
||||||
|
#undef JEMALLOC_EXPERIMENTAL_SMALLOCX_API
|
||||||
|
|
||||||
/* JEMALLOC_PROF enables allocation profiling. */
|
/* JEMALLOC_PROF enables allocation profiling. */
|
||||||
#undef JEMALLOC_PROF
|
#undef JEMALLOC_PROF
|
||||||
|
|
||||||
|
@ -28,6 +28,10 @@ JEMALLOC_EXPORT void JEMALLOC_NOTHROW @je_@free(void *ptr)
|
|||||||
JEMALLOC_EXPORT JEMALLOC_ALLOCATOR JEMALLOC_RESTRICT_RETURN
|
JEMALLOC_EXPORT JEMALLOC_ALLOCATOR JEMALLOC_RESTRICT_RETURN
|
||||||
void JEMALLOC_NOTHROW *@je_@mallocx(size_t size, int flags)
|
void JEMALLOC_NOTHROW *@je_@mallocx(size_t size, int flags)
|
||||||
JEMALLOC_ATTR(malloc) JEMALLOC_ALLOC_SIZE(1);
|
JEMALLOC_ATTR(malloc) JEMALLOC_ALLOC_SIZE(1);
|
||||||
|
#ifdef JEMALLOC_EXPERIMENTAL_SMALLOCX_API
|
||||||
|
JEMALLOC_EXPORT JEMALLOC_ALLOCATOR JEMALLOC_RESTRICT_RETURN
|
||||||
|
smallocx_return_t JEMALLOC_NOTHROW @je_@smallocx(size_t size, int flags);
|
||||||
|
#endif
|
||||||
JEMALLOC_EXPORT JEMALLOC_ALLOCATOR JEMALLOC_RESTRICT_RETURN
|
JEMALLOC_EXPORT JEMALLOC_ALLOCATOR JEMALLOC_RESTRICT_RETURN
|
||||||
void JEMALLOC_NOTHROW *@je_@rallocx(void *ptr, size_t size,
|
void JEMALLOC_NOTHROW *@je_@rallocx(void *ptr, size_t size,
|
||||||
int flags) JEMALLOC_ALLOC_SIZE(2);
|
int flags) JEMALLOC_ALLOC_SIZE(2);
|
||||||
|
@ -75,3 +75,10 @@ struct extent_hooks_s {
|
|||||||
extent_split_t *split;
|
extent_split_t *split;
|
||||||
extent_merge_t *merge;
|
extent_merge_t *merge;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
#ifdef JEMALLOC_EXPERIMENTAL_SMALLOCX_API
|
||||||
|
typedef struct {
|
||||||
|
void *ptr;
|
||||||
|
size_t size;
|
||||||
|
} smallocx_return_t;
|
||||||
|
#endif
|
||||||
|
@ -1747,6 +1747,11 @@ struct static_opts_s {
|
|||||||
* initialization) options.
|
* initialization) options.
|
||||||
*/
|
*/
|
||||||
bool slow;
|
bool slow;
|
||||||
|
/*
|
||||||
|
* Return size
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
bool usize;
|
||||||
};
|
};
|
||||||
|
|
||||||
JEMALLOC_ALWAYS_INLINE void
|
JEMALLOC_ALWAYS_INLINE void
|
||||||
@ -1760,6 +1765,7 @@ static_opts_init(static_opts_t *static_opts) {
|
|||||||
static_opts->oom_string = "";
|
static_opts->oom_string = "";
|
||||||
static_opts->invalid_alignment_string = "";
|
static_opts->invalid_alignment_string = "";
|
||||||
static_opts->slow = false;
|
static_opts->slow = false;
|
||||||
|
static_opts->usize = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -1774,6 +1780,7 @@ static_opts_init(static_opts_t *static_opts) {
|
|||||||
typedef struct dynamic_opts_s dynamic_opts_t;
|
typedef struct dynamic_opts_s dynamic_opts_t;
|
||||||
struct dynamic_opts_s {
|
struct dynamic_opts_s {
|
||||||
void **result;
|
void **result;
|
||||||
|
size_t usize;
|
||||||
size_t num_items;
|
size_t num_items;
|
||||||
size_t item_size;
|
size_t item_size;
|
||||||
size_t alignment;
|
size_t alignment;
|
||||||
@ -1785,6 +1792,7 @@ struct dynamic_opts_s {
|
|||||||
JEMALLOC_ALWAYS_INLINE void
|
JEMALLOC_ALWAYS_INLINE void
|
||||||
dynamic_opts_init(dynamic_opts_t *dynamic_opts) {
|
dynamic_opts_init(dynamic_opts_t *dynamic_opts) {
|
||||||
dynamic_opts->result = NULL;
|
dynamic_opts->result = NULL;
|
||||||
|
dynamic_opts->usize = 0;
|
||||||
dynamic_opts->num_items = 0;
|
dynamic_opts->num_items = 0;
|
||||||
dynamic_opts->item_size = 0;
|
dynamic_opts->item_size = 0;
|
||||||
dynamic_opts->alignment = 0;
|
dynamic_opts->alignment = 0;
|
||||||
@ -1960,13 +1968,15 @@ imalloc_body(static_opts_t *sopts, dynamic_opts_t *dopts, tsd_t *tsd) {
|
|||||||
if (unlikely(ind >= SC_NSIZES)) {
|
if (unlikely(ind >= SC_NSIZES)) {
|
||||||
goto label_oom;
|
goto label_oom;
|
||||||
}
|
}
|
||||||
if (config_stats || (config_prof && opt_prof)) {
|
if (config_stats || (config_prof && opt_prof) || sopts->usize) {
|
||||||
usize = sz_index2size(ind);
|
usize = sz_index2size(ind);
|
||||||
|
dopts->usize = usize;
|
||||||
assert(usize > 0 && usize
|
assert(usize > 0 && usize
|
||||||
<= SC_LARGE_MAXCLASS);
|
<= SC_LARGE_MAXCLASS);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
usize = sz_sa2u(size, dopts->alignment);
|
usize = sz_sa2u(size, dopts->alignment);
|
||||||
|
dopts->usize = usize;
|
||||||
if (unlikely(usize == 0
|
if (unlikely(usize == 0
|
||||||
|| usize > SC_LARGE_MAXCLASS)) {
|
|| usize > SC_LARGE_MAXCLASS)) {
|
||||||
goto label_oom;
|
goto label_oom;
|
||||||
@ -2759,6 +2769,71 @@ int __posix_memalign(void** r, size_t a, size_t s) PREALIAS(je_posix_memalign);
|
|||||||
* Begin non-standard functions.
|
* Begin non-standard functions.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
#ifdef JEMALLOC_EXPERIMENTAL_SMALLOCX_API
|
||||||
|
JEMALLOC_EXPORT JEMALLOC_ALLOCATOR JEMALLOC_RESTRICT_RETURN
|
||||||
|
smallocx_return_t JEMALLOC_NOTHROW
|
||||||
|
/*
|
||||||
|
* The attribute JEMALLOC_ATTR(malloc) cannot be used due to:
|
||||||
|
* - https://gcc.gnu.org/bugzilla/show_bug.cgi?id=86488
|
||||||
|
*/
|
||||||
|
je_smallocx(size_t size, int flags) {
|
||||||
|
/*
|
||||||
|
* Note: the attribute JEMALLOC_ALLOC_SIZE(1) cannot be
|
||||||
|
* used here because it makes writing beyond the `size`
|
||||||
|
* of the `ptr` undefined behavior, but the objective
|
||||||
|
* of this function is to allow writing beyond `size`
|
||||||
|
* up to `smallocx_return_t::size`.
|
||||||
|
*/
|
||||||
|
smallocx_return_t ret;
|
||||||
|
static_opts_t sopts;
|
||||||
|
dynamic_opts_t dopts;
|
||||||
|
|
||||||
|
LOG("core.smallocx.entry", "size: %zu, flags: %d", size, flags);
|
||||||
|
|
||||||
|
static_opts_init(&sopts);
|
||||||
|
dynamic_opts_init(&dopts);
|
||||||
|
|
||||||
|
sopts.assert_nonempty_alloc = true;
|
||||||
|
sopts.null_out_result_on_error = true;
|
||||||
|
sopts.oom_string = "<jemalloc>: Error in mallocx(): out of memory\n";
|
||||||
|
sopts.usize = true;
|
||||||
|
|
||||||
|
dopts.result = &ret.ptr;
|
||||||
|
dopts.num_items = 1;
|
||||||
|
dopts.item_size = size;
|
||||||
|
if (unlikely(flags != 0)) {
|
||||||
|
if ((flags & MALLOCX_LG_ALIGN_MASK) != 0) {
|
||||||
|
dopts.alignment = MALLOCX_ALIGN_GET_SPECIFIED(flags);
|
||||||
|
}
|
||||||
|
|
||||||
|
dopts.zero = MALLOCX_ZERO_GET(flags);
|
||||||
|
|
||||||
|
if ((flags & MALLOCX_TCACHE_MASK) != 0) {
|
||||||
|
if ((flags & MALLOCX_TCACHE_MASK)
|
||||||
|
== MALLOCX_TCACHE_NONE) {
|
||||||
|
dopts.tcache_ind = TCACHE_IND_NONE;
|
||||||
|
} else {
|
||||||
|
dopts.tcache_ind = MALLOCX_TCACHE_GET(flags);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
dopts.tcache_ind = TCACHE_IND_AUTOMATIC;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((flags & MALLOCX_ARENA_MASK) != 0)
|
||||||
|
dopts.arena_ind = MALLOCX_ARENA_GET(flags);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
imalloc(&sopts, &dopts);
|
||||||
|
assert(dopts.usize == je_nallocx(size, flags));
|
||||||
|
ret.size = dopts.usize;
|
||||||
|
|
||||||
|
LOG("core.smallocx.exit", "result: %p, size: %zu", ret.ptr, ret.size);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
JEMALLOC_EXPORT JEMALLOC_ALLOCATOR JEMALLOC_RESTRICT_RETURN
|
JEMALLOC_EXPORT JEMALLOC_ALLOCATOR JEMALLOC_RESTRICT_RETURN
|
||||||
void JEMALLOC_NOTHROW *
|
void JEMALLOC_NOTHROW *
|
||||||
JEMALLOC_ATTR(malloc) JEMALLOC_ALLOC_SIZE(1)
|
JEMALLOC_ATTR(malloc) JEMALLOC_ALLOC_SIZE(1)
|
||||||
|
Loading…
Reference in New Issue
Block a user