From c584f4434576742dce3afc699bb5264773d067b1 Mon Sep 17 00:00:00 2001
From: John Naylor <john.naylor@postgresql.org>
Date: Thu, 19 Aug 2021 18:43:18 -0400
Subject: [PATCH v1] Specialize SearchSysCache4 by the search key data types

Use a modified version of SearchCatCacheInternal as a template
for specializing the search functions. The reason to do this is
to avoid calling equality and hash functions via through a pointer,
in the hopes this will improve syscache performance.
---
 src/backend/access/brin/brin_inclusion.c     |   2 +-
 src/backend/access/brin/brin_minmax.c        |   2 +-
 src/backend/access/brin/brin_minmax_multi.c  |   2 +-
 src/backend/catalog/namespace.c              |   2 +-
 src/backend/catalog/objectaddress.c          |   4 +-
 src/backend/catalog/pg_operator.c            |   2 +-
 src/backend/utils/cache/catcache.c           |  55 +++++-
 src/backend/utils/cache/lsyscache.c          |   4 +-
 src/backend/utils/cache/syscache.c           |  29 ++-
 src/include/utils/catcache.h                 |   4 +-
 src/include/utils/catcache_search_template.h | 176 +++++++++++++++++++
 src/include/utils/syscache.h                 |   6 +-
 12 files changed, 266 insertions(+), 22 deletions(-)
 create mode 100644 src/include/utils/catcache_search_template.h

diff --git a/src/backend/access/brin/brin_inclusion.c b/src/backend/access/brin/brin_inclusion.c
index 0b384c0bd1..78e546821d 100644
--- a/src/backend/access/brin/brin_inclusion.c
+++ b/src/backend/access/brin/brin_inclusion.c
@@ -634,7 +634,7 @@ inclusion_get_strategy_procinfo(BrinDesc *bdesc, uint16 attno, Oid subtype,
 
 		opfamily = bdesc->bd_index->rd_opfamily[attno - 1];
 		attr = TupleDescAttr(bdesc->bd_tupdesc, attno - 1);
-		tuple = SearchSysCache4(AMOPSTRATEGY, ObjectIdGetDatum(opfamily),
+		tuple = SearchSysCacheAMOPSTRATEGY(ObjectIdGetDatum(opfamily),
 								ObjectIdGetDatum(attr->atttypid),
 								ObjectIdGetDatum(subtype),
 								Int16GetDatum(strategynum));
diff --git a/src/backend/access/brin/brin_minmax.c b/src/backend/access/brin/brin_minmax.c
index 798f06c722..9d548f0576 100644
--- a/src/backend/access/brin/brin_minmax.c
+++ b/src/backend/access/brin/brin_minmax.c
@@ -294,7 +294,7 @@ minmax_get_strategy_procinfo(BrinDesc *bdesc, uint16 attno, Oid subtype,
 
 		opfamily = bdesc->bd_index->rd_opfamily[attno - 1];
 		attr = TupleDescAttr(bdesc->bd_tupdesc, attno - 1);
-		tuple = SearchSysCache4(AMOPSTRATEGY, ObjectIdGetDatum(opfamily),
+		tuple = SearchSysCacheAMOPSTRATEGY(ObjectIdGetDatum(opfamily),
 								ObjectIdGetDatum(attr->atttypid),
 								ObjectIdGetDatum(subtype),
 								Int16GetDatum(strategynum));
diff --git a/src/backend/access/brin/brin_minmax_multi.c b/src/backend/access/brin/brin_minmax_multi.c
index e3c98c2ffd..2ab4fb046a 100644
--- a/src/backend/access/brin/brin_minmax_multi.c
+++ b/src/backend/access/brin/brin_minmax_multi.c
@@ -2932,7 +2932,7 @@ minmax_multi_get_strategy_procinfo(BrinDesc *bdesc, uint16 attno, Oid subtype,
 
 		opfamily = bdesc->bd_index->rd_opfamily[attno - 1];
 		attr = TupleDescAttr(bdesc->bd_tupdesc, attno - 1);
-		tuple = SearchSysCache4(AMOPSTRATEGY, ObjectIdGetDatum(opfamily),
+		tuple = SearchSysCacheAMOPSTRATEGY(ObjectIdGetDatum(opfamily),
 								ObjectIdGetDatum(attr->atttypid),
 								ObjectIdGetDatum(subtype),
 								Int16GetDatum(strategynum));
diff --git a/src/backend/catalog/namespace.c b/src/backend/catalog/namespace.c
index fd767fc5cf..5c700d537b 100644
--- a/src/backend/catalog/namespace.c
+++ b/src/backend/catalog/namespace.c
@@ -1545,7 +1545,7 @@ OpernameGetOprid(List *names, Oid oprleft, Oid oprright)
 		{
 			HeapTuple	opertup;
 
-			opertup = SearchSysCache4(OPERNAMENSP,
+			opertup = SearchSysCacheOPERNAMENSP(
 									  CStringGetDatum(opername),
 									  ObjectIdGetDatum(oprleft),
 									  ObjectIdGetDatum(oprright),
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index 9882e549c4..5aca2badb3 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -1751,7 +1751,7 @@ get_object_address_opf_member(ObjectType objtype,
 				ObjectAddressSet(address, AccessMethodOperatorRelationId,
 								 InvalidOid);
 
-				tp = SearchSysCache4(AMOPSTRATEGY,
+				tp = SearchSysCacheAMOPSTRATEGY(
 									 ObjectIdGetDatum(famaddr.objectId),
 									 ObjectIdGetDatum(typeoids[0]),
 									 ObjectIdGetDatum(typeoids[1]),
@@ -1782,7 +1782,7 @@ get_object_address_opf_member(ObjectType objtype,
 				ObjectAddressSet(address, AccessMethodProcedureRelationId,
 								 InvalidOid);
 
-				tp = SearchSysCache4(AMPROCNUM,
+				tp = SearchSysCacheAMPROCNUM(
 									 ObjectIdGetDatum(famaddr.objectId),
 									 ObjectIdGetDatum(typeoids[0]),
 									 ObjectIdGetDatum(typeoids[1]),
diff --git a/src/backend/catalog/pg_operator.c b/src/backend/catalog/pg_operator.c
index 6c270f9338..0dca12010b 100644
--- a/src/backend/catalog/pg_operator.c
+++ b/src/backend/catalog/pg_operator.c
@@ -136,7 +136,7 @@ OperatorGet(const char *operatorName,
 	HeapTuple	tup;
 	Oid			operatorObjectId;
 
-	tup = SearchSysCache4(OPERNAMENSP,
+	tup = SearchSysCacheOPERNAMENSP(
 						  PointerGetDatum(operatorName),
 						  ObjectIdGetDatum(leftObjectId),
 						  ObjectIdGetDatum(rightObjectId),
diff --git a/src/backend/utils/cache/catcache.c b/src/backend/utils/cache/catcache.c
index 4fbdc62d8c..cd98766d59 100644
--- a/src/backend/utils/cache/catcache.c
+++ b/src/backend/utils/cache/catcache.c
@@ -1186,12 +1186,55 @@ SearchCatCache3(CatCache *cache,
 }
 
 
-HeapTuple
-SearchCatCache4(CatCache *cache,
-				Datum v1, Datum v2, Datum v3, Datum v4)
-{
-	return SearchCatCacheInternal(cache, 4, v1, v2, v3, v4);
-}
+/* 4-key search functions */
+
+#define CC_SEARCH SearchCatCacheOidOidOidInt16
+#define CC_NKEYS 4
+#define FASTEQFUNC1 int4eqfast
+#define FASTEQFUNC2 int4eqfast
+#define FASTEQFUNC3 int4eqfast
+#define FASTEQFUNC4 int2eqfast
+#define HASHFUNC1 int4hashfast
+#define HASHFUNC2 int4hashfast
+#define HASHFUNC3 int4hashfast
+#define HASHFUNC4 int2hashfast
+
+#include "utils/catcache_search_template.h"
+
+#undef CC_SEARCH
+#undef CC_NKEYS
+#undef FASTEQFUNC1
+#undef FASTEQFUNC2
+#undef FASTEQFUNC3
+#undef FASTEQFUNC4
+#undef HASHFUNC1
+#undef HASHFUNC2
+#undef HASHFUNC3
+#undef HASHFUNC4
+
+#define CC_SEARCH SearchCatCacheNameOidOidOid
+#define CC_NKEYS 4
+#define FASTEQFUNC1 nameeqfast
+#define FASTEQFUNC2 int4eqfast
+#define FASTEQFUNC3 int4eqfast
+#define FASTEQFUNC4 int4eqfast
+#define HASHFUNC1 namehashfast
+#define HASHFUNC2 int4hashfast
+#define HASHFUNC3 int4hashfast
+#define HASHFUNC4 int4hashfast
+
+#include "utils/catcache_search_template.h"
+
+#undef CC_SEARCH
+#undef CC_NKEYS
+#undef FASTEQFUNC1
+#undef FASTEQFUNC2
+#undef FASTEQFUNC3
+#undef FASTEQFUNC4
+#undef HASHFUNC1
+#undef HASHFUNC2
+#undef HASHFUNC3
+#undef HASHFUNC4
 
 /*
  * Work-horse for SearchCatCache/SearchCatCacheN.
diff --git a/src/backend/utils/cache/lsyscache.c b/src/backend/utils/cache/lsyscache.c
index 4ebaa552a2..b5d7dcf94b 100644
--- a/src/backend/utils/cache/lsyscache.c
+++ b/src/backend/utils/cache/lsyscache.c
@@ -168,7 +168,7 @@ get_opfamily_member(Oid opfamily, Oid lefttype, Oid righttype,
 	Form_pg_amop amop_tup;
 	Oid			result;
 
-	tp = SearchSysCache4(AMOPSTRATEGY,
+	tp = SearchSysCacheAMOPSTRATEGY(
 						 ObjectIdGetDatum(opfamily),
 						 ObjectIdGetDatum(lefttype),
 						 ObjectIdGetDatum(righttype),
@@ -797,7 +797,7 @@ get_opfamily_proc(Oid opfamily, Oid lefttype, Oid righttype, int16 procnum)
 	Form_pg_amproc amproc_tup;
 	RegProcedure result;
 
-	tp = SearchSysCache4(AMPROCNUM,
+	tp = SearchSysCacheAMPROCNUM(
 						 ObjectIdGetDatum(opfamily),
 						 ObjectIdGetDatum(lefttype),
 						 ObjectIdGetDatum(righttype),
diff --git a/src/backend/utils/cache/syscache.c b/src/backend/utils/cache/syscache.c
index d6cb78dea8..b0131374ea 100644
--- a/src/backend/utils/cache/syscache.c
+++ b/src/backend/utils/cache/syscache.c
@@ -1157,14 +1157,33 @@ SearchSysCache3(int cacheId,
 }
 
 HeapTuple
-SearchSysCache4(int cacheId,
+SearchSysCacheAMOPSTRATEGY(
 				Datum key1, Datum key2, Datum key3, Datum key4)
 {
-	Assert(cacheId >= 0 && cacheId < SysCacheSize &&
-		   PointerIsValid(SysCache[cacheId]));
-	Assert(SysCache[cacheId]->cc_nkeys == 4);
+	Assert(PointerIsValid(SysCache[AMOPSTRATEGY]));
+	Assert(SysCache[AMOPSTRATEGY]->cc_nkeys == 4);
+
+	return SearchCatCacheOidOidOidInt16(SysCache[AMOPSTRATEGY], key1, key2, key3, key4);
+}
+
+HeapTuple
+SearchSysCacheAMPROCNUM(
+				Datum key1, Datum key2, Datum key3, Datum key4)
+{
+	Assert(PointerIsValid(SysCache[AMPROCNUM]));
+	Assert(SysCache[AMPROCNUM]->cc_nkeys == 4);
+
+	return SearchCatCacheOidOidOidInt16(SysCache[AMPROCNUM], key1, key2, key3, key4);
+}
+
+HeapTuple
+SearchSysCacheOPERNAMENSP(
+				Datum key1, Datum key2, Datum key3, Datum key4)
+{
+	Assert(PointerIsValid(SysCache[OPERNAMENSP]));
+	Assert(SysCache[OPERNAMENSP]->cc_nkeys == 4);
 
-	return SearchCatCache4(SysCache[cacheId], key1, key2, key3, key4);
+	return SearchCatCacheNameOidOidOid(SysCache[OPERNAMENSP], key1, key2, key3, key4);
 }
 
 /*
diff --git a/src/include/utils/catcache.h b/src/include/utils/catcache.h
index ddc2762eb3..623133f57b 100644
--- a/src/include/utils/catcache.h
+++ b/src/include/utils/catcache.h
@@ -204,7 +204,9 @@ extern HeapTuple SearchCatCache2(CatCache *cache,
 								 Datum v1, Datum v2);
 extern HeapTuple SearchCatCache3(CatCache *cache,
 								 Datum v1, Datum v2, Datum v3);
-extern HeapTuple SearchCatCache4(CatCache *cache,
+extern HeapTuple SearchCatCacheOidOidOidInt16(CatCache *cache,
+								 Datum v1, Datum v2, Datum v3, Datum v4);
+extern HeapTuple SearchCatCacheNameOidOidOid(CatCache *cache,
 								 Datum v1, Datum v2, Datum v3, Datum v4);
 extern void ReleaseCatCache(HeapTuple tuple);
 
diff --git a/src/include/utils/catcache_search_template.h b/src/include/utils/catcache_search_template.h
new file mode 100644
index 0000000000..6f5dc2573c
--- /dev/null
+++ b/src/include/utils/catcache_search_template.h
@@ -0,0 +1,176 @@
+/*-------------------------------------------------------------------------
+ * catcache_search_template.h
+ *
+ * A template for type-specialized SearchCatCache functions
+ *
+ * Usage notes:
+ *
+ * To generate functions specialized for a set of catcache keys,
+ * the following parameter macros should be #define'd before this
+ * file is included.
+ *
+ * - CC_SEARCH - the name of the search function to be generated
+ * - CC_NKEYS - the number of search keys for the tuple
+ * - FASTEQFUNCx (x in 1,2,3,4) - type-specific equality function(s)
+ * - HASHFUNCx (x in 1,2,3,4) - type-specific hash function(s)
+ *
+ *
+ * Copyright (c) 2021, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1992-1994, Regents of the University of California
+ *
+ * src/include/utils/catcache_search_template.h
+ *
+ *-------------------------------------------------------------------------
+ */
+
+
+HeapTuple
+CC_SEARCH(CatCache *cache,
+		  Datum v1,
+		  Datum v2,
+		  Datum v3,
+		  Datum v4)
+{
+	uint32		hashValue = 0;
+	uint32		oneHash;
+	Index		hashIndex;
+	dlist_iter	iter;
+	dlist_head *bucket;
+	CatCTup    *ct;
+
+	/* Make sure we're in an xact, even if this ends up being a cache hit */
+	Assert(IsTransactionState());
+
+	Assert(cache->cc_nkeys == CC_NKEYS);
+
+	/*
+	 * one-time startup overhead for each cache
+	 */
+	if (unlikely(cache->cc_tupdesc == NULL))
+		CatalogCacheInitializeCache(cache);
+
+#ifdef CATCACHE_STATS
+	cache->cc_searches++;
+#endif
+
+	/*
+	 * find the hash bucket in which to look for the tuple
+	 */
+	CACHE_elog(DEBUG2, "CatalogCacheComputeHashValue %s %d %p",
+			   cache->cc_relname, CC_NKEYS, cache);
+
+	switch (CC_NKEYS)
+	{
+		case 4:
+			oneHash = HASHFUNC4(v4);
+
+			hashValue ^= oneHash << 24;
+			hashValue ^= oneHash >> 8;
+			/* FALLTHROUGH */
+		case 3:
+			oneHash = HASHFUNC3(v3);
+
+			hashValue ^= oneHash << 16;
+			hashValue ^= oneHash >> 16;
+			/* FALLTHROUGH */
+		case 2:
+			oneHash = HASHFUNC2(v2);
+
+			hashValue ^= oneHash << 8;
+			hashValue ^= oneHash >> 24;
+			/* FALLTHROUGH */
+		case 1:
+			oneHash = HASHFUNC1(v1);
+
+			hashValue ^= oneHash;
+			break;
+		default:
+			elog(FATAL, "wrong number of hash keys: %d", CC_NKEYS);
+			break;
+	}
+
+	hashIndex = HASH_INDEX(hashValue, cache->cc_nbuckets);
+
+	/*
+	 * scan the hash bucket until we find a match or exhaust our tuples
+	 *
+	 * Note: it's okay to use dlist_foreach here, even though we modify the
+	 * dlist within the loop, because we don't continue the loop afterwards.
+	 */
+	bucket = &cache->cc_bucket[hashIndex];
+	dlist_foreach(iter, bucket)
+	{
+		ct = dlist_container(CatCTup, cache_elem, iter.cur);
+
+		if (ct->dead)
+			continue;			/* ignore dead entries */
+
+		if (ct->hash_value != hashValue)
+			continue;			/* quickly skip entry if wrong hash val */
+
+		switch (CC_NKEYS)
+		{
+			case 4:
+				if (!FASTEQFUNC4(ct->keys[3], v4))
+					continue;
+				/* FALLTHROUGH */
+			case 3:
+				if (!FASTEQFUNC3(ct->keys[2], v3))
+					continue;
+				/* FALLTHROUGH */
+			case 2:
+				if (!FASTEQFUNC2(ct->keys[1], v2))
+					continue;
+				/* FALLTHROUGH */
+			case 1:
+				if (!FASTEQFUNC1(ct->keys[0], v1))
+					continue;
+				break;
+			default:
+				elog(FATAL, "wrong number of keys: %d", CC_NKEYS);
+				break;
+		}
+
+		/*
+		 * We found a match in the cache.  Move it to the front of the list
+		 * for its hashbucket, in order to speed subsequent searches.  (The
+		 * most frequently accessed elements in any hashbucket will tend to be
+		 * near the front of the hashbucket's list.)
+		 */
+		dlist_move_head(bucket, &ct->cache_elem);
+
+		/*
+		 * If it's a positive entry, bump its refcount and return it. If it's
+		 * negative, we can report failure to the caller.
+		 */
+		if (!ct->negative)
+		{
+			ResourceOwnerEnlargeCatCacheRefs(CurrentResourceOwner);
+			ct->refcount++;
+			ResourceOwnerRememberCatCacheRef(CurrentResourceOwner, &ct->tuple);
+
+			CACHE_elog(DEBUG2, "SearchCatCache(%s): found in bucket %d",
+					   cache->cc_relname, hashIndex);
+
+#ifdef CATCACHE_STATS
+			cache->cc_hits++;
+#endif
+
+			return &ct->tuple;
+		}
+		else
+		{
+			CACHE_elog(DEBUG2, "SearchCatCache(%s): found neg entry in bucket %d",
+					   cache->cc_relname, hashIndex);
+
+#ifdef CATCACHE_STATS
+			cache->cc_neg_hits++;
+#endif
+
+			return NULL;
+		}
+	}
+
+	return SearchCatCacheMiss(cache, CC_NKEYS, hashValue, hashIndex, v1, v2, v3, v4);
+}
+
diff --git a/src/include/utils/syscache.h b/src/include/utils/syscache.h
index d74a348600..5396f9ccfd 100644
--- a/src/include/utils/syscache.h
+++ b/src/include/utils/syscache.h
@@ -130,7 +130,11 @@ extern HeapTuple SearchSysCache2(int cacheId,
 								 Datum key1, Datum key2);
 extern HeapTuple SearchSysCache3(int cacheId,
 								 Datum key1, Datum key2, Datum key3);
-extern HeapTuple SearchSysCache4(int cacheId,
+extern HeapTuple SearchSysCacheAMOPSTRATEGY(
+								 Datum key1, Datum key2, Datum key3, Datum key4);
+extern HeapTuple SearchSysCacheAMPROCNUM(
+								 Datum key1, Datum key2, Datum key3, Datum key4);
+extern HeapTuple SearchSysCacheOPERNAMENSP(
 								 Datum key1, Datum key2, Datum key3, Datum key4);
 
 extern void ReleaseSysCache(HeapTuple tuple);
-- 
2.31.1

