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:
gnzlbg 2017-11-15 18:26:49 +01:00 committed by Jason Evans
parent 325e3305fc
commit 08260a6b94
5 changed files with 108 additions and 2 deletions

View File

@ -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}])

View File

@ -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

View File

@ -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);

View File

@ -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

View File

@ -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)