]> git.ipfire.org Git - thirdparty/knot-resolver.git/commitdiff
lib: improve memory reallocation strategy on mempools docs-array-growth-0xship/deployments/8742
authorVladimír Čunát <vladimir.cunat@nic.cz>
Wed, 11 Mar 2026 13:39:45 +0000 (14:39 +0100)
committerVladimír Čunát <vladimir.cunat@nic.cz>
Tue, 17 Mar 2026 11:39:42 +0000 (12:39 +0100)
lib/generic/array.h
lib/utils.c
lib/utils.h

index eb1f7bc253318c70358c53cdef633107d15342f2..04a45284bb5fc2c3e2036bb199083be29241e89e 100644 (file)
@@ -53,7 +53,7 @@
 #pragma once
 #include <stdlib.h>
 
-/** Choose array length when it overflows. */
+/** Choose array length when it overflows.  Also see pool_next_count() */
 static inline size_t array_next_count(size_t elm_size, size_t want, size_t have)
 {
        if (want >= have * 2) // We amortized enough and maybe more won't be needed.
@@ -66,7 +66,7 @@ static inline size_t array_next_count(size_t elm_size, size_t want, size_t have)
        return want * 2; // Doubling growth amortizes to roughly 2 copies per element.
 }
 
-/** @internal Incremental memory reservation */
+/** @internal Incremental memory reservation.  Also see kr_memreserve() */
 static inline int array_std_reserve(void *baton, void **mem, size_t elm_size, size_t want, size_t *have)
 {
        if (*have >= want) {
index fc4c6c9674ed611a2a3a739e04fb9656b12ec69b..ef00ccc4e88329b11be86ac056c8cc5197b326ef 100644 (file)
@@ -186,25 +186,49 @@ char * kr_absolutize_path(const char *dirname, const char *fname)
        return NULL;
 }
 
+static inline size_t pool_next_count(size_t want, size_t have)
+{
+       const int q = 3;
+       if (want >= have * q) // We amortized enough and maybe more won't be needed.
+               return want;
+       return have * q;
+       /* Why a tripling growth in particular?  The issue with mempools is that
+        * we can't really free memory (until the very end).
+        * That changes the tradeoff, and consumed memory dominates, not cost of moving.
+        * If @vcunat counted this right, with +1 requests and *q growth (q>1), we use about:
+        *  - 2q^2 / (q^2 - 1) factor of space in the average case,
+        *    which is a decreasing function of q;
+        *  - q^2 / (q - 1) factor of space in the worst case,
+        *    which has minimum for q=2 and it grows for q>2
+        * Note that for q<2 both values end up worse (i.e. larger) than for q=2.
+        * That gives us:
+        *   q=2   -> (2.67, 4   )
+        *   q=3   -> (2.25, 4.5 )
+        *   q=4   -> (2.13, 5.33)
+        * We're not really limited to whole numbers, but the changes are slow,
+        * and q=3 looks like a decent compromise.
+        */
+}
+/* Also see array_std_reserve() */
 int kr_memreserve(void *baton, void **mem, size_t elm_size, size_t want, size_t *have)
 {
-    if (*have >= want) {
-        return 0;
-    } else {
-        knot_mm_t *pool = baton;
-        size_t next_size = array_next_count(elm_size, want, *have);
-        void *mem_new = mm_alloc(pool, next_size * elm_size);
-        if (mem_new != NULL) {
-           if (*mem) { /* 0-length memcpy from NULL isn't technically OK */
-               memcpy(mem_new, *mem, (*have)*(elm_size));
-               mm_free(pool, *mem);
-           }
-            *mem = mem_new;
-            *have = next_size;
-            return 0;
-        }
-    }
-    return -1;
+       if (*have >= want)
+               return 0;
+
+       knot_mm_t *pool = baton;
+       size_t next_size = pool_next_count(want, *have);
+
+       void *mem_new = mm_alloc(pool, next_size * elm_size);
+       if (mem_new != NULL) {
+               if (*mem) { /* 0-length memcpy from NULL isn't technically OK */
+                       memcpy(mem_new, *mem, (*have)*(elm_size));
+                       mm_free(pool, *mem);
+               }
+               *mem = mem_new;
+               *have = next_size;
+               return 0;
+       }
+       return -1;
 }
 
 static int pkt_recycle(knot_pkt_t *pkt, bool keep_question)
index 387882f54ae389ef673032e4341c2dc365114bd4..ce246a7b881db84c374d3c19c8dbeca4ecf3675d 100644 (file)
@@ -229,7 +229,7 @@ static inline bool kr_rand_coin(unsigned int nomin, unsigned int denomin)
        return (kr_rand_bytes(1) < threshold);
 }
 
-/** Memory reservation routine for knot_mm_t */
+/** Memory reservation routine meant for memory pools. */
 KR_EXPORT
 int kr_memreserve(void *baton, void **mem, size_t elm_size, size_t want, size_t *have);