[PATCH] malloc: Improve performance of __libc_calloc
Wilco Dijkstra
Wilco.Dijkstra@arm.com
Fri May 9 16:52:59 GMT 2025
Improve performance of __libc_calloc by splitting it into 2 parts: first handle
the tcache fastpath, then do the rest in a separate tailcalled function.
This results in significant performance gains since __libc_calloc doesn't need
to setup a frame.
On Neoverse V2, bench-calloc-simple improves by 5.0% overall. Bench-calloc-tcache 32
improves by 24%.
Regress passed, OK for commit?
---
diff --git a/malloc/malloc.c b/malloc/malloc.c
index 9d860eac9cc923ef8f20218eb37f35b926f3ef82..7ddb71e08a8e79af7e0e0030ae1df6aaec7fdbed 100644
--- a/malloc/malloc.c
+++ b/malloc/malloc.c
@@ -591,6 +591,8 @@ tag_at (void *ptr)
void* __libc_malloc(size_t);
libc_hidden_proto (__libc_malloc)
+static void *__libc_calloc2 (size_t);
+
/*
free(void* p)
Releases the chunk of memory pointed to by p, that had been previously
@@ -3291,6 +3293,9 @@ tcache_init(void)
if (tcache_shutting_down)
return;
+ if (!__malloc_initialized)
+ ptmalloc_init ();
+
/* Check minimum mmap chunk is larger than max tcache size. This means
mmap chunks with their different layout are never added to tcache. */
if (MAX_TCACHE_SIZE >= GLRO (dl_pagesize) / 2)
@@ -3321,6 +3326,13 @@ tcache_init(void)
}
+static void * __attribute_noinline__
+tcache_calloc_init (size_t bytes)
+{
+ tcache_init ();
+ return __libc_calloc2 (bytes);
+}
+
# define MAYBE_INIT_TCACHE() \
if (__glibc_unlikely (tcache == NULL)) \
tcache_init();
@@ -3726,42 +3738,17 @@ __libc_pvalloc (size_t bytes)
}
void *
-__libc_calloc (size_t n, size_t elem_size)
+__libc_calloc2 (size_t sz)
{
mstate av;
mchunkptr oldtop, p;
- INTERNAL_SIZE_T sz, oldtopsize, csz;
+ INTERNAL_SIZE_T oldtopsize, csz;
void *mem;
unsigned long clearsize;
- ptrdiff_t bytes;
-
- if (__glibc_unlikely (__builtin_mul_overflow (n, elem_size, &bytes)))
- {
- __set_errno (ENOMEM);
- return NULL;
- }
-
- sz = bytes;
if (!__malloc_initialized)
ptmalloc_init ();
-#if USE_TCACHE
- size_t tc_idx = usize2tidx (bytes);
- if (tcache_available (tc_idx))
- {
- mem = tcache_get (tc_idx);
- p = mem2chunk (mem);
- if (__glibc_unlikely (mtag_enabled))
- return tag_new_zero_region (mem, memsize (p));
-
- csz = chunksize (p);
- clearsize = csz - SIZE_SZ;
- return clear_memory ((INTERNAL_SIZE_T *) mem, clearsize);
- }
- MAYBE_INIT_TCACHE ();
-#endif
-
if (SINGLE_THREAD_P)
av = &main_arena;
else
@@ -3846,6 +3833,38 @@ __libc_calloc (size_t n, size_t elem_size)
clearsize = csz - SIZE_SZ;
return clear_memory ((INTERNAL_SIZE_T *) mem, clearsize);
}
+
+void *
+__libc_calloc (size_t n, size_t elem_size)
+{
+ ptrdiff_t bytes;
+
+ if (__glibc_unlikely (__builtin_mul_overflow (n, elem_size, &bytes)))
+ {
+ __set_errno (ENOMEM);
+ return NULL;
+ }
+
+#if USE_TCACHE
+ size_t tc_idx = usize2tidx (bytes);
+ if (__glibc_likely (tc_idx < mp_.tcache_bins))
+ {
+ if (__glibc_unlikely (tcache == NULL))
+ return tcache_calloc_init (bytes);
+
+ if (__glibc_likely (tcache->entries[tc_idx] != NULL))
+ {
+ void *mem = tcache_get (tc_idx);
+
+ if (__glibc_unlikely (mtag_enabled))
+ return tag_new_zero_region (mem, memsize (mem2chunk (mem)));
+
+ return clear_memory ((INTERNAL_SIZE_T *) mem, tidx2usize (tc_idx));
+ }
+ }
+#endif
+ return __libc_calloc2 (bytes);
+}
#endif /* IS_IN (libc) */
/*
More information about the Libc-alpha
mailing list