From 520baa2284c221fb36ade079263dbe5d7eb4f4f6 Mon Sep 17 00:00:00 2001 From: David Geier Date: Fri, 18 Sep 2026 12:17:11 +0200 Subject: [PATCH v4 2/7] Allocate RelationData structs in a dedicated slab context The power-of-two size classes of aset mean that reductions in the size of RelationData often yield no actual memory savings, as the allocation is rounded up to the next size class anyway. RelationData objects are fixed-size and numerous, so they are a good fit for a slab allocator. Create a slab context as a child of CacheMemoryContext in RelationCacheInitialize() and allocate all RelationData structs there; subsidiary allocations remain in CacheMemoryContext itself. --- src/backend/utils/cache/relcache.c | 44 +++++++++++++++++++++++------- 1 file changed, 34 insertions(+), 10 deletions(-) diff --git a/src/backend/utils/cache/relcache.c b/src/backend/utils/cache/relcache.c index a04e8a7fa93..78794a2fb60 100644 --- a/src/backend/utils/cache/relcache.c +++ b/src/backend/utils/cache/relcache.c @@ -137,6 +137,17 @@ typedef struct relidcacheent static HTAB *RelationIdCache; +/* + * Slab memory context holding all the relcache's RelationData structs. + * It is a child of CacheMemoryContext; only the fixed-size RelationData + * objects are allocated there, while all subsidiary data stays in + * CacheMemoryContext itself. + */ +static MemoryContext RelationSlabContext = NULL; + +#define AllocRelationData() \ + MemoryContextAllocZero(RelationSlabContext, sizeof(RelationData)) + /* * This flag is false until we have prepared the critical relcache entries * that are needed to do indexscans on the tables read by relcache building. @@ -420,13 +431,15 @@ AllocateRelationDesc(Form_pg_class relp) MemoryContext oldcxt; Form_pg_class relationForm; - /* Relcache entries must live in CacheMemoryContext */ - oldcxt = MemoryContextSwitchTo(CacheMemoryContext); - /* - * allocate and zero space for new relation descriptor + * Allocate and zero space for new relation descriptor. The RelationData + * struct goes into RelationSlabContext; all subsidiary data is allocated + * in CacheMemoryContext. */ - relation = palloc0_object(RelationData); + relation = AllocRelationData(); + + /* Relcache subsidiary data must live in CacheMemoryContext */ + oldcxt = MemoryContextSwitchTo(CacheMemoryContext); /* make sure relation is marked as having no open file yet */ relation->rd_smgr = NULL; @@ -1894,9 +1907,10 @@ formrdesc(const char *relationName, Oid relationReltype, bool has_not_null; /* - * allocate new relation desc, clear all fields of reldesc + * allocate new relation desc, clear all fields of reldesc; the + * RelationData struct itself goes into RelationSlabContext */ - relation = palloc0_object(RelationData); + relation = AllocRelationData(); /* make sure relation is marked as having no open file yet */ relation->rd_smgr = NULL; @@ -3567,9 +3581,10 @@ RelationBuildLocalRelation(const char *relname, oldcxt = MemoryContextSwitchTo(CacheMemoryContext); /* - * allocate a new relation descriptor and fill in basic state fields. + * allocate a new relation descriptor and fill in basic state fields; the + * RelationData struct itself goes into RelationSlabContext */ - rel = palloc0_object(RelationData); + rel = AllocRelationData(); /* make sure relation is marked as having no open file yet */ rel->rd_smgr = NULL; @@ -4002,6 +4017,14 @@ RelationCacheInitialize(void) if (!CacheMemoryContext) CreateCacheMemoryContext(); + /* + * create the slab context holding the relcache's RelationData structs + */ + RelationSlabContext = SlabContextCreate(CacheMemoryContext, + "RelationSlabContext", + SLAB_DEFAULT_BLOCK_SIZE, + sizeof(RelationData)); + /* * create hashtable that indexes the relcache */ @@ -6301,7 +6324,8 @@ load_relcache_init_file(bool shared) rels = repalloc_array(rels, Relation, max_rels); } - rel = rels[num_rels++] = (Relation) palloc(len); + rel = rels[num_rels++] = (Relation) + MemoryContextAlloc(RelationSlabContext, len); /* then, read the Relation structure */ if (fread(rel, 1, len, fp) != len) -- 2.55.0