[PATCH v4 7/7] malloc: performance improvements
Cupertino Miranda
cupertino.miranda@oracle.com
Fri Mar 14 17:29:26 GMT 2025
Improving previous code for performance reasons.
Most significant changes are:
- the addition of fast_csize2tidx function which is used in key
locations where we know we will only deal with chunks within
original fix size tcache bins,
- simplify conditions in hot locations and
- hint the compiler for most likely condition outcomes with
__glibc_(un)likely macros (or __builtin_expect).
---
malloc/arena.c | 2 +-
malloc/malloc.c | 60 ++++++++++++++++++++++++++++---------------------
2 files changed, 35 insertions(+), 27 deletions(-)
diff --git a/malloc/arena.c b/malloc/arena.c
index 353b63488d..6b5a31d03f 100644
--- a/malloc/arena.c
+++ b/malloc/arena.c
@@ -261,7 +261,7 @@ static void tcache_key_initialize (void);
static void
ptmalloc_init (void)
{
- if (__malloc_initialized)
+ if (__glibc_likely (__malloc_initialized))
return;
__malloc_initialized = true;
diff --git a/malloc/malloc.c b/malloc/malloc.c
index c031ca0071..7fb0fdaee7 100644
--- a/malloc/malloc.c
+++ b/malloc/malloc.c
@@ -293,6 +293,7 @@
/* We want 64 entries. This is an arbitrary limit, which tunables can reduce. */
# define TCACHE_UNBOUND_SIZE_BINS 10
# define TCACHE_MAX_BINS 64
+# define TCACHE_ALL_BINS (TCACHE_MAX_BINS + TCACHE_UNBOUND_SIZE_BINS)
# define MAX_TCACHE_SIZE tidx2usize (TCACHE_MAX_BINS-1)
# define TCACHE_FIXED_SIZE_BINS \
(mp_.tcache_bins < TCACHE_MAX_BINS ? mp_.tcache_bins : TCACHE_MAX_BINS)
@@ -3154,17 +3155,22 @@ tcache_key_initialize (void)
}
}
+#define fast_csize2tidx(nb) ((nb - MINSIZE + MALLOC_ALIGNMENT - 1) / MALLOC_ALIGNMENT)
+
static __always_inline size_t
csize2tidx(size_t nb)
{
- size_t idx = ((nb - MINSIZE + MALLOC_ALIGNMENT - 1) / MALLOC_ALIGNMENT);
- if (__glibc_unlikely (idx >= TCACHE_MAX_BINS)
- && __glibc_unlikely (mp_.tcache_bins > TCACHE_MAX_BINS))
+ if (__glibc_likely (nb < tidx2usize (TCACHE_MAX_BINS)))
+ return fast_csize2tidx(nb);
+ else if (__glibc_likely (mp_.tcache_bins < TCACHE_MAX_BINS))
+ return TCACHE_MAX_BINS;
+ else
{
- idx = TCACHE_MAX_BINS + __builtin_clz (tidx2usize (TCACHE_MAX_BINS-1)) - __builtin_clz (nb);
- idx = idx < mp_.tcache_bins ? idx : mp_.tcache_bins - 1;
+ size_t idx = TCACHE_MAX_BINS
+ + __builtin_clz (tidx2usize (TCACHE_MAX_BINS-1))
+ - __builtin_clz (nb);
+ return idx < TCACHE_ALL_BINS ? idx : TCACHE_ALL_BINS - 1;
}
- return idx;
}
static __always_inline tcache_entry *tcache_next (tcache_entry *);
@@ -3172,19 +3178,21 @@ static __always_inline tcache_entry *tcache_next (tcache_entry *);
static __always_inline tcache_entry **
tcache_location_for_size (size_t nb, size_t tc_idx)
{
- tcache_entry **tep = &(tcache->entries[tc_idx]);
- tcache_entry *te = REVEAL_PTR (*tep);
- if (__glibc_unlikely (tc_idx >= TCACHE_MAX_BINS))
+ if (__glibc_likely (nb <= tidx2usize (TCACHE_MAX_BINS-1)))
+ return &(tcache->entries[tc_idx]);
+ else
{
+ tcache_entry **tep = &(tcache->entries[tc_idx]);
+ tcache_entry *te = REVEAL_PTR (*tep);
while (te != NULL
- && __glibc_unlikely (chunksize (mem2chunk (te)) < nb))
- {
- tep = & (te->next);
- te = tcache_next (te);
- }
- }
+ && __glibc_unlikely (chunksize (mem2chunk (te)) < nb))
+ {
+ tep = & (te->next);
+ te = tcache_next (te);
+ }
- return tep;
+ return tep;
+ }
}
/* Caller must ensure that we know tc_idx is valid and there's room
@@ -3228,7 +3236,7 @@ tcache_get (size_t nb, size_t tc_idx)
{
tcache_entry **entry = tcache_location_for_size (nb, tc_idx);
tcache_entry *e = REVEAL_PTR (*entry);
- if (tc_idx >= TCACHE_MAX_BINS
+ if (__glibc_unlikely (tc_idx >= TCACHE_MAX_BINS)
&& (e == NULL || chunksize (mem2chunk (e)) != nb))
return NULL;
@@ -3246,8 +3254,7 @@ tcache_next (tcache_entry *e)
static __always_inline bool
tcache_available (size_t tc_idx)
{
- if (tc_idx < mp_.tcache_bins
- && tcache != NULL
+ if (__glibc_likely (tcache != NULL)
&& tcache->counts[tc_idx] > 0)
return true;
else
@@ -3284,7 +3291,8 @@ tcache_free (mchunkptr p, INTERNAL_SIZE_T size)
{
bool done = false;
size_t tc_idx = csize2tidx (size);
- if (tcache != NULL && tc_idx < mp_.tcache_bins)
+ if (__glibc_likely (tcache != NULL)
+ && __glibc_likely (tc_idx < mp_.tcache_bins))
{
/* Check to see if it's already in the tcache. */
tcache_entry *e = (tcache_entry *) chunk2mem (p);
@@ -4040,7 +4048,7 @@ _int_malloc (mstate av, size_t bytes)
#if USE_TCACHE
/* While we're here, if we see other chunks of the same size,
stash them in the tcache. */
- size_t tc_idx = csize2tidx (nb);
+ size_t tc_idx = fast_csize2tidx (nb);
if (tcache != NULL && tc_idx < TCACHE_FIXED_SIZE_BINS)
{
mchunkptr tc_victim;
@@ -4098,8 +4106,8 @@ _int_malloc (mstate av, size_t bytes)
#if USE_TCACHE
/* While we're here, if we see other chunks of the same size,
stash them in the tcache. */
- size_t tc_idx = csize2tidx (nb);
- if (tcache != NULL && tc_idx < TCACHE_FIXED_SIZE_BINS)
+ size_t tc_idx = fast_csize2tidx (nb);
+ if (__glibc_likely (tcache != NULL) && tc_idx < TCACHE_FIXED_SIZE_BINS)
{
mchunkptr tc_victim;
@@ -4160,7 +4168,7 @@ _int_malloc (mstate av, size_t bytes)
#if USE_TCACHE
INTERNAL_SIZE_T tcache_nb = 0;
- size_t tc_idx = csize2tidx (nb);
+ size_t tc_idx = fast_csize2tidx (nb);
if (tcache != NULL && tc_idx < TCACHE_FIXED_SIZE_BINS)
tcache_nb = nb;
int return_cached = 0;
@@ -4241,8 +4249,8 @@ _int_malloc (mstate av, size_t bytes)
#if USE_TCACHE
/* Fill cache first, return to user only if cache fills.
We may return one of these chunks later. */
- if (tcache_nb > 0
- && tcache->counts[tc_idx] < mp_.tcache_count)
+ if (__glibc_likely (tcache_nb > 0)
+ && __glibc_likely (tcache->counts[tc_idx] < mp_.tcache_count))
{
tcache_put (victim, tc_idx);
return_cached = 1;
--
2.39.5
More information about the Libc-alpha
mailing list