Here a patchset instead :)
On Thu, 25 Nov 2021 at 12:25, Willy Tarreau <[email protected]> wrote:
>
> On Thu, Nov 25, 2021 at 04:38:27PM +0500, ???? ??????? wrote:
> > > Thus I think that instead of focusing on the OS we ought to continue
> > > to focus on the allocator and improve runtime detection:
> > >
> > > - glibc (currently detected using detect_allocator)
> > > => use malloc_trim()
> > > - jemalloc at build time (mallctl != NULL)
> > > => use mallctl() as you did
> > > - jemalloc at runtime (mallctl == NULL but dlsym("mallctl") != NULL)
> > > => use mallctl() as you did
> > > - others
> > > => no trimming
> > >
> >
> > I never imagined earlier that high level applications (such as reverse
> > https/tcp proxy) cares about such low level things as allocator behaviour.
> > no jokes, really.
>
> Yes it does count a lot. That's also why we spent a lot of time optimizing
> the pools, to limit the number of calls to the system's allocator for
> everything that uses a fixed size. I've seen some performance graphs in
> our internal ticket tracker showing the memory consumption between and
> after the switch to jemalloc, and the CPU usage as well, and sometimes
> it was very important.
>
> Glibc improved quite a bit recently (2.28 or 2.33 I don't remember) by
> implementing a per-thread cache in its ptmalloc. But in our case it's
> still not as good as jemalloc, and neither perform as well as our
> thread-local pools for fixed sizes.
>
> I'm seeing in a paper about snmalloc that it performs exceptionally well
> for small allocations. I just don't know how this degrades depending on
> the access patterns. For example some allocators are fast when you free()
> in the exact reverse allocation order, but can start to fragment or have
> more work to do finding holes if you don't free() in the exact same order.
>
> But that's something to keep an eye on in the future.
>
> Willy
From 7206aff8ce6b6cb8c9b9b36d33b4f218f33cf0d1 Mon Sep 17 00:00:00 2001
From: David Carlier <[email protected]>
Date: Thu, 25 Nov 2021 13:14:37 +0000
Subject: [PATCH 3/3] MEDIUM: pool: using jemalloc when relevant.
Finally, we trim the arenas with jemalloc if detected otherwise falling back
to glibc's malloc_trim.
---
src/pool.c | 18 ++++++++++++++++--
1 file changed, 16 insertions(+), 2 deletions(-)
diff --git a/src/pool.c b/src/pool.c
index 65c037911..3ec7a5386 100644
--- a/src/pool.c
+++ b/src/pool.c
@@ -48,10 +48,24 @@ static int(*my_mallctl)(const char *, void *, size_t *, void *, size_t) = NULL;
/* ask the allocator to trim memory pools */
static void trim_all_pools(void)
{
+ if (using_libc_allocator) {
+ if (my_mallctl) {
+ unsigned int i, narenas = 0;
+ size_t len = sizeof(narenas);
+
+ if (my_mallctl("arenas.narenas", &narenas, &len, NULL, 0) == 0) {
+ for (i = 0; i < narenas; i ++) {
+ char mib[32] = {0};
+ snprintf(mib, sizeof(mib), "arena.%u.purge", i);
+ (void)my_mallctl(mib, NULL, NULL, NULL, 0);
+ }
+ }
+ } else {
#ifdef HAVE_MALLOC_TRIM
- if (using_libc_allocator)
- malloc_trim(0);
+ malloc_trim(0);
#endif
+ }
+ }
}
/* check if we're using the same allocator as the one that provides
--
2.33.1
From e8daa477b53a43ab39113cf0e9c43d9bbda1e9a9 Mon Sep 17 00:00:00 2001
From: David Carlier <[email protected]>
Date: Thu, 25 Nov 2021 10:26:50 +0000
Subject: [PATCH 1/3] MINOR: pool: refactoring to make it available for other
systems/allocators.
Actually it's focusing on linux/glibc, we re trying to expand it for other possible systems and allocators combinations.
---
include/haproxy/compat.h | 9 +++++++--
src/pool.c | 38 +++++++-------------------------------
2 files changed, 14 insertions(+), 33 deletions(-)
diff --git a/include/haproxy/compat.h b/include/haproxy/compat.h
index 25b15a1f0..b801dac08 100644
--- a/include/haproxy/compat.h
+++ b/include/haproxy/compat.h
@@ -263,9 +263,14 @@ typedef struct { } empty_t;
#endif
/* glibc 2.33 provides mallinfo2() that overcomes mallinfo()'s type limitations */
-#if (defined(__GNU_LIBRARY__) && (__GLIBC__ > 2 || __GLIBC__ == 2 && __GLIBC_MINOR__ >= 33))
+#if defined(__GNU_LIBRARY__)
#include <malloc.h>
-#define HA_HAVE_MALLINFO2
+#if (__GLIBC__ > 2 || __GLIBC__ == 2 && __GLIBC_MINOR__ >= 33)
+#define HA_MALLINFO_API mallinfo2
+#else
+#define HA_MALLINFO_API mallinfo
+#endif
+#define HA_HAVE_MALLINFO
#endif
/* FreeBSD also has malloc_usable_size() but it requires malloc_np.h */
diff --git a/src/pool.c b/src/pool.c
index af46b4469..16ef76cf0 100644
--- a/src/pool.c
+++ b/src/pool.c
@@ -42,14 +42,15 @@ int mem_poison_byte = -1;
static int mem_fail_rate = 0;
#endif
-#if defined(HA_HAVE_MALLOC_TRIM)
static int using_libc_allocator = 0;
/* ask the allocator to trim memory pools */
static void trim_all_pools(void)
{
+#ifdef HAVE_MALLOC_TRIM
if (using_libc_allocator)
malloc_trim(0);
+#endif
}
/* check if we're using the same allocator as the one that provides
@@ -60,48 +61,23 @@ static void trim_all_pools(void)
*/
static void detect_allocator(void)
{
-#ifdef HA_HAVE_MALLINFO2
- struct mallinfo2 mi1, mi2;
-#else
- struct mallinfo mi1, mi2;
-#endif
+#if defined(HA_HAVE_MALLINFO)
+ struct HA_MALLINFO_API mi1, mi2;
void *ptr;
-#ifdef HA_HAVE_MALLINFO2
- mi1 = mallinfo2();
-#else
- mi1 = mallinfo();
-#endif
+ mi1 = HA_MALLINFO_API();
ptr = DISGUISE(malloc(1));
-#ifdef HA_HAVE_MALLINFO2
- mi2 = mallinfo2();
-#else
- mi2 = mallinfo();
-#endif
+ mi2 = HA_MALLINFO_API();
free(DISGUISE(ptr));
using_libc_allocator = !!memcmp(&mi1, &mi2, sizeof(mi1));
+#endif
}
static int is_trim_enabled(void)
{
return using_libc_allocator;
}
-#else
-
-static void trim_all_pools(void)
-{
-}
-
-static void detect_allocator(void)
-{
-}
-
-static int is_trim_enabled(void)
-{
- return 0;
-}
-#endif
/* Try to find an existing shared pool with the same characteristics and
* returns it, otherwise creates this one. NULL is returned if no memory
--
2.33.1
From c0f5cf6f935392e1ea69a84aae104de5ed06e68c Mon Sep 17 00:00:00 2001
From: David Carlier <[email protected]>
Date: Thu, 25 Nov 2021 13:00:54 +0000
Subject: [PATCH 2/3] MEDIUM: pool : detecting jemalloc usage at runtime.
Here we're just looking up if the mallctl's jemalloc API symbol is present.
---
src/pool.c | 26 +++++++++++++++++++-------
1 file changed, 19 insertions(+), 7 deletions(-)
diff --git a/src/pool.c b/src/pool.c
index 16ef76cf0..65c037911 100644
--- a/src/pool.c
+++ b/src/pool.c
@@ -43,6 +43,7 @@ static int mem_fail_rate = 0;
#endif
static int using_libc_allocator = 0;
+static int(*my_mallctl)(const char *, void *, size_t *, void *, size_t) = NULL;
/* ask the allocator to trim memory pools */
static void trim_all_pools(void)
@@ -61,17 +62,28 @@ static void trim_all_pools(void)
*/
static void detect_allocator(void)
{
+ extern int mallctl(const char *, void *, size_t *, void *, size_t) __attribute__((weak));
+
+ my_mallctl = mallctl;
+
+ if (!my_mallctl)
+ my_mallctl = get_sym_curr_addr("mallctl");
+
+ if (!my_mallctl) {
#if defined(HA_HAVE_MALLINFO)
- struct HA_MALLINFO_API mi1, mi2;
- void *ptr;
+ struct HA_MALLINFO_API mi1, mi2;
+ void *ptr;
- mi1 = HA_MALLINFO_API();
- ptr = DISGUISE(malloc(1));
- mi2 = HA_MALLINFO_API();
- free(DISGUISE(ptr));
+ mi1 = HA_MALLINFO_API();
+ ptr = DISGUISE(malloc(1));
+ mi2 = HA_MALLINFO_API();
+ free(DISGUISE(ptr));
- using_libc_allocator = !!memcmp(&mi1, &mi2, sizeof(mi1));
+ using_libc_allocator = !!memcmp(&mi1, &mi2, sizeof(mi1));
#endif
+ } else {
+ using_libc_allocator = 1;
+ }
}
static int is_trim_enabled(void)
--
2.33.1