Hi,

Heikki's catcache rehashing stuff reminded me that I'd posted an
optimization to catcache (20121220153555.gh4...@awork2.anarazel.de) some
time back which I didn't have energy to pursue at that point.

I've brushed the patch up a bit and verified it still gives be a
performance improvement. It's still about 2% in a readonly pgbench on my
elderly laptop.

There's basically two tricks in the patch:
1) Don't always copy the cache's ScanKey to the stack. Instead pass
   an array of arguments around. That get's rid of a good amount of
   memcpy()ing in the common, cached case.
2) If we have to memcpy() because we need to pass a ScanKey to
   systable_*, copy only cache->cc_nkey * sizeof(ScanKeyData) instead of
   always copying the maximum size.

I'd be nicer to get rid of the mostly copied HeapKeyTestArg, but I don't
immediately see how.

Greetings,

Andres Freund

-- 
 Andres Freund                     http://www.2ndQuadrant.com/
 PostgreSQL Development, 24x7 Support, Training & Services
>From df40dab17e7582ab597214d3c7fcefd953dcc9f7 Mon Sep 17 00:00:00 2001
From: Andres Freund <and...@anarazel.de>
Date: Thu, 5 Sep 2013 20:30:44 +0200
Subject: [PATCH] Improve catalog cache lookup performance.

Do so by not copying the entire ScanKey upon entering SearchCatCache,
SearchCatCacheList. Instead perform operations like hashing on a smaller array
containing only the passed in Datums.

When a lookup into the underlying relations need to happen, copy the Datums
into a local copy of the ScanKey as before, but use a smaller memcpy() if
possible.
---
 src/backend/utils/cache/catcache.c | 120 ++++++++++++++++++++-----------------
 src/include/access/valid.h         |  56 +++++++++++++++++
 2 files changed, 122 insertions(+), 54 deletions(-)

diff --git a/src/backend/utils/cache/catcache.c b/src/backend/utils/cache/catcache.c
index c467f11..fb7eaa2 100644
--- a/src/backend/utils/cache/catcache.c
+++ b/src/backend/utils/cache/catcache.c
@@ -74,7 +74,7 @@ static CatCacheHeader *CacheHdr = NULL;
 
 
 static uint32 CatalogCacheComputeHashValue(CatCache *cache, int nkeys,
-							 ScanKey cur_skey);
+										   Datum *argument);
 static uint32 CatalogCacheComputeTupleHashValue(CatCache *cache,
 								  HeapTuple tuple);
 
@@ -174,7 +174,7 @@ GetCCHashEqFuncs(Oid keytype, PGFunction *hashfunc, RegProcedure *eqfunc)
  * Compute the hash value associated with a given set of lookup keys
  */
 static uint32
-CatalogCacheComputeHashValue(CatCache *cache, int nkeys, ScanKey cur_skey)
+CatalogCacheComputeHashValue(CatCache *cache, int nkeys, Datum *argument)
 {
 	uint32		hashValue = 0;
 	uint32		oneHash;
@@ -189,28 +189,28 @@ CatalogCacheComputeHashValue(CatCache *cache, int nkeys, ScanKey cur_skey)
 		case 4:
 			oneHash =
 				DatumGetUInt32(DirectFunctionCall1(cache->cc_hashfunc[3],
-												   cur_skey[3].sk_argument));
+												   argument[3]));
 			hashValue ^= oneHash << 24;
 			hashValue ^= oneHash >> 8;
 			/* FALLTHROUGH */
 		case 3:
 			oneHash =
 				DatumGetUInt32(DirectFunctionCall1(cache->cc_hashfunc[2],
-												   cur_skey[2].sk_argument));
+												   argument[2]));
 			hashValue ^= oneHash << 16;
 			hashValue ^= oneHash >> 16;
 			/* FALLTHROUGH */
 		case 2:
 			oneHash =
 				DatumGetUInt32(DirectFunctionCall1(cache->cc_hashfunc[1],
-												   cur_skey[1].sk_argument));
+												   argument[1]));
 			hashValue ^= oneHash << 8;
 			hashValue ^= oneHash >> 24;
 			/* FALLTHROUGH */
 		case 1:
 			oneHash =
 				DatumGetUInt32(DirectFunctionCall1(cache->cc_hashfunc[0],
-												   cur_skey[0].sk_argument));
+												   argument[0]));
 			hashValue ^= oneHash;
 			break;
 		default:
@@ -229,17 +229,14 @@ CatalogCacheComputeHashValue(CatCache *cache, int nkeys, ScanKey cur_skey)
 static uint32
 CatalogCacheComputeTupleHashValue(CatCache *cache, HeapTuple tuple)
 {
-	ScanKeyData cur_skey[CATCACHE_MAXKEYS];
+	Datum		arguments[CATCACHE_MAXKEYS];
 	bool		isNull = false;
 
-	/* Copy pre-initialized overhead data for scankey */
-	memcpy(cur_skey, cache->cc_skey, sizeof(cur_skey));
-
 	/* Now extract key fields from tuple, insert into scankey */
 	switch (cache->cc_nkeys)
 	{
 		case 4:
-			cur_skey[3].sk_argument =
+			arguments[3] =
 				(cache->cc_key[3] == ObjectIdAttributeNumber)
 				? ObjectIdGetDatum(HeapTupleGetOid(tuple))
 				: fastgetattr(tuple,
@@ -249,7 +246,7 @@ CatalogCacheComputeTupleHashValue(CatCache *cache, HeapTuple tuple)
 			Assert(!isNull);
 			/* FALLTHROUGH */
 		case 3:
-			cur_skey[2].sk_argument =
+			arguments[2] =
 				(cache->cc_key[2] == ObjectIdAttributeNumber)
 				? ObjectIdGetDatum(HeapTupleGetOid(tuple))
 				: fastgetattr(tuple,
@@ -259,7 +256,7 @@ CatalogCacheComputeTupleHashValue(CatCache *cache, HeapTuple tuple)
 			Assert(!isNull);
 			/* FALLTHROUGH */
 		case 2:
-			cur_skey[1].sk_argument =
+			arguments[1] =
 				(cache->cc_key[1] == ObjectIdAttributeNumber)
 				? ObjectIdGetDatum(HeapTupleGetOid(tuple))
 				: fastgetattr(tuple,
@@ -269,7 +266,7 @@ CatalogCacheComputeTupleHashValue(CatCache *cache, HeapTuple tuple)
 			Assert(!isNull);
 			/* FALLTHROUGH */
 		case 1:
-			cur_skey[0].sk_argument =
+			arguments[0] =
 				(cache->cc_key[0] == ObjectIdAttributeNumber)
 				? ObjectIdGetDatum(HeapTupleGetOid(tuple))
 				: fastgetattr(tuple,
@@ -283,7 +280,7 @@ CatalogCacheComputeTupleHashValue(CatCache *cache, HeapTuple tuple)
 			break;
 	}
 
-	return CatalogCacheComputeHashValue(cache, cache->cc_nkeys, cur_skey);
+	return CatalogCacheComputeHashValue(cache, cache->cc_nkeys, arguments);
 }
 
 
@@ -1096,6 +1093,7 @@ SearchCatCache(CatCache *cache,
 			   Datum v4)
 {
 	ScanKeyData cur_skey[CATCACHE_MAXKEYS];
+	Datum		arguments[CATCACHE_MAXKEYS];
 	uint32		hashValue;
 	Index		hashIndex;
 	dlist_iter	iter;
@@ -1118,19 +1116,16 @@ SearchCatCache(CatCache *cache,
 	cache->cc_searches++;
 #endif
 
-	/*
-	 * initialize the search key information
-	 */
-	memcpy(cur_skey, cache->cc_skey, sizeof(cur_skey));
-	cur_skey[0].sk_argument = v1;
-	cur_skey[1].sk_argument = v2;
-	cur_skey[2].sk_argument = v3;
-	cur_skey[3].sk_argument = v4;
+	/* Initialize local parameter array */
+	arguments[0] = v1;
+	arguments[1] = v2;
+	arguments[2] = v3;
+	arguments[3] = v4;
 
 	/*
 	 * find the hash bucket in which to look for the tuple
 	 */
-	hashValue = CatalogCacheComputeHashValue(cache, cache->cc_nkeys, cur_skey);
+	hashValue = CatalogCacheComputeHashValue(cache, cache->cc_nkeys, arguments);
 	hashIndex = HASH_INDEX(hashValue, cache->cc_nbuckets);
 
 	/*
@@ -1155,11 +1150,12 @@ SearchCatCache(CatCache *cache,
 		/*
 		 * see if the cached tuple matches our key.
 		 */
-		HeapKeyTest(&ct->tuple,
-					cache->cc_tupdesc,
-					cache->cc_nkeys,
-					cur_skey,
-					res);
+		HeapKeyTestArg(&ct->tuple,
+					   cache->cc_tupdesc,
+					   cache->cc_nkeys,
+					   cache->cc_skey,
+					   arguments,
+					   res);
 		if (!res)
 			continue;
 
@@ -1204,6 +1200,16 @@ SearchCatCache(CatCache *cache,
 	}
 
 	/*
+	 * Ok, need to make a lookup in the relation, copy the scankey and fill out
+	 * any per-call fields.
+	 */
+	memcpy(cur_skey, cache->cc_skey, sizeof(ScanKeyData) * cache->cc_nkeys);
+	cur_skey[0].sk_argument = v1;
+	cur_skey[1].sk_argument = v2;
+	cur_skey[2].sk_argument = v3;
+	cur_skey[3].sk_argument = v4;
+
+	/*
 	 * Tuple was not found in cache, so we have to try to retrieve it directly
 	 * from the relation.  If found, we will add it to the cache; if not
 	 * found, we will add a negative cache entry instead.
@@ -1341,7 +1347,7 @@ GetCatCacheHashValue(CatCache *cache,
 					 Datum v3,
 					 Datum v4)
 {
-	ScanKeyData cur_skey[CATCACHE_MAXKEYS];
+	Datum		arguments[CATCACHE_MAXKEYS];
 
 	/*
 	 * one-time startup overhead for each cache
@@ -1349,19 +1355,16 @@ GetCatCacheHashValue(CatCache *cache,
 	if (cache->cc_tupdesc == NULL)
 		CatalogCacheInitializeCache(cache);
 
-	/*
-	 * initialize the search key information
-	 */
-	memcpy(cur_skey, cache->cc_skey, sizeof(cur_skey));
-	cur_skey[0].sk_argument = v1;
-	cur_skey[1].sk_argument = v2;
-	cur_skey[2].sk_argument = v3;
-	cur_skey[3].sk_argument = v4;
+	/* Initialize local parameter array */
+	arguments[0] = v1;
+	arguments[1] = v2;
+	arguments[2] = v3;
+	arguments[3] = v4;
 
 	/*
 	 * calculate the hash value
 	 */
-	return CatalogCacheComputeHashValue(cache, cache->cc_nkeys, cur_skey);
+	return CatalogCacheComputeHashValue(cache, cache->cc_nkeys, arguments);
 }
 
 
@@ -1382,7 +1385,7 @@ SearchCatCacheList(CatCache *cache,
 				   Datum v3,
 				   Datum v4)
 {
-	ScanKeyData cur_skey[CATCACHE_MAXKEYS];
+	Datum		arguments[CATCACHE_MAXKEYS];
 	uint32		lHashValue;
 	dlist_iter	iter;
 	CatCList   *cl;
@@ -1407,21 +1410,18 @@ SearchCatCacheList(CatCache *cache,
 	cache->cc_lsearches++;
 #endif
 
-	/*
-	 * initialize the search key information
-	 */
-	memcpy(cur_skey, cache->cc_skey, sizeof(cur_skey));
-	cur_skey[0].sk_argument = v1;
-	cur_skey[1].sk_argument = v2;
-	cur_skey[2].sk_argument = v3;
-	cur_skey[3].sk_argument = v4;
+	/* Initialize local parameter array */
+	arguments[0] = v1;
+	arguments[1] = v2;
+	arguments[2] = v3;
+	arguments[3] = v4;
 
 	/*
 	 * compute a hash value of the given keys for faster search.  We don't
 	 * presently divide the CatCList items into buckets, but this still lets
 	 * us skip non-matching items quickly most of the time.
 	 */
-	lHashValue = CatalogCacheComputeHashValue(cache, nkeys, cur_skey);
+	lHashValue = CatalogCacheComputeHashValue(cache, nkeys, arguments);
 
 	/*
 	 * scan the items until we find a match or exhaust our list
@@ -1446,11 +1446,12 @@ SearchCatCacheList(CatCache *cache,
 		 */
 		if (cl->nkeys != nkeys)
 			continue;
-		HeapKeyTest(&cl->tuple,
-					cache->cc_tupdesc,
-					nkeys,
-					cur_skey,
-					res);
+		HeapKeyTestArg(&cl->tuple,
+					   cache->cc_tupdesc,
+					   nkeys,
+					   cache->cc_skey,
+					   arguments,
+					   res);
 		if (!res)
 			continue;
 
@@ -1494,9 +1495,20 @@ SearchCatCacheList(CatCache *cache,
 
 	PG_TRY();
 	{
+		ScanKeyData cur_skey[CATCACHE_MAXKEYS];
 		Relation	relation;
 		SysScanDesc scandesc;
 
+		/*
+		 * Ok, need to make a lookup in the relation, copy the scankey and fill out
+		 * any per-call fields.
+		 */
+		memcpy(cur_skey, cache->cc_skey, sizeof(ScanKeyData) * cache->cc_nkeys);
+		cur_skey[0].sk_argument = v1;
+		cur_skey[1].sk_argument = v2;
+		cur_skey[2].sk_argument = v3;
+		cur_skey[3].sk_argument = v4;
+
 		relation = heap_open(cache->cc_reloid, AccessShareLock);
 
 		scandesc = systable_beginscan(relation,
diff --git a/src/include/access/valid.h b/src/include/access/valid.h
index 8d9630a..4adcdfa 100644
--- a/src/include/access/valid.h
+++ b/src/include/access/valid.h
@@ -66,4 +66,60 @@ do \
 	} \
 } while (0)
 
+/*
+ *		HeapKeyTestArg
+ *
+ *		Like HeapKeyTest test a heap tuple to see if it satisfies a scan key,
+ *		but get the actual data from the separate 'arguments' parameter. That
+ *		can be advantageous if it allows not to modify the scankey.
+ */
+#define HeapKeyTestArg(tuple, \
+					tupdesc, \
+					nkeys, \
+					keys, \
+					arguments, \
+					result) \
+do \
+{ \
+	/* Use underscores to protect the variables passed in as parameters */ \
+	int			__cur_nkeys = (nkeys); \
+	ScanKey		__cur_keys = (keys); \
+	Datum	   *__cur_args = (arguments); \
+ \
+	(result) = true; /* may change */ \
+	for (; __cur_nkeys--; __cur_keys++, __cur_args++)		\
+	{ \
+		Datum	__atp; \
+		bool	__isnull; \
+		Datum	__test; \
+ \
+		if (__cur_keys->sk_flags & SK_ISNULL) \
+		{ \
+			(result) = false; \
+			break; \
+		} \
+ \
+		__atp = heap_getattr((tuple), \
+							 __cur_keys->sk_attno, \
+							 (tupdesc), \
+							 &__isnull); \
+ \
+		if (__isnull) \
+		{ \
+			(result) = false; \
+			break; \
+		} \
+ \
+		__test = FunctionCall2Coll(&__cur_keys->sk_func, \
+								   __cur_keys->sk_collation, \
+								   __atp, *__cur_args); \
+ \
+		if (!DatumGetBool(__test)) \
+		{ \
+			(result) = false; \
+			break; \
+		} \
+	} \
+} while (0)
+
 #endif   /* VALID_H */
-- 
1.8.3.251.g1462b67

-- 
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

Reply via email to