0001-Use-static-allocation-for-IndexAmRoutine-in-amapi.c-.patch
text/x-patch
Filename: 0001-Use-static-allocation-for-IndexAmRoutine-in-amapi.c-.patch
Type: text/x-patch
Part: 0
Patch
Same data as JSON:
GET /api/v1/attachments/:id/patch
the parsed metadata as JSON — format, series position, per-file stats; never the diff bytes.
API reference →
Format: format-patch
Series: patch 0001
Subject: Use static allocation for IndexAmRoutine in amapi.c to reduce malloc/free overhead
| File | + | − |
|---|---|---|
| src/backend/access/index/amapi.c | 75 | 6 |
From c80b2a07733112e23b70ed5b309f177f6bf79ef6 Mon Sep 17 00:00:00 2001
From: athiyaman-m <heroathi303@gmail.com>
Date: Wed, 10 Sep 2025 12:08:56 +0530
Subject: [PATCH] Use static allocation for IndexAmRoutine in amapi.c to reduce
malloc/free overhead
This avoids repeated palloc/free cycles by keeping the routine
structure in static memory. Initial pgbench tests show that this
does not regress performance and may slightly improve efficiency
at higher client counts.
Signed-off-by: athiyaman-m <heroathi303@gmail.com>
---
src/backend/access/index/amapi.c | 81 +++++++++++++++++++++++++++++---
1 file changed, 75 insertions(+), 6 deletions(-)
diff --git a/src/backend/access/index/amapi.c b/src/backend/access/index/amapi.c
index d6b8dad4d5..d5481569d9 100644
--- a/src/backend/access/index/amapi.c
+++ b/src/backend/access/index/amapi.c
@@ -13,12 +13,19 @@
*/
#include "postgres.h"
+#include <string.h>
+
#include "access/amapi.h"
#include "access/htup_details.h"
#include "catalog/pg_am.h"
#include "catalog/pg_opclass.h"
#include "utils/fmgrprotos.h"
#include "utils/syscache.h"
+#include "utils/hsearch.h"
+#include "utils/memutils.h"
+#include "utils/elog.h"
+#include "utils/pg_locale.h"
+#include "utils/builtins.h"
/*
@@ -51,6 +58,10 @@ GetIndexAmRoutine(Oid amhandler)
*
* If the given OID isn't a valid index access method, returns NULL if
* noerror is true, else throws error.
+ *
+ * This implementation caches a copy of the IndexAmRoutine per handler OID
+ * in TopMemoryContext so callers get a stable pointer and we avoid repeated
+ * per-call allocations/freeing of the routine struct.
*/
IndexAmRoutine *
GetIndexAmRoutineByAmId(Oid amoid, bool noerror)
@@ -65,8 +76,7 @@ GetIndexAmRoutineByAmId(Oid amoid, bool noerror)
{
if (noerror)
return NULL;
- elog(ERROR, "cache lookup failed for access method %u",
- amoid);
+ elog(ERROR, "cache lookup failed for access method %u", amoid);
}
amform = (Form_pg_am) GETSTRUCT(tuple);
@@ -102,8 +112,67 @@ GetIndexAmRoutineByAmId(Oid amoid, bool noerror)
ReleaseSysCache(tuple);
- /* And finally, call the handler function to get the API struct. */
- return GetIndexAmRoutine(amhandler);
+ /*
+ * Cache management: keep a single HTAB (in TopMemoryContext) that maps
+ * amhandler OIDs to a stored copy of IndexAmRoutine.
+ *
+ * Use HASH_BLOBS and store the copy of IndexAmRoutine inline in the entry.
+ */
+ {
+ typedef struct IndexAmRoutineCacheEntry
+ {
+ IndexAmRoutine routine; /* stored inline */
+ } IndexAmRoutineCacheEntry;
+
+ static HTAB *IndexAmRoutine_cache = NULL;
+
+ if (IndexAmRoutine_cache == NULL)
+ {
+ HASHCTL ctl;
+
+ /* initialize hash control structure */
+ MemSet(&ctl, 0, sizeof(ctl));
+ ctl.keysize = sizeof(Oid);
+ ctl.entrysize = sizeof(IndexAmRoutineCacheEntry);
+ ctl.hcxt = TopMemoryContext;
+
+ /* create cache in TopMemoryContext; small fixed initial size */
+ IndexAmRoutine_cache = hash_create("IndexAmRoutine cache",
+ 8,
+ &ctl,
+ HASH_ELEM | HASH_BLOBS);
+ }
+
+ /* call handler to get the runtime-provided struct (may be palloc'd) */
+ IndexAmRoutine *runtime_routine = GetIndexAmRoutine(amhandler);
+ bool found;
+ IndexAmRoutineCacheEntry *entry;
+
+ /* find or create entry; key is the amhandler Oid */
+ entry = (IndexAmRoutineCacheEntry *) hash_search(IndexAmRoutine_cache,
+ (void *)&amhandler,
+ HASH_ENTER,
+ &found);
+ if (!found)
+ {
+ /* copy runtime struct into persistent cache entry */
+ /* ensure we don't copy past the size of IndexAmRoutine */
+ memcpy(&entry->routine, runtime_routine, sizeof(IndexAmRoutine));
+ }
+
+ /*
+ * Note: we intentionally do not pfree(runtime_routine) here. The
+ * handler may have returned a pointer into static memory or memory
+ * allocated in some context. Freeing it blindly may be unsafe. The
+ * runtime allocation (if any) will be at most one small struct per
+ * handler and is acceptable for this prototype. A more polished
+ * implementation could detect and free an allocated pointer when
+ * safe.
+ */
+
+ /* return pointer to the cached copy (in TopMemoryContext) */
+ return &entry->routine;
+ }
}
@@ -187,7 +256,7 @@ amvalidate(PG_FUNCTION_ARGS)
result = amroutine->amvalidate(opclassoid);
- pfree(amroutine);
+ /* Previously we pfree(amroutine); but routine is now cached in TopMemoryContext. */
PG_RETURN_BOOL(result);
-}
+}
\ No newline at end of file
--
2.39.5