From: Vladimír Čunát Date: Wed, 11 Mar 2026 13:39:45 +0000 (+0100) Subject: lib: improve memory reallocation strategy on mempools X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=9c9513a0662a2b1ccde2ab7cce45c173624ac805;p=thirdparty%2Fknot-resolver.git lib: improve memory reallocation strategy on mempools --- diff --git a/lib/generic/array.h b/lib/generic/array.h index eb1f7bc25..04a45284b 100644 --- a/lib/generic/array.h +++ b/lib/generic/array.h @@ -53,7 +53,7 @@ #pragma once #include -/** 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) { diff --git a/lib/utils.c b/lib/utils.c index fc4c6c967..ef00ccc4e 100644 --- a/lib/utils.c +++ b/lib/utils.c @@ -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) diff --git a/lib/utils.h b/lib/utils.h index 387882f54..ce246a7b8 100644 --- a/lib/utils.h +++ b/lib/utils.h @@ -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);