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]
|
||||
)
|
||||
|
||||
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.
|
||||
AC_CHECK_FUNC([memalign],
|
||||
[AC_DEFINE([JEMALLOC_OVERRIDE_MEMALIGN], [ ])
|
||||
@ -1043,6 +1043,22 @@ if test "x$enable_stats" = "x1" ; then
|
||||
fi
|
||||
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.
|
||||
AC_ARG_ENABLE([prof],
|
||||
[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([debug : ${enable_debug}])
|
||||
AC_MSG_RESULT([stats : ${enable_stats}])
|
||||
AC_MSG_RESULT([experimetal_smallocx : ${enable_experimental_smallocx}])
|
||||
AC_MSG_RESULT([prof : ${enable_prof}])
|
||||
AC_MSG_RESULT([prof-libunwind : ${enable_prof_libunwind}])
|
||||
AC_MSG_RESULT([prof-libgcc : ${enable_prof_libgcc}])
|
||||
|
@ -153,6 +153,9 @@
|
||||
/* JEMALLOC_STATS enables statistics calculation. */
|
||||
#undef JEMALLOC_STATS
|
||||
|
||||
/* JEMALLOC_EXPERIMENTAL_SMALLOCX_API enables experimental smallocx API. */
|
||||
#undef JEMALLOC_EXPERIMENTAL_SMALLOCX_API
|
||||
|
||||
/* JEMALLOC_PROF enables allocation profiling. */
|
||||
#undef JEMALLOC_PROF
|
||||
|
||||
|
@ -28,6 +28,10 @@ JEMALLOC_EXPORT void JEMALLOC_NOTHROW @je_@free(void *ptr)
|
||||
JEMALLOC_EXPORT JEMALLOC_ALLOCATOR JEMALLOC_RESTRICT_RETURN
|
||||
void JEMALLOC_NOTHROW *@je_@mallocx(size_t size, int flags)
|
||||
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
|
||||
void JEMALLOC_NOTHROW *@je_@rallocx(void *ptr, size_t size,
|
||||
int flags) JEMALLOC_ALLOC_SIZE(2);
|
||||
|
@ -75,3 +75,10 @@ struct extent_hooks_s {
|
||||
extent_split_t *split;
|
||||
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.
|
||||
*/
|
||||
bool slow;
|
||||
/*
|
||||
* Return size
|
||||
*
|
||||
*/
|
||||
bool usize;
|
||||
};
|
||||
|
||||
JEMALLOC_ALWAYS_INLINE void
|
||||
@ -1760,6 +1765,7 @@ static_opts_init(static_opts_t *static_opts) {
|
||||
static_opts->oom_string = "";
|
||||
static_opts->invalid_alignment_string = "";
|
||||
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;
|
||||
struct dynamic_opts_s {
|
||||
void **result;
|
||||
size_t usize;
|
||||
size_t num_items;
|
||||
size_t item_size;
|
||||
size_t alignment;
|
||||
@ -1785,6 +1792,7 @@ struct dynamic_opts_s {
|
||||
JEMALLOC_ALWAYS_INLINE void
|
||||
dynamic_opts_init(dynamic_opts_t *dynamic_opts) {
|
||||
dynamic_opts->result = NULL;
|
||||
dynamic_opts->usize = 0;
|
||||
dynamic_opts->num_items = 0;
|
||||
dynamic_opts->item_size = 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)) {
|
||||
goto label_oom;
|
||||
}
|
||||
if (config_stats || (config_prof && opt_prof)) {
|
||||
if (config_stats || (config_prof && opt_prof) || sopts->usize) {
|
||||
usize = sz_index2size(ind);
|
||||
dopts->usize = usize;
|
||||
assert(usize > 0 && usize
|
||||
<= SC_LARGE_MAXCLASS);
|
||||
}
|
||||
} else {
|
||||
usize = sz_sa2u(size, dopts->alignment);
|
||||
dopts->usize = usize;
|
||||
if (unlikely(usize == 0
|
||||
|| usize > SC_LARGE_MAXCLASS)) {
|
||||
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.
|
||||
*/
|
||||
|
||||
#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
|
||||
void JEMALLOC_NOTHROW *
|
||||
JEMALLOC_ATTR(malloc) JEMALLOC_ALLOC_SIZE(1)
|
||||
|
Loading…
Reference in New Issue
Block a user