From: Andres Freund Date: Thu, 5 Sep 2013 18:30:44 +0000 (+0200) Subject: Improve catalog cache lookup performance. X-Git-Url: http://git.postgresql.org/gitweb/static/gitweb.js?a=commitdiff_plain;h=407391f01033d40f93a0572c6f806002deb6139c;p=users%2Fandresfreund%2Fpostgres.git 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. --- diff --git a/src/backend/utils/cache/catcache.c b/src/backend/utils/cache/catcache.c index eca3f97af7..c70b6c2292 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); } @@ -1112,6 +1109,7 @@ SearchCatCache(CatCache *cache, Datum v4) { ScanKeyData cur_skey[CATCACHE_MAXKEYS]; + Datum arguments[CATCACHE_MAXKEYS]; uint32 hashValue; Index hashIndex; dlist_iter iter; @@ -1134,19 +1132,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); /* @@ -1171,11 +1166,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; @@ -1219,6 +1215,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 @@ -1357,7 +1363,7 @@ GetCatCacheHashValue(CatCache *cache, Datum v3, Datum v4) { - ScanKeyData cur_skey[CATCACHE_MAXKEYS]; + Datum arguments[CATCACHE_MAXKEYS]; /* * one-time startup overhead for each cache @@ -1365,19 +1371,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); } @@ -1398,7 +1401,7 @@ SearchCatCacheList(CatCache *cache, Datum v3, Datum v4) { - ScanKeyData cur_skey[CATCACHE_MAXKEYS]; + Datum arguments[CATCACHE_MAXKEYS]; uint32 lHashValue; dlist_iter iter; CatCList *cl; @@ -1423,21 +1426,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 @@ -1462,11 +1462,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; @@ -1510,9 +1511,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 7a83395492..9365f2f6d1 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 */