From: Vsevolod Stakhov Date: Tue, 21 Oct 2025 10:34:58 +0000 (+0100) Subject: [Optimize] Add rspamd_heap_push_slot to eliminate double allocation X-Git-Url: http://git.ipfire.org/gitweb.cgi?a=commitdiff_plain;h=962c4ef589abce40adaf8835a1b9c89bfc3caa98;p=thirdparty%2Frspamd.git [Optimize] Add rspamd_heap_push_slot to eliminate double allocation Add rspamd_heap_push_slot() macro that allocates a slot directly in the heap and returns a pointer to it, avoiding unnecessary copying. Previously, memory pool destructors were allocated twice: 1. First allocated in mempool via rspamd_mempool_alloc_ 2. Then copied into heap via rspamd_heap_push_safe New approach: - rspamd_heap_push_slot allocates zero-initialized slot in heap - Returns pointer to the slot for direct filling - User calls rspamd_heap_swim after filling to restore heap property Benefits: - Eliminates duplicate allocation of destructor structures - Reduces memory usage (no temporary allocation in mempool) - Better cache locality (destructor lives only in heap) - Same pattern can be used elsewhere for efficient heap usage Updated rspamd_mempool_add_destructor_full to use new API. --- diff --git a/src/libutil/heap.h b/src/libutil/heap.h index 50a2222866..08ed4afab3 100644 --- a/src/libutil/heap.h +++ b/src/libutil/heap.h @@ -153,6 +153,22 @@ extern "C" { } \ } while (0) +/** + * Allocate slot in heap and return pointer to it (zero-initialized) + * User fills the slot, then must call rspamd_heap_swim to restore heap property + * Returns NULL on allocation failure + */ +#define rspamd_heap_push_slot(name, heap) \ + ({ \ + typeof(&kv_A(*(heap), 0)) slot = NULL; \ + kv_push(typeof(kv_A(*(heap), 0)), *(heap), (typeof(kv_A(*(heap), 0))) {0}); \ + if (kv_size(*(heap)) > 0) { \ + slot = &kv_A(*(heap), kv_size(*(heap)) - 1); \ + slot->idx = kv_size(*(heap)) - 1; \ + } \ + slot; \ + }) + /** * Push element to heap (safe version with error handling) * Element is copied into the heap array. diff --git a/src/libutil/mem_pool.c b/src/libutil/mem_pool.c index d806a34e23..b7441ee4bc 100644 --- a/src/libutil/mem_pool.c +++ b/src/libutil/mem_pool.c @@ -670,15 +670,6 @@ void rspamd_mempool_add_destructor_full(rspamd_mempool_t *pool, POOL_MTX_LOCK(); - /* Allocate destructor structure */ - dtor = rspamd_mempool_alloc_(pool, sizeof(*dtor), - RSPAMD_ALIGNOF(struct _pool_destructors), line); - dtor->func = func; - dtor->data = data; - dtor->function = function; - dtor->loc = line; - dtor->pri = priority; - /* Initialize heap if not yet created */ if (pool->priv->dtors_heap.a == NULL) { rspamd_heap_init(rspamd_mempool_destruct_heap, &pool->priv->dtors_heap); @@ -690,8 +681,21 @@ void rspamd_mempool_add_destructor_full(rspamd_mempool_t *pool, } } - /* Push to heap - using non-safe version as we're in mempool */ - rspamd_heap_push_safe(rspamd_mempool_destruct_heap, &pool->priv->dtors_heap, dtor, cleanup); + /* Allocate slot directly in heap (avoids double allocation) */ + dtor = rspamd_heap_push_slot(rspamd_mempool_destruct_heap, &pool->priv->dtors_heap); + if (dtor == NULL) { + goto cleanup; + } + + /* Fill destructor structure */ + dtor->func = func; + dtor->data = data; + dtor->function = function; + dtor->loc = line; + dtor->pri = priority; + + /* Restore heap property after filling the slot */ + rspamd_heap_swim(rspamd_mempool_destruct_heap, &pool->priv->dtors_heap, dtor); POOL_MTX_UNLOCK(); return;