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

Reply via email to