Patch: ResourceOwner optimization for tables with many partitions

Started by Aleksander Alekseevabout 10 years ago21 messages
#1Aleksander Alekseev
a.alekseev@postgrespro.ru
3 attachment(s)

Hello all,

Current implementation of ResourceOwner uses arrays to store resources
like TupleDescs, Snapshots, etc. When we want to release one of these
resources ResourceOwner finds it with linear scan. Granted, resource
array are usually small so its not a problem most of the time. But it
appears to be a bottleneck when we are working with tables which have a
lot of partitions.

To reproduce this issue:
1. run `./gen.pl 10000 | psql my_database postgres`
2. run `pgbench -j 8 -c 8 -f q.sql -T 100 my_database`
3. in second terminal run `sudo perf top -u postgres`

Both gen.pl and q.sql are attached to this message.

You will see that postgres spends a lot of time in ResourceOwnerForget*
procedures:

32.80% postgres [.] list_nth
20.29% postgres [.] ResourceOwnerForgetRelationRef
12.87% postgres [.] find_all_inheritors
7.90% postgres [.] get_tabstat_entry
6.68% postgres [.] ResourceOwnerForgetTupleDesc
1.17% postgres [.] hash_search_with_hash_value
... < 1% ...

I would like to suggest a patch (see attachment) witch fixes this
bottleneck. Also I discovered that there is a lot of code duplication in
ResourceOwner. Patch fixes this too. The idea is quite simple. I just
replaced arrays with something that could be considered hash tables,
but with some specific optimizations.

After applying this patch we can see that bottleneck is gone:

42.89% postgres [.] list_nth
18.30% postgres [.] find_all_inheritors
10.97% postgres [.] get_tabstat_entry
1.82% postgres [.] hash_search_with_hash_value
1.21% postgres [.] SearchCatCache
... < 1% ...

For tables with thousands partitions we gain in average 32.5% more TPS.

As far as I can see in the same time patch doesn't make anything worse.
`make check` passes with asserts enabled and disabled. There is no
performance degradation according to both standard pgbench benchmark
and benchmark described above for tables with 10 and 100 partitions.

Best regards,
Aleksander

Attachments:

gen.plapplication/x-perlDownload
q.sqlapplication/sqlDownload
resource-owner-optimization.patchtext/x-patchDownload
diff --git a/src/backend/utils/resowner/resowner.c b/src/backend/utils/resowner/resowner.c
index 0e7acbf..6abbfa5 100644
--- a/src/backend/utils/resowner/resowner.c
+++ b/src/backend/utils/resowner/resowner.c
@@ -47,62 +47,296 @@
 #define MAX_RESOWNER_LOCKS 15
 
 /*
- * ResourceOwner objects look like this
+ * This number is used as initial size of resource arrays (like buffers,
+ * catrefs, etc) in ResourceOwnerData structure. If given number of items is
+ * not enough, we double array size and reallocate memory.
+ *
+ * Should be power of two since we use (arrsize -1) as mask for hash value.
+ *
  */
-typedef struct ResourceOwnerData
+#define RESARRAY_INIT_SIZE 16
+
+/*
+ * How many items could be stored in a resource array of given capacity. If
+ * this number is reached we need to resize an array to prevent hash collisions.
+ *
+ * This computation actually costs only two additions and one binary shift.
+ */
+#define RESARRAY_MAX_ITEMS(capacity) ((capacity)*3/4)
+
+/*
+ * Common structure for storing different type of resources.
+ */
+typedef struct ResourceArray
+{
+	uint8*		itemsarr;		/* buffer for storing values */
+	uint32		itemsizelg:  2;	/* sizeof one item log 2 */
+	uint32		capacity:   30;	/* capacity of array */
+	uint32		nitems;			/* how many items is stored in items array */
+	uint32		maxitems;		/* precalculated RESARRAY_MAX_ITEMS(capacity) */
+} ResourceArray;
+
+/*
+ * Such type of callback function is called when resource stored in
+ * ResourceArray is released using ResourceArrayFree. Callback should
+ * _actually_ release a resource so nitems value will be decremented.
+ */
+typedef void (*ResourceArrayRemoveCallback)(const uint8* ref, bool isCommit);
+
+/* Used as argument to memcmp to determine if ResourceArray[i] is free. */
+static const uint8 RESOURCE_ARRAY_ZERO_ELEMENT[sizeof(void*)] = {0};
+
+/*
+ * Calculate hash_any of given data. For uint32 values use faster hash_uint32.
+ */
+static Datum
+ResourceArrayHash(const uint8* data, int size)
+{
+	uint32		tmp;
+
+	Assert(size == sizeof(uint32) || size == sizeof(void*));
+
+	if(size == sizeof(uint32))
+	{
+		tmp = *((const uint32*)data);
+		return hash_uint32(tmp);
+	}
+	else
+	{
+		return hash_any(data, size);
+	}
+}
+
+/*
+ * Initialize ResourceArray
+ */
+static void
+ResourceArrayInit(ResourceArray *resarr, uint32 itemsize) {
+	Assert(itemsize == sizeof(int) || itemsize == sizeof(void*));
+	Assert(resarr->itemsarr == NULL);
+	Assert(resarr->capacity == 0);
+	Assert(resarr->nitems == 0);
+	Assert(resarr->maxitems == 0);
+
+	resarr->itemsizelg = 0;
+	while(itemsize > 1)
+	{
+		resarr->itemsizelg++;
+		itemsize >>= 1;
+	}
+
+	Assert(resarr->itemsizelg == 2 || resarr->itemsizelg == 3);
+}
+
+/*
+ * Add a resource to ResourceArray
+ *
+ * Caller must have previously done ResourceArrayEnlarge()
+ */
+static void
+ResourceArrayAdd(ResourceArray *resarr, const uint8 *dataref)
+{
+	uint8 		*itemptr;
+	Datum 		idx;
+	Datum 		mask = resarr->capacity - 1;
+	uint32 		itemsize = 1 << resarr->itemsizelg;
+
+	Assert(memcmp(dataref, RESOURCE_ARRAY_ZERO_ELEMENT, itemsize) != 0);
+	Assert(resarr->maxitems > resarr->nitems);
+	Assert(resarr->capacity > 0);
+	Assert(resarr->itemsarr != NULL);
+
+	/*
+	 * Hashing is quite expensive, so we use it only for large arrays. For
+	 * small arrays we just use a linear scan.
+	 */
+	if(resarr->capacity == RESARRAY_INIT_SIZE)
+		idx = resarr->nitems;
+	else
+		idx = ResourceArrayHash(dataref, itemsize);
+	idx &= mask;
+
+	while(true)
+	{
+		itemptr = resarr->itemsarr + (idx << resarr->itemsizelg);
+		if(memcmp(itemptr, RESOURCE_ARRAY_ZERO_ELEMENT, itemsize) == 0)
+			break;
+		idx = (idx + 1) & mask;
+	}
+
+	memcpy(itemptr, dataref, itemsize);
+	resarr->nitems++;
+}
+
+/*
+ * Remove a resource from ResourceArray
+ *
+ * Returns true on success, false if resource was not found
+ */
+static bool
+ResourceArrayRemove(ResourceArray *resarr, const uint8 *dataref)
+{
+	uint8 		*itemptr;
+	Datum		idx;
+	uint32		i;
+	Datum		mask = resarr->capacity - 1;
+	uint32 		itemsize = 1 << resarr->itemsizelg;
+
+	Assert(memcmp(dataref, RESOURCE_ARRAY_ZERO_ELEMENT, itemsize) != 0);
+	Assert(resarr->capacity > 0);
+	Assert(resarr->itemsarr != NULL);
+
+	/*
+	 * Hashing is quite expensive, so we use it only for large arrays. For
+	 * small arrays we use a linear scan.
+	 */
+	if(resarr->capacity == RESARRAY_INIT_SIZE)
+		idx = 0;
+	else
+		idx = ResourceArrayHash(dataref, itemsize);
+	idx &= mask;
+
+	for(i = 0; i < resarr->capacity; i++)
+	{
+		itemptr = resarr->itemsarr + (idx << resarr->itemsizelg);
+		if(memcmp(itemptr, dataref, itemsize) == 0)
+		{
+			memset(itemptr, 0, itemsize);
+			resarr->nitems--;
+			return true;
+		}
+		idx = (idx + 1) & mask;
+	}
+
+	return false;
+}
+
+/*
+ * Make sure there is a room for at least one more resource in an array.
+ *
+ * This is separate from actually inserting a resource because if we run out
+ * of memory, it's critical to do so *before* acquiring the resource.
+ */
+static void
+ResourceArrayEnlarge(ResourceArray* resarr)
+{
+	uint32		oldcap;
+	uint8*		olditemsarr;
+	uint8*		olditemptr;
+	uint32		itemsize = 1 << resarr->itemsizelg;
+
+	Assert(resarr->itemsizelg != 0);
+
+	if(resarr->nitems < resarr->maxitems)
+		return;	/* nothing to do */
+
+	olditemsarr = resarr->itemsarr;
+	oldcap = resarr->capacity;
+
+	resarr->capacity = oldcap > 0 ? oldcap * 2 : RESARRAY_INIT_SIZE;
+	resarr->itemsarr = (uint8*)
+		MemoryContextAllocZero(TopMemoryContext,
+							   resarr->capacity * itemsize);
+	resarr->maxitems = RESARRAY_MAX_ITEMS(resarr->capacity);
+	resarr->nitems = 0;
+
+	if(olditemsarr != NULL)
+	{
+		while(oldcap > 0)
+		{
+			oldcap--;
+			/*
+			 * Read next line as:
+			 *
+			 *   olditemptr = &(olditemsarr[oldcap]).
+			 *
+			 * We use binary shift since compiler don't know that itemsize
+			 * is always power of two. It would use multiplication instead of
+			 * efficient binary shift in code `oldcap * itemsize`.
+			 */
+			olditemptr = olditemsarr + (oldcap << resarr->itemsizelg);
+			if(memcmp(olditemptr, RESOURCE_ARRAY_ZERO_ELEMENT, itemsize) != 0)
+				ResourceArrayAdd(resarr, olditemptr);
+		}
+		pfree(olditemsarr);
+	}
+}
+
+/*
+ * Remove all resources in array and call a callback function for each release.
+ */
+static void
+ResourceArrayRemoveAll(ResourceArray *resarr,
+					   ResourceArrayRemoveCallback releasecb,
+					   bool isCommit)
+{
+	uint32		idx = 0;
+	uint8*		itemptr;
+	uint32		itemsize = 1 << resarr->itemsizelg;
+
+	while(resarr->nitems > 0)
+	{
+		/*
+		 * Read next line as:
+		 *
+		 *   itemptr = &(itemsarr[idx]).
+		 *
+		 * We use binary shift since compiler don't know that itemsize
+		 * is always power of two. It would use multiplication instead of
+		 * efficient binary shift in code `oldcap * itemsize`.
+		 */
+		itemptr = resarr->itemsarr + (idx << resarr->itemsizelg);
+		if(memcmp(itemptr, RESOURCE_ARRAY_ZERO_ELEMENT, itemsize) == 0)
+		{
+			idx++;
+			continue;
+		}
+		releasecb(itemptr, isCommit);
+	}
+}
+
+/*
+ * Return ResourceArray to initial state
+ */
+static void
+ResourceArrayFree(ResourceArray *resarr)
 {
-	ResourceOwner parent;		/* NULL if no parent (toplevel owner) */
-	ResourceOwner firstchild;	/* head of linked list of children */
-	ResourceOwner nextchild;	/* next child of same parent */
-	const char *name;			/* name (just for debugging) */
+	Assert(resarr->nitems == 0);
+
+	resarr->capacity = 0;
+	resarr->maxitems = 0;
 
-	/* We have built-in support for remembering owned buffers */
-	int			nbuffers;		/* number of owned buffer pins */
-	Buffer	   *buffers;		/* dynamically allocated array */
-	int			maxbuffers;		/* currently allocated array size */
+	if(!resarr->itemsarr)
+		return;
+
+	pfree(resarr->itemsarr);
+	resarr->itemsarr = NULL;
+}
+
+/* ResourceOwner objects look like this */
+typedef struct ResourceOwnerData
+{
+	ResourceOwner parent;			/* NULL if no parent (toplevel owner) */
+	ResourceOwner firstchild;		/* head of linked list of children */
+	ResourceOwner nextchild;		/* next child of same parent */
+	const char *name;				/* name (just for debugging) */
 
 	/* We can remember up to MAX_RESOWNER_LOCKS references to local locks. */
-	int			nlocks;			/* number of owned locks */
+	int			nlocks;				/* number of owned locks */
 	LOCALLOCK  *locks[MAX_RESOWNER_LOCKS];		/* list of owned locks */
 
-	/* We have built-in support for remembering catcache references */
-	int			ncatrefs;		/* number of owned catcache pins */
-	HeapTuple  *catrefs;		/* dynamically allocated array */
-	int			maxcatrefs;		/* currently allocated array size */
-
-	int			ncatlistrefs;	/* number of owned catcache-list pins */
-	CatCList  **catlistrefs;	/* dynamically allocated array */
-	int			maxcatlistrefs; /* currently allocated array size */
-
-	/* We have built-in support for remembering relcache references */
-	int			nrelrefs;		/* number of owned relcache pins */
-	Relation   *relrefs;		/* dynamically allocated array */
-	int			maxrelrefs;		/* currently allocated array size */
-
-	/* We have built-in support for remembering plancache references */
-	int			nplanrefs;		/* number of owned plancache pins */
-	CachedPlan **planrefs;		/* dynamically allocated array */
-	int			maxplanrefs;	/* currently allocated array size */
-
-	/* We have built-in support for remembering tupdesc references */
-	int			ntupdescs;		/* number of owned tupdesc references */
-	TupleDesc  *tupdescs;		/* dynamically allocated array */
-	int			maxtupdescs;	/* currently allocated array size */
-
-	/* We have built-in support for remembering snapshot references */
-	int			nsnapshots;		/* number of owned snapshot references */
-	Snapshot   *snapshots;		/* dynamically allocated array */
-	int			maxsnapshots;	/* currently allocated array size */
-
-	/* We have built-in support for remembering open temporary files */
-	int			nfiles;			/* number of owned temporary files */
-	File	   *files;			/* dynamically allocated array */
-	int			maxfiles;		/* currently allocated array size */
-
-	/* We have built-in support for remembering dynamic shmem segments */
-	int			ndsms;			/* number of owned shmem segments */
-	dsm_segment **dsms;			/* dynamically allocated array */
-	int			maxdsms;		/* currently allocated array size */
+	/* We have built-in support for remembering: */
+
+	ResourceArray catrefarr;		/* `HeapTuple`s */
+	ResourceArray catlistrefarr; 	/* `ResourceOwner`s */
+	ResourceArray relrefarr;		/* `Relation`s */
+	ResourceArray planrefarr;		/* `CachedPlan*`s */
+	ResourceArray tupdescarr;		/* `TupleDesc`s */
+	ResourceArray snapshotarr;		/* `Snapshot`s */
+	ResourceArray dsmarr;			/* `dsm_segment*`s */
+	ResourceArray bufferarr; 		/* `Buffer`s  */
+	ResourceArray filearr; 			/* `File`s */
+
 }	ResourceOwnerData;
 
 
@@ -168,6 +402,16 @@ ResourceOwnerCreate(ResourceOwner parent, const char *name)
 		parent->firstchild = owner;
 	}
 
+	ResourceArrayInit(&(owner->catrefarr), sizeof(HeapTuple));
+	ResourceArrayInit(&(owner->catlistrefarr), sizeof(ResourceOwner));
+	ResourceArrayInit(&(owner->relrefarr), sizeof(Relation));
+	ResourceArrayInit(&(owner->planrefarr), sizeof(CachedPlan*));
+	ResourceArrayInit(&(owner->tupdescarr), sizeof(TupleDesc));
+	ResourceArrayInit(&(owner->snapshotarr), sizeof(Snapshot));
+	ResourceArrayInit(&(owner->dsmarr), sizeof(dsm_segment*));
+	ResourceArrayInit(&(owner->bufferarr), sizeof(Buffer));
+	ResourceArrayInit(&(owner->filearr), sizeof(File));
+
 	return owner;
 }
 
@@ -221,6 +465,87 @@ ResourceOwnerRelease(ResourceOwner owner,
 }
 
 static void
+ReleaseCachedPlanCallback(const uint8* dataref, bool isCommit)
+{
+	CachedPlan* res = *((CachedPlan**)dataref);
+	if (isCommit)
+		PrintPlanCacheLeakWarning(res);
+	ReleaseCachedPlan(res, true);
+}
+
+static void
+ReleaseDSMCallback(const uint8* dataref, bool isCommit)
+{
+	dsm_segment* res = *((dsm_segment**)dataref);
+	if (isCommit)
+		PrintDSMLeakWarning(res);
+	dsm_detach(res);
+}
+
+static void
+ReleaseTupleDescCallback(const uint8* dataref, bool isCommit)
+{
+	TupleDesc 	res = *((TupleDesc*)dataref);
+	if(isCommit)
+		PrintTupleDescLeakWarning(res);
+	DecrTupleDescRefCount(res);
+}
+
+static void
+ReleaseSnapshotCallback(const uint8* dataref, bool isCommit)
+{
+	Snapshot 	res = *((Snapshot*)dataref);
+	if (isCommit)
+		PrintSnapshotLeakWarning(res);
+	UnregisterSnapshot(res);
+}
+
+static void
+ReleaseRelationCallback(const uint8* dataref, bool isCommit)
+{
+	Relation 	res = *((Relation*)dataref);
+	if (isCommit)
+		PrintRelCacheLeakWarning(res);
+	RelationClose(res);
+}
+
+static void
+ReleaseCatCacheListCallback(const uint8* dataref, bool isCommit)
+{
+	CatCList* 	res = *((CatCList**)dataref);
+	if (isCommit)
+		PrintCatCacheListLeakWarning(res);
+	ReleaseCatCacheList(res);
+}
+
+static void
+ReleaseCatCacheCallback(const uint8* dataref, bool isCommit)
+{
+	HeapTuple 	res = *((HeapTuple*)dataref);
+	if (isCommit)
+		PrintCatCacheLeakWarning(res);
+	ReleaseCatCache(res);
+}
+
+static void
+ReleaseBufferCallback(const uint8* dataref, bool isCommit)
+{
+	Buffer 		res = *((Buffer*)dataref);
+	if (isCommit)
+		PrintBufferLeakWarning(res);
+	ReleaseBuffer(res);
+}
+
+static void
+ReleaseFileCallback(const uint8* dataref, bool isCommit)
+{
+	File 		res = *((File*)dataref);
+	if (isCommit)
+		PrintFileLeakWarning(res);
+	FileClose(res);
+}
+
+static void
 ResourceOwnerReleaseInternal(ResourceOwner owner,
 							 ResourceReleasePhase phase,
 							 bool isCommit,
@@ -252,46 +577,17 @@ ResourceOwnerReleaseInternal(ResourceOwner owner,
 		 * During a commit, there shouldn't be any remaining pins --- that
 		 * would indicate failure to clean up the executor correctly --- so
 		 * issue warnings.  In the abort case, just clean up quietly.
-		 *
-		 * We are careful to do the releasing back-to-front, so as to avoid
-		 * O(N^2) behavior in ResourceOwnerForgetBuffer().
 		 */
-		while (owner->nbuffers > 0)
-		{
-			if (isCommit)
-				PrintBufferLeakWarning(owner->buffers[owner->nbuffers - 1]);
-			ReleaseBuffer(owner->buffers[owner->nbuffers - 1]);
-		}
+		ResourceArrayRemoveAll(&(owner->bufferarr),
+							   ReleaseBufferCallback, isCommit);
 
-		/*
-		 * Release relcache references.  Note that RelationClose will remove
-		 * the relref entry from my list, so I just have to iterate till there
-		 * are none.
-		 *
-		 * As with buffer pins, warn if any are left at commit time, and
-		 * release back-to-front for speed.
-		 */
-		while (owner->nrelrefs > 0)
-		{
-			if (isCommit)
-				PrintRelCacheLeakWarning(owner->relrefs[owner->nrelrefs - 1]);
-			RelationClose(owner->relrefs[owner->nrelrefs - 1]);
-		}
+		/* Ditto for relcache references. */
+		ResourceArrayRemoveAll(&(owner->relrefarr),
+							   ReleaseRelationCallback, isCommit);
 
-		/*
-		 * Release dynamic shared memory segments.  Note that dsm_detach()
-		 * will remove the segment from my list, so I just have to iterate
-		 * until there are none.
-		 *
-		 * As in the preceding cases, warn if there are leftover at commit
-		 * time.
-		 */
-		while (owner->ndsms > 0)
-		{
-			if (isCommit)
-				PrintDSMLeakWarning(owner->dsms[owner->ndsms - 1]);
-			dsm_detach(owner->dsms[owner->ndsms - 1]);
-		}
+		/* Ditto for dynamic shared memory segments */
+		ResourceArrayRemoveAll(&(owner->dsmarr),
+							   ReleaseDSMCallback, isCommit);
 	}
 	else if (phase == RESOURCE_RELEASE_LOCKS)
 	{
@@ -351,48 +647,28 @@ ResourceOwnerReleaseInternal(ResourceOwner owner,
 		 * As with buffer pins, warn if any are left at commit time, and
 		 * release back-to-front for speed.
 		 */
-		while (owner->ncatrefs > 0)
-		{
-			if (isCommit)
-				PrintCatCacheLeakWarning(owner->catrefs[owner->ncatrefs - 1]);
-			ReleaseCatCache(owner->catrefs[owner->ncatrefs - 1]);
-		}
+		ResourceArrayRemoveAll(&(owner->catrefarr),
+							   ReleaseCatCacheCallback, isCommit);
+
 		/* Ditto for catcache lists */
-		while (owner->ncatlistrefs > 0)
-		{
-			if (isCommit)
-				PrintCatCacheListLeakWarning(owner->catlistrefs[owner->ncatlistrefs - 1]);
-			ReleaseCatCacheList(owner->catlistrefs[owner->ncatlistrefs - 1]);
-		}
+		ResourceArrayRemoveAll(&(owner->catlistrefarr),
+							   ReleaseCatCacheListCallback, isCommit);
+
 		/* Ditto for plancache references */
-		while (owner->nplanrefs > 0)
-		{
-			if (isCommit)
-				PrintPlanCacheLeakWarning(owner->planrefs[owner->nplanrefs - 1]);
-			ReleaseCachedPlan(owner->planrefs[owner->nplanrefs - 1], true);
-		}
+		ResourceArrayRemoveAll(&(owner->planrefarr),
+							   ReleaseCachedPlanCallback, isCommit);
+
 		/* Ditto for tupdesc references */
-		while (owner->ntupdescs > 0)
-		{
-			if (isCommit)
-				PrintTupleDescLeakWarning(owner->tupdescs[owner->ntupdescs - 1]);
-			DecrTupleDescRefCount(owner->tupdescs[owner->ntupdescs - 1]);
-		}
+		ResourceArrayRemoveAll(&(owner->tupdescarr),
+							   ReleaseTupleDescCallback, isCommit);
+
 		/* Ditto for snapshot references */
-		while (owner->nsnapshots > 0)
-		{
-			if (isCommit)
-				PrintSnapshotLeakWarning(owner->snapshots[owner->nsnapshots - 1]);
-			UnregisterSnapshot(owner->snapshots[owner->nsnapshots - 1]);
-		}
+		ResourceArrayRemoveAll(&(owner->snapshotarr),
+							   ReleaseSnapshotCallback, isCommit);
 
 		/* Ditto for temporary files */
-		while (owner->nfiles > 0)
-		{
-			if (isCommit)
-				PrintFileLeakWarning(owner->files[owner->nfiles - 1]);
-			FileClose(owner->files[owner->nfiles - 1]);
-		}
+		ResourceArrayRemoveAll(&(owner->filearr),
+							   ReleaseFileCallback, isCommit);
 
 		/* Clean up index scans too */
 		ReleaseResources_hash();
@@ -418,16 +694,7 @@ ResourceOwnerDelete(ResourceOwner owner)
 	Assert(owner != CurrentResourceOwner);
 
 	/* And it better not own any resources, either */
-	Assert(owner->nbuffers == 0);
 	Assert(owner->nlocks == 0 || owner->nlocks == MAX_RESOWNER_LOCKS + 1);
-	Assert(owner->ncatrefs == 0);
-	Assert(owner->ncatlistrefs == 0);
-	Assert(owner->nrelrefs == 0);
-	Assert(owner->ndsms == 0);
-	Assert(owner->nplanrefs == 0);
-	Assert(owner->ntupdescs == 0);
-	Assert(owner->nsnapshots == 0);
-	Assert(owner->nfiles == 0);
 
 	/*
 	 * Delete children.  The recursive call will delink the child from me, so
@@ -444,25 +711,15 @@ ResourceOwnerDelete(ResourceOwner owner)
 	ResourceOwnerNewParent(owner, NULL);
 
 	/* And free the object. */
-	if (owner->buffers)
-		pfree(owner->buffers);
-	if (owner->catrefs)
-		pfree(owner->catrefs);
-	if (owner->catlistrefs)
-		pfree(owner->catlistrefs);
-	if (owner->relrefs)
-		pfree(owner->relrefs);
-	if (owner->planrefs)
-		pfree(owner->planrefs);
-	if (owner->tupdescs)
-		pfree(owner->tupdescs);
-	if (owner->snapshots)
-		pfree(owner->snapshots);
-	if (owner->files)
-		pfree(owner->files);
-	if (owner->dsms)
-		pfree(owner->dsms);
-
+	ResourceArrayFree(&(owner->catrefarr));
+	ResourceArrayFree(&(owner->catlistrefarr));
+	ResourceArrayFree(&(owner->relrefarr));
+	ResourceArrayFree(&(owner->planrefarr));
+	ResourceArrayFree(&(owner->tupdescarr));
+	ResourceArrayFree(&(owner->snapshotarr));
+	ResourceArrayFree(&(owner->dsmarr));
+	ResourceArrayFree(&(owner->bufferarr));
+	ResourceArrayFree(&(owner->filearr));
 	pfree(owner);
 }
 
@@ -575,26 +832,9 @@ UnregisterResourceReleaseCallback(ResourceReleaseCallback callback, void *arg)
 void
 ResourceOwnerEnlargeBuffers(ResourceOwner owner)
 {
-	int			newmax;
-
-	if (owner == NULL ||
-		owner->nbuffers < owner->maxbuffers)
-		return;					/* nothing to do */
-
-	if (owner->buffers == NULL)
-	{
-		newmax = 16;
-		owner->buffers = (Buffer *)
-			MemoryContextAlloc(TopMemoryContext, newmax * sizeof(Buffer));
-		owner->maxbuffers = newmax;
-	}
-	else
-	{
-		newmax = owner->maxbuffers * 2;
-		owner->buffers = (Buffer *)
-			repalloc(owner->buffers, newmax * sizeof(Buffer));
-		owner->maxbuffers = newmax;
-	}
+	if (owner == NULL)
+		return;
+	ResourceArrayEnlarge(&(owner->bufferarr));
 }
 
 /*
@@ -608,12 +848,9 @@ ResourceOwnerEnlargeBuffers(ResourceOwner owner)
 void
 ResourceOwnerRememberBuffer(ResourceOwner owner, Buffer buffer)
 {
-	if (owner != NULL)
-	{
-		Assert(owner->nbuffers < owner->maxbuffers);
-		owner->buffers[owner->nbuffers] = buffer;
-		owner->nbuffers++;
-	}
+	if (owner == NULL)
+		return;
+	ResourceArrayAdd(&(owner->bufferarr), (const uint8*)&buffer);
 }
 
 /*
@@ -625,33 +862,15 @@ ResourceOwnerRememberBuffer(ResourceOwner owner, Buffer buffer)
 void
 ResourceOwnerForgetBuffer(ResourceOwner owner, Buffer buffer)
 {
-	if (owner != NULL)
-	{
-		Buffer	   *buffers = owner->buffers;
-		int			nb1 = owner->nbuffers - 1;
-		int			i;
+	bool		res;
 
-		/*
-		 * Scan back-to-front because it's more likely we are releasing a
-		 * recently pinned buffer.  This isn't always the case of course, but
-		 * it's the way to bet.
-		 */
-		for (i = nb1; i >= 0; i--)
-		{
-			if (buffers[i] == buffer)
-			{
-				while (i < nb1)
-				{
-					buffers[i] = buffers[i + 1];
-					i++;
-				}
-				owner->nbuffers = nb1;
-				return;
-			}
-		}
+	if (owner == NULL)
+		return;
+
+	res = ResourceArrayRemove(&(owner->bufferarr), (const uint8*)&buffer);
+	if(!res)
 		elog(ERROR, "buffer %d is not owned by resource owner %s",
 			 buffer, owner->name);
-	}
 }
 
 /*
@@ -667,6 +886,8 @@ ResourceOwnerForgetBuffer(ResourceOwner owner, Buffer buffer)
 void
 ResourceOwnerRememberLock(ResourceOwner owner, LOCALLOCK *locallock)
 {
+	Assert(locallock != NULL);
+
 	if (owner->nlocks > MAX_RESOWNER_LOCKS)
 		return;					/* we have already overflowed */
 
@@ -714,25 +935,7 @@ ResourceOwnerForgetLock(ResourceOwner owner, LOCALLOCK *locallock)
 void
 ResourceOwnerEnlargeCatCacheRefs(ResourceOwner owner)
 {
-	int			newmax;
-
-	if (owner->ncatrefs < owner->maxcatrefs)
-		return;					/* nothing to do */
-
-	if (owner->catrefs == NULL)
-	{
-		newmax = 16;
-		owner->catrefs = (HeapTuple *)
-			MemoryContextAlloc(TopMemoryContext, newmax * sizeof(HeapTuple));
-		owner->maxcatrefs = newmax;
-	}
-	else
-	{
-		newmax = owner->maxcatrefs * 2;
-		owner->catrefs = (HeapTuple *)
-			repalloc(owner->catrefs, newmax * sizeof(HeapTuple));
-		owner->maxcatrefs = newmax;
-	}
+	ResourceArrayEnlarge(&(owner->catrefarr));
 }
 
 /*
@@ -743,9 +946,7 @@ ResourceOwnerEnlargeCatCacheRefs(ResourceOwner owner)
 void
 ResourceOwnerRememberCatCacheRef(ResourceOwner owner, HeapTuple tuple)
 {
-	Assert(owner->ncatrefs < owner->maxcatrefs);
-	owner->catrefs[owner->ncatrefs] = tuple;
-	owner->ncatrefs++;
+	ResourceArrayAdd(&(owner->catrefarr), (const uint8*)&tuple);
 }
 
 /*
@@ -754,25 +955,11 @@ ResourceOwnerRememberCatCacheRef(ResourceOwner owner, HeapTuple tuple)
 void
 ResourceOwnerForgetCatCacheRef(ResourceOwner owner, HeapTuple tuple)
 {
-	HeapTuple  *catrefs = owner->catrefs;
-	int			nc1 = owner->ncatrefs - 1;
-	int			i;
-
-	for (i = nc1; i >= 0; i--)
-	{
-		if (catrefs[i] == tuple)
-		{
-			while (i < nc1)
-			{
-				catrefs[i] = catrefs[i + 1];
-				i++;
-			}
-			owner->ncatrefs = nc1;
-			return;
-		}
-	}
-	elog(ERROR, "catcache reference %p is not owned by resource owner %s",
-		 tuple, owner->name);
+	bool 		res = ResourceArrayRemove(&(owner->catrefarr),
+										  (const uint8*)&tuple);
+	if(!res)
+		elog(ERROR, "catcache reference %p is not owned by resource owner %s",
+			 tuple, owner->name);
 }
 
 /*
@@ -785,25 +972,7 @@ ResourceOwnerForgetCatCacheRef(ResourceOwner owner, HeapTuple tuple)
 void
 ResourceOwnerEnlargeCatCacheListRefs(ResourceOwner owner)
 {
-	int			newmax;
-
-	if (owner->ncatlistrefs < owner->maxcatlistrefs)
-		return;					/* nothing to do */
-
-	if (owner->catlistrefs == NULL)
-	{
-		newmax = 16;
-		owner->catlistrefs = (CatCList **)
-			MemoryContextAlloc(TopMemoryContext, newmax * sizeof(CatCList *));
-		owner->maxcatlistrefs = newmax;
-	}
-	else
-	{
-		newmax = owner->maxcatlistrefs * 2;
-		owner->catlistrefs = (CatCList **)
-			repalloc(owner->catlistrefs, newmax * sizeof(CatCList *));
-		owner->maxcatlistrefs = newmax;
-	}
+	ResourceArrayEnlarge(&(owner->catlistrefarr));
 }
 
 /*
@@ -814,9 +983,7 @@ ResourceOwnerEnlargeCatCacheListRefs(ResourceOwner owner)
 void
 ResourceOwnerRememberCatCacheListRef(ResourceOwner owner, CatCList *list)
 {
-	Assert(owner->ncatlistrefs < owner->maxcatlistrefs);
-	owner->catlistrefs[owner->ncatlistrefs] = list;
-	owner->ncatlistrefs++;
+	ResourceArrayAdd(&(owner->catlistrefarr), (const uint8*)&list);
 }
 
 /*
@@ -825,25 +992,11 @@ ResourceOwnerRememberCatCacheListRef(ResourceOwner owner, CatCList *list)
 void
 ResourceOwnerForgetCatCacheListRef(ResourceOwner owner, CatCList *list)
 {
-	CatCList  **catlistrefs = owner->catlistrefs;
-	int			nc1 = owner->ncatlistrefs - 1;
-	int			i;
-
-	for (i = nc1; i >= 0; i--)
-	{
-		if (catlistrefs[i] == list)
-		{
-			while (i < nc1)
-			{
-				catlistrefs[i] = catlistrefs[i + 1];
-				i++;
-			}
-			owner->ncatlistrefs = nc1;
-			return;
-		}
-	}
-	elog(ERROR, "catcache list reference %p is not owned by resource owner %s",
-		 list, owner->name);
+	bool 		res = ResourceArrayRemove(&(owner->catlistrefarr),
+										  (const uint8*)&list);
+	if(!res)
+		elog(ERROR, "catcache list reference %p is not owned by resource owner %s",
+			 list, owner->name);
 }
 
 /*
@@ -856,25 +1009,7 @@ ResourceOwnerForgetCatCacheListRef(ResourceOwner owner, CatCList *list)
 void
 ResourceOwnerEnlargeRelationRefs(ResourceOwner owner)
 {
-	int			newmax;
-
-	if (owner->nrelrefs < owner->maxrelrefs)
-		return;					/* nothing to do */
-
-	if (owner->relrefs == NULL)
-	{
-		newmax = 16;
-		owner->relrefs = (Relation *)
-			MemoryContextAlloc(TopMemoryContext, newmax * sizeof(Relation));
-		owner->maxrelrefs = newmax;
-	}
-	else
-	{
-		newmax = owner->maxrelrefs * 2;
-		owner->relrefs = (Relation *)
-			repalloc(owner->relrefs, newmax * sizeof(Relation));
-		owner->maxrelrefs = newmax;
-	}
+	ResourceArrayEnlarge(&(owner->relrefarr));
 }
 
 /*
@@ -885,9 +1020,7 @@ ResourceOwnerEnlargeRelationRefs(ResourceOwner owner)
 void
 ResourceOwnerRememberRelationRef(ResourceOwner owner, Relation rel)
 {
-	Assert(owner->nrelrefs < owner->maxrelrefs);
-	owner->relrefs[owner->nrelrefs] = rel;
-	owner->nrelrefs++;
+	ResourceArrayAdd(&(owner->relrefarr), (const uint8*)&rel);
 }
 
 /*
@@ -896,25 +1029,11 @@ ResourceOwnerRememberRelationRef(ResourceOwner owner, Relation rel)
 void
 ResourceOwnerForgetRelationRef(ResourceOwner owner, Relation rel)
 {
-	Relation   *relrefs = owner->relrefs;
-	int			nr1 = owner->nrelrefs - 1;
-	int			i;
-
-	for (i = nr1; i >= 0; i--)
-	{
-		if (relrefs[i] == rel)
-		{
-			while (i < nr1)
-			{
-				relrefs[i] = relrefs[i + 1];
-				i++;
-			}
-			owner->nrelrefs = nr1;
-			return;
-		}
-	}
-	elog(ERROR, "relcache reference %s is not owned by resource owner %s",
-		 RelationGetRelationName(rel), owner->name);
+	bool		res = ResourceArrayRemove(&(owner->relrefarr),
+										  (const uint8*)&rel);
+	if(!res)
+		elog(ERROR, "relcache reference %s is not owned by resource owner %s",
+			 RelationGetRelationName(rel), owner->name);
 }
 
 /*
@@ -937,25 +1056,7 @@ PrintRelCacheLeakWarning(Relation rel)
 void
 ResourceOwnerEnlargePlanCacheRefs(ResourceOwner owner)
 {
-	int			newmax;
-
-	if (owner->nplanrefs < owner->maxplanrefs)
-		return;					/* nothing to do */
-
-	if (owner->planrefs == NULL)
-	{
-		newmax = 16;
-		owner->planrefs = (CachedPlan **)
-			MemoryContextAlloc(TopMemoryContext, newmax * sizeof(CachedPlan *));
-		owner->maxplanrefs = newmax;
-	}
-	else
-	{
-		newmax = owner->maxplanrefs * 2;
-		owner->planrefs = (CachedPlan **)
-			repalloc(owner->planrefs, newmax * sizeof(CachedPlan *));
-		owner->maxplanrefs = newmax;
-	}
+	ResourceArrayEnlarge(&(owner->planrefarr));
 }
 
 /*
@@ -966,9 +1067,7 @@ ResourceOwnerEnlargePlanCacheRefs(ResourceOwner owner)
 void
 ResourceOwnerRememberPlanCacheRef(ResourceOwner owner, CachedPlan *plan)
 {
-	Assert(owner->nplanrefs < owner->maxplanrefs);
-	owner->planrefs[owner->nplanrefs] = plan;
-	owner->nplanrefs++;
+	ResourceArrayAdd(&(owner->planrefarr), (const uint8*)&plan);
 }
 
 /*
@@ -977,25 +1076,11 @@ ResourceOwnerRememberPlanCacheRef(ResourceOwner owner, CachedPlan *plan)
 void
 ResourceOwnerForgetPlanCacheRef(ResourceOwner owner, CachedPlan *plan)
 {
-	CachedPlan **planrefs = owner->planrefs;
-	int			np1 = owner->nplanrefs - 1;
-	int			i;
-
-	for (i = np1; i >= 0; i--)
-	{
-		if (planrefs[i] == plan)
-		{
-			while (i < np1)
-			{
-				planrefs[i] = planrefs[i + 1];
-				i++;
-			}
-			owner->nplanrefs = np1;
-			return;
-		}
-	}
-	elog(ERROR, "plancache reference %p is not owned by resource owner %s",
-		 plan, owner->name);
+	bool 		res = ResourceArrayRemove(&(owner->planrefarr),
+										  (const uint8*)&plan);
+	if(!res)
+		elog(ERROR, "plancache reference %p is not owned by resource owner %s",
+			 plan, owner->name);
 }
 
 /*
@@ -1017,25 +1102,7 @@ PrintPlanCacheLeakWarning(CachedPlan *plan)
 void
 ResourceOwnerEnlargeTupleDescs(ResourceOwner owner)
 {
-	int			newmax;
-
-	if (owner->ntupdescs < owner->maxtupdescs)
-		return;					/* nothing to do */
-
-	if (owner->tupdescs == NULL)
-	{
-		newmax = 16;
-		owner->tupdescs = (TupleDesc *)
-			MemoryContextAlloc(TopMemoryContext, newmax * sizeof(TupleDesc));
-		owner->maxtupdescs = newmax;
-	}
-	else
-	{
-		newmax = owner->maxtupdescs * 2;
-		owner->tupdescs = (TupleDesc *)
-			repalloc(owner->tupdescs, newmax * sizeof(TupleDesc));
-		owner->maxtupdescs = newmax;
-	}
+	ResourceArrayEnlarge(&(owner->tupdescarr));
 }
 
 /*
@@ -1046,9 +1113,7 @@ ResourceOwnerEnlargeTupleDescs(ResourceOwner owner)
 void
 ResourceOwnerRememberTupleDesc(ResourceOwner owner, TupleDesc tupdesc)
 {
-	Assert(owner->ntupdescs < owner->maxtupdescs);
-	owner->tupdescs[owner->ntupdescs] = tupdesc;
-	owner->ntupdescs++;
+	ResourceArrayAdd(&(owner->tupdescarr), (const uint8*)&tupdesc);
 }
 
 /*
@@ -1057,25 +1122,11 @@ ResourceOwnerRememberTupleDesc(ResourceOwner owner, TupleDesc tupdesc)
 void
 ResourceOwnerForgetTupleDesc(ResourceOwner owner, TupleDesc tupdesc)
 {
-	TupleDesc  *tupdescs = owner->tupdescs;
-	int			nt1 = owner->ntupdescs - 1;
-	int			i;
-
-	for (i = nt1; i >= 0; i--)
-	{
-		if (tupdescs[i] == tupdesc)
-		{
-			while (i < nt1)
-			{
-				tupdescs[i] = tupdescs[i + 1];
-				i++;
-			}
-			owner->ntupdescs = nt1;
-			return;
-		}
-	}
-	elog(ERROR, "tupdesc reference %p is not owned by resource owner %s",
-		 tupdesc, owner->name);
+	bool 		res = ResourceArrayRemove(&(owner->tupdescarr),
+										  (const uint8*)&tupdesc);
+	if(!res)
+		elog(ERROR, "tupdesc reference %p is not owned by resource owner %s",
+			 tupdesc, owner->name);
 }
 
 /*
@@ -1099,25 +1150,7 @@ PrintTupleDescLeakWarning(TupleDesc tupdesc)
 void
 ResourceOwnerEnlargeSnapshots(ResourceOwner owner)
 {
-	int			newmax;
-
-	if (owner->nsnapshots < owner->maxsnapshots)
-		return;					/* nothing to do */
-
-	if (owner->snapshots == NULL)
-	{
-		newmax = 16;
-		owner->snapshots = (Snapshot *)
-			MemoryContextAlloc(TopMemoryContext, newmax * sizeof(Snapshot));
-		owner->maxsnapshots = newmax;
-	}
-	else
-	{
-		newmax = owner->maxsnapshots * 2;
-		owner->snapshots = (Snapshot *)
-			repalloc(owner->snapshots, newmax * sizeof(Snapshot));
-		owner->maxsnapshots = newmax;
-	}
+	ResourceArrayEnlarge(&(owner->snapshotarr));
 }
 
 /*
@@ -1128,9 +1161,7 @@ ResourceOwnerEnlargeSnapshots(ResourceOwner owner)
 void
 ResourceOwnerRememberSnapshot(ResourceOwner owner, Snapshot snapshot)
 {
-	Assert(owner->nsnapshots < owner->maxsnapshots);
-	owner->snapshots[owner->nsnapshots] = snapshot;
-	owner->nsnapshots++;
+	ResourceArrayAdd(&(owner->snapshotarr), (const uint8*)&snapshot);
 }
 
 /*
@@ -1139,25 +1170,11 @@ ResourceOwnerRememberSnapshot(ResourceOwner owner, Snapshot snapshot)
 void
 ResourceOwnerForgetSnapshot(ResourceOwner owner, Snapshot snapshot)
 {
-	Snapshot   *snapshots = owner->snapshots;
-	int			ns1 = owner->nsnapshots - 1;
-	int			i;
-
-	for (i = ns1; i >= 0; i--)
-	{
-		if (snapshots[i] == snapshot)
-		{
-			while (i < ns1)
-			{
-				snapshots[i] = snapshots[i + 1];
-				i++;
-			}
-			owner->nsnapshots = ns1;
-			return;
-		}
-	}
-	elog(ERROR, "snapshot reference %p is not owned by resource owner %s",
-		 snapshot, owner->name);
+	bool		res = ResourceArrayRemove(&(owner->snapshotarr),
+										  (const uint8*)&snapshot);
+	if(!res)
+		elog(ERROR, "snapshot reference %p is not owned by resource owner %s",
+			 snapshot, owner->name);
 }
 
 /*
@@ -1182,25 +1199,7 @@ PrintSnapshotLeakWarning(Snapshot snapshot)
 void
 ResourceOwnerEnlargeFiles(ResourceOwner owner)
 {
-	int			newmax;
-
-	if (owner->nfiles < owner->maxfiles)
-		return;					/* nothing to do */
-
-	if (owner->files == NULL)
-	{
-		newmax = 16;
-		owner->files = (File *)
-			MemoryContextAlloc(TopMemoryContext, newmax * sizeof(File));
-		owner->maxfiles = newmax;
-	}
-	else
-	{
-		newmax = owner->maxfiles * 2;
-		owner->files = (File *)
-			repalloc(owner->files, newmax * sizeof(File));
-		owner->maxfiles = newmax;
-	}
+	ResourceArrayEnlarge(&(owner->filearr));
 }
 
 /*
@@ -1211,9 +1210,7 @@ ResourceOwnerEnlargeFiles(ResourceOwner owner)
 void
 ResourceOwnerRememberFile(ResourceOwner owner, File file)
 {
-	Assert(owner->nfiles < owner->maxfiles);
-	owner->files[owner->nfiles] = file;
-	owner->nfiles++;
+	ResourceArrayAdd(&(owner->filearr), (const uint8*)&file);
 }
 
 /*
@@ -1222,25 +1219,10 @@ ResourceOwnerRememberFile(ResourceOwner owner, File file)
 void
 ResourceOwnerForgetFile(ResourceOwner owner, File file)
 {
-	File	   *files = owner->files;
-	int			ns1 = owner->nfiles - 1;
-	int			i;
-
-	for (i = ns1; i >= 0; i--)
-	{
-		if (files[i] == file)
-		{
-			while (i < ns1)
-			{
-				files[i] = files[i + 1];
-				i++;
-			}
-			owner->nfiles = ns1;
-			return;
-		}
-	}
-	elog(ERROR, "temporery file %d is not owned by resource owner %s",
-		 file, owner->name);
+	bool res = ResourceArrayRemove(&(owner->filearr), (const uint8*)&file);
+	if(!res)
+		elog(ERROR, "temporery file %d is not owned by resource owner %s",
+			 file, owner->name);
 }
 
 
@@ -1265,26 +1247,7 @@ PrintFileLeakWarning(File file)
 void
 ResourceOwnerEnlargeDSMs(ResourceOwner owner)
 {
-	int			newmax;
-
-	if (owner->ndsms < owner->maxdsms)
-		return;					/* nothing to do */
-
-	if (owner->dsms == NULL)
-	{
-		newmax = 16;
-		owner->dsms = (dsm_segment **)
-			MemoryContextAlloc(TopMemoryContext,
-							   newmax * sizeof(dsm_segment *));
-		owner->maxdsms = newmax;
-	}
-	else
-	{
-		newmax = owner->maxdsms * 2;
-		owner->dsms = (dsm_segment **)
-			repalloc(owner->dsms, newmax * sizeof(dsm_segment *));
-		owner->maxdsms = newmax;
-	}
+	ResourceArrayEnlarge(&(owner->dsmarr));
 }
 
 /*
@@ -1295,9 +1258,7 @@ ResourceOwnerEnlargeDSMs(ResourceOwner owner)
 void
 ResourceOwnerRememberDSM(ResourceOwner owner, dsm_segment *seg)
 {
-	Assert(owner->ndsms < owner->maxdsms);
-	owner->dsms[owner->ndsms] = seg;
-	owner->ndsms++;
+	ResourceArrayAdd(&(owner->dsmarr), (const uint8*)&seg);
 }
 
 /*
@@ -1306,26 +1267,10 @@ ResourceOwnerRememberDSM(ResourceOwner owner, dsm_segment *seg)
 void
 ResourceOwnerForgetDSM(ResourceOwner owner, dsm_segment *seg)
 {
-	dsm_segment **dsms = owner->dsms;
-	int			ns1 = owner->ndsms - 1;
-	int			i;
-
-	for (i = ns1; i >= 0; i--)
-	{
-		if (dsms[i] == seg)
-		{
-			while (i < ns1)
-			{
-				dsms[i] = dsms[i + 1];
-				i++;
-			}
-			owner->ndsms = ns1;
-			return;
-		}
-	}
-	elog(ERROR,
-		 "dynamic shared memory segment %u is not owned by resource owner %s",
-		 dsm_segment_handle(seg), owner->name);
+	bool res = ResourceArrayRemove(&(owner->dsmarr), (const uint8*)&seg);
+	if(!res)
+		elog(ERROR, "dynamic shared memory segment %u is not owned by resource"
+			 " owner %s", dsm_segment_handle(seg), owner->name);
 }
 
 
#2Robert Haas
robertmhaas@gmail.com
In reply to: Aleksander Alekseev (#1)
Re: Patch: ResourceOwner optimization for tables with many partitions

On Fri, Dec 4, 2015 at 7:15 AM, Aleksander Alekseev
<a.alekseev@postgrespro.ru> wrote:

Current implementation of ResourceOwner uses arrays to store resources
like TupleDescs, Snapshots, etc. When we want to release one of these
resources ResourceOwner finds it with linear scan. Granted, resource
array are usually small so its not a problem most of the time. But it
appears to be a bottleneck when we are working with tables which have a
lot of partitions.

To reproduce this issue:
1. run `./gen.pl 10000 | psql my_database postgres`
2. run `pgbench -j 8 -c 8 -f q.sql -T 100 my_database`
3. in second terminal run `sudo perf top -u postgres`

Both gen.pl and q.sql are attached to this message.

You will see that postgres spends a lot of time in ResourceOwnerForget*
procedures:

32.80% postgres [.] list_nth
20.29% postgres [.] ResourceOwnerForgetRelationRef
12.87% postgres [.] find_all_inheritors
7.90% postgres [.] get_tabstat_entry
6.68% postgres [.] ResourceOwnerForgetTupleDesc
1.17% postgres [.] hash_search_with_hash_value
... < 1% ...

I would like to suggest a patch (see attachment) witch fixes this
bottleneck. Also I discovered that there is a lot of code duplication in
ResourceOwner. Patch fixes this too. The idea is quite simple. I just
replaced arrays with something that could be considered hash tables,
but with some specific optimizations.

After applying this patch we can see that bottleneck is gone:

42.89% postgres [.] list_nth
18.30% postgres [.] find_all_inheritors
10.97% postgres [.] get_tabstat_entry
1.82% postgres [.] hash_search_with_hash_value
1.21% postgres [.] SearchCatCache
... < 1% ...

For tables with thousands partitions we gain in average 32.5% more TPS.

As far as I can see in the same time patch doesn't make anything worse.
`make check` passes with asserts enabled and disabled. There is no
performance degradation according to both standard pgbench benchmark
and benchmark described above for tables with 10 and 100 partitions.

I do think it's interesting to try to speed up the ResourceOwner
mechanism when there are many resources owned. We've had some forays
in that direction in the past, and I think it's useful to continue
that work. But I also think that this patch, while interesting, is
not something we can seriously consider committing in its current
form, because:

1. It lacks adequate comments and submission notes to explain the
general theory of operation of the patch.

2. It seems to use uint8 * rather freely as a substitute for char * or
void *, which doesn't seem like a great idea.

3. It doesn't follow PostgreSQL formatting conventions (uncuddled
curly braces, space after if and before open paren, etc.).

4. It mixes together multiple ideas in a single patch, not only
introducing a hashing concept but also striping a brand-new layer of
abstraction across the resource-owner mechanism. I am not sure that
layer of abstraction is a very good idea, but if it needs to be done,
I think it should be a separate patch.

--
Robert Haas
EnterpriseDB: http://www.enterprisedb.com
The Enterprise PostgreSQL Company

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

#3Aleksander Alekseev
a.alekseev@postgrespro.ru
In reply to: Robert Haas (#2)
1 attachment(s)
Re: Patch: ResourceOwner optimization for tables with many partitions

Hello, Robert

Thanks for your review. I believe I fixed items 1, 2 and 3 (see
attachment). Also I would like to clarify item 4.

4. It mixes together multiple ideas in a single patch, not only
introducing a hashing concept but also striping a brand-new layer of
abstraction across the resource-owner mechanism. I am not sure that
layer of abstraction is a very good idea, but if it needs to be done,
I think it should be a separate patch.

Do I right understand that you suggest following?

Current patch should be split in two parts. In first patch we create
and use ResourceArray with array-based implementation (abstraction
layer). Then we apply second patch which change ResourceArray
implementation to hashing based (optimization).

Best regards,
Aleksander

On Tue, 8 Dec 2015 17:30:33 -0500
Robert Haas <robertmhaas@gmail.com> wrote:

Show quoted text

On Fri, Dec 4, 2015 at 7:15 AM, Aleksander Alekseev
<a.alekseev@postgrespro.ru> wrote:

Current implementation of ResourceOwner uses arrays to store
resources like TupleDescs, Snapshots, etc. When we want to release
one of these resources ResourceOwner finds it with linear scan.
Granted, resource array are usually small so its not a problem most
of the time. But it appears to be a bottleneck when we are working
with tables which have a lot of partitions.

To reproduce this issue:
1. run `./gen.pl 10000 | psql my_database postgres`
2. run `pgbench -j 8 -c 8 -f q.sql -T 100 my_database`
3. in second terminal run `sudo perf top -u postgres`

Both gen.pl and q.sql are attached to this message.

You will see that postgres spends a lot of time in
ResourceOwnerForget* procedures:

32.80% postgres [.] list_nth
20.29% postgres [.] ResourceOwnerForgetRelationRef
12.87% postgres [.] find_all_inheritors
7.90% postgres [.] get_tabstat_entry
6.68% postgres [.] ResourceOwnerForgetTupleDesc
1.17% postgres [.] hash_search_with_hash_value
... < 1% ...

I would like to suggest a patch (see attachment) witch fixes this
bottleneck. Also I discovered that there is a lot of code
duplication in ResourceOwner. Patch fixes this too. The idea is
quite simple. I just replaced arrays with something that could be
considered hash tables, but with some specific optimizations.

After applying this patch we can see that bottleneck is gone:

42.89% postgres [.] list_nth
18.30% postgres [.] find_all_inheritors
10.97% postgres [.] get_tabstat_entry
1.82% postgres [.] hash_search_with_hash_value
1.21% postgres [.] SearchCatCache
... < 1% ...

For tables with thousands partitions we gain in average 32.5% more
TPS.

As far as I can see in the same time patch doesn't make anything
worse. `make check` passes with asserts enabled and disabled. There
is no performance degradation according to both standard pgbench
benchmark and benchmark described above for tables with 10 and 100
partitions.

I do think it's interesting to try to speed up the ResourceOwner
mechanism when there are many resources owned. We've had some forays
in that direction in the past, and I think it's useful to continue
that work. But I also think that this patch, while interesting, is
not something we can seriously consider committing in its current
form, because:

1. It lacks adequate comments and submission notes to explain the
general theory of operation of the patch.

2. It seems to use uint8 * rather freely as a substitute for char * or
void *, which doesn't seem like a great idea.

3. It doesn't follow PostgreSQL formatting conventions (uncuddled
curly braces, space after if and before open paren, etc.).

4. It mixes together multiple ideas in a single patch, not only
introducing a hashing concept but also striping a brand-new layer of
abstraction across the resource-owner mechanism. I am not sure that
layer of abstraction is a very good idea, but if it needs to be done,
I think it should be a separate patch.

Attachments:

resource-owner-optimization-v2.patchtext/x-patchDownload
diff --git a/src/backend/utils/resowner/resowner.c b/src/backend/utils/resowner/resowner.c
index 0e7acbf..d2b37d5 100644
--- a/src/backend/utils/resowner/resowner.c
+++ b/src/backend/utils/resowner/resowner.c
@@ -29,6 +29,323 @@
 #include "utils/snapmgr.h"
 
 /*
+ * ResourceArray is a common structure for storing different types of resources.
+ *
+ * ResourceOwner can own `HeapTuple`s, `Relation`s, `Snapshot`s, etc. For
+ * each resource type there are procedures ResourceOwnerRemember* and
+ * ResourceOwnerForget*. There are also ResourceOwnerEnlarge* procedures
+ * which should be called before corresponding ResourceOwnerRemember* calls
+ * (see below). Internally each type of resource is stored in separate
+ * ResourceArray.
+ *
+ * There are two major reasons for using ResourceArray instead of, say,
+ * regular C arrays.
+ *
+ * Firstly we would like to prevent code duplication. For instance
+ * ResourceArray provides generic Remember/Forget/Enlarge procedures, so
+ * corresponding ResourceOwner* procedures are just a typesafe wrappers for
+ * these procedures. Note that different resources have different sizeof. Thus
+ * ResourceArray should know size of resource and store all data in char[]
+ * buffer.
+ *
+ * Secondly ResourceArray must be more efficient than regular C array.
+ * Previous implementation of ResourceOwner used arrays. It had O(1) complexity
+ * of Remember procedures and O(N) complexity of Forget procedures where N is a
+ * number of remembered resources. It turned out that such implementation
+ * creates a bottleneck in some cases, e.g. when working with partitioned
+ * tables which have a lot of (say, thousands) partitions. New implementation
+ * in general could be considered a hash table with some specific optimizations.
+ * It has O(1) complexity of both Remember and Forget procedures and
+ * apparently doesn't cause any performance degradation compared to previous
+ * implementation.
+ */
+typedef struct ResourceArray
+{
+	char	   *itemsarr;		/* buffer for storing values */
+	uint32		itemsizelg:2;	/* sizeof one item log 2 */
+	uint32		capacity:30;	/* capacity of array */
+	uint32		nitems;			/* how many items is stored in items array */
+	uint32		maxitems;		/* precalculated RESARRAY_MAX_ITEMS(capacity) */
+}	ResourceArray;
+
+/*
+ * This number is used as initial size of resource array. If given number of
+ * items is not enough, we double array size and reallocate memory.
+ *
+ * Should be power of two since we use (arrsize -1) as mask for hash value.
+ *
+ */
+#define RESARRAY_INIT_SIZE 16
+
+/*
+ * How many items could be stored in a resource array of given capacity. If
+ * this number is reached we need to resize an array to prevent hash collisions.
+ *
+ * This computation actually costs only two additions and one binary shift.
+ */
+#define RESARRAY_MAX_ITEMS(capacity) ((capacity)*3/4)
+
+/*
+ * Such type of callback function is called when resource stored in
+ * ResourceArray is released using ResourceArrayFree. Callback should
+ * _actually_ release a resource so nitems value will be decremented.
+ */
+typedef void (*ResourceArrayRemoveCallback) (const void *ref, bool isCommit);
+
+/* Used as argument to memcmp to determine if ResourceArray[i] is free. */
+static const char RESOURCE_ARRAY_ZERO_ELEMENT[sizeof(void *)] =
+{
+	0
+};
+
+/*
+ * Calculate hash_any of given data. For uint32 values use faster hash_uint32.
+ */
+static Datum
+ResourceArrayHash(const void *data, int size)
+{
+	uint32		tmp;
+
+	Assert(size == sizeof(uint32) || size == sizeof(void *));
+
+	if (size == sizeof(uint32))
+	{
+		tmp = *((const uint32 *) data);
+		return hash_uint32(tmp);
+	}
+	else
+	{
+		return hash_any(data, size);
+	}
+}
+
+/*
+ * Initialize ResourceArray
+ */
+static void
+ResourceArrayInit(ResourceArray * resarr, uint32 itemsize)
+{
+	Assert(itemsize == sizeof(int) || itemsize == sizeof(void *));
+	Assert(resarr->itemsarr == NULL);
+	Assert(resarr->capacity == 0);
+	Assert(resarr->nitems == 0);
+	Assert(resarr->maxitems == 0);
+
+	resarr->itemsizelg = 0;
+	while (itemsize > 1)
+	{
+		resarr->itemsizelg++;
+		itemsize >>= 1;
+	}
+
+	Assert(resarr->itemsizelg == 2 || resarr->itemsizelg == 3);
+}
+
+/*
+ * Add a resource to ResourceArray
+ *
+ * Caller must have previously done ResourceArrayEnlarge()
+ */
+static void
+ResourceArrayAdd(ResourceArray * resarr, const void *dataref)
+{
+	char	   *itemptr;
+	Datum		idx;
+	Datum		mask = resarr->capacity - 1;
+	uint32		itemsize = 1 << resarr->itemsizelg;
+
+	Assert(memcmp(dataref, RESOURCE_ARRAY_ZERO_ELEMENT, itemsize) != 0);
+	Assert(resarr->maxitems > resarr->nitems);
+	Assert(resarr->capacity > 0);
+	Assert(resarr->itemsarr != NULL);
+
+	/*
+	 * Hashing is quite expensive, so we use it only for large arrays. For
+	 * small arrays we just use a linear scan.
+	 */
+	if (resarr->capacity == RESARRAY_INIT_SIZE)
+		idx = resarr->nitems;
+	else
+		idx = ResourceArrayHash(dataref, itemsize);
+	idx &= mask;
+
+	while (true)
+	{
+		/*
+		 * Read next line as:
+		 *
+		 * itemptr = &(itemsarr[idx])
+		 *
+		 * We use binary shift since compiler doesn't know that itemsize is
+		 * always power of two. It would use multiplication instead of
+		 * efficient binary shift in code `idx * itemsize`.
+		 */
+		itemptr = resarr->itemsarr + (idx << resarr->itemsizelg);
+		if (memcmp(itemptr, RESOURCE_ARRAY_ZERO_ELEMENT, itemsize) == 0)
+			break;
+		idx = (idx + 1) & mask;
+	}
+
+	memcpy(itemptr, dataref, itemsize);
+	resarr->nitems++;
+}
+
+/*
+ * Remove a resource from ResourceArray
+ *
+ * Returns true on success, false if resource was not found
+ */
+static bool
+ResourceArrayRemove(ResourceArray * resarr, const void *dataref)
+{
+	char	   *itemptr;
+	Datum		idx;
+	uint32		i;
+	Datum		mask = resarr->capacity - 1;
+	uint32		itemsize = 1 << resarr->itemsizelg;
+
+	Assert(memcmp(dataref, RESOURCE_ARRAY_ZERO_ELEMENT, itemsize) != 0);
+	Assert(resarr->capacity > 0);
+	Assert(resarr->itemsarr != NULL);
+
+	/*
+	 * Hashing is quite expensive, so we use it only for large arrays. For
+	 * small arrays we use a linear scan.
+	 */
+	if (resarr->capacity == RESARRAY_INIT_SIZE)
+		idx = 0;
+	else
+		idx = ResourceArrayHash(dataref, itemsize);
+	idx &= mask;
+
+	for (i = 0; i < resarr->capacity; i++)
+	{
+		/*
+		 * Read next line as:
+		 *
+		 * itemptr = &(itemsarr[idx])
+		 *
+		 * We use binary shift since compiler doesn't know that itemsize is
+		 * always power of two. It would use multiplication instead of
+		 * efficient binary shift in code `idx * itemsize`.
+		 */
+		itemptr = resarr->itemsarr + (idx << resarr->itemsizelg);
+		if (memcmp(itemptr, dataref, itemsize) == 0)
+		{
+			memset(itemptr, 0, itemsize);
+			resarr->nitems--;
+			return true;
+		}
+		idx = (idx + 1) & mask;
+	}
+
+	return false;
+}
+
+/*
+ * Make sure there is a room for at least one more resource in an array.
+ *
+ * This is separate from actually inserting a resource because if we run out
+ * of memory, it's critical to do so *before* acquiring the resource.
+ */
+static void
+ResourceArrayEnlarge(ResourceArray * resarr)
+{
+	uint32		oldcap;
+	char	   *olditemsarr;
+	char	   *olditemptr;
+	uint32		itemsize = 1 << resarr->itemsizelg;
+
+	Assert(resarr->itemsizelg != 0);
+
+	if (resarr->nitems < resarr->maxitems)
+		return;					/* nothing to do */
+
+	olditemsarr = resarr->itemsarr;
+	oldcap = resarr->capacity;
+
+	resarr->capacity = oldcap > 0 ? oldcap * 2 : RESARRAY_INIT_SIZE;
+	resarr->itemsarr = (char *)
+		MemoryContextAllocZero(TopMemoryContext,
+							   resarr->capacity * itemsize);
+	resarr->maxitems = RESARRAY_MAX_ITEMS(resarr->capacity);
+	resarr->nitems = 0;
+
+	if (olditemsarr != NULL)
+	{
+		while (oldcap > 0)
+		{
+			oldcap--;
+
+			/*
+			 * Read next line as:
+			 *
+			 * olditemptr = &(olditemsarr[oldcap])
+			 *
+			 * We use binary shift since compiler doesn't know that itemsize
+			 * is always power of two. It would use multiplication instead of
+			 * efficient binary shift in code `oldcap * itemsize`.
+			 */
+			olditemptr = olditemsarr + (oldcap << resarr->itemsizelg);
+			if (memcmp(olditemptr, RESOURCE_ARRAY_ZERO_ELEMENT, itemsize) != 0)
+				ResourceArrayAdd(resarr, olditemptr);
+		}
+		pfree(olditemsarr);
+	}
+}
+
+/*
+ * Remove all resources in array and call a callback function for each release.
+ */
+static void
+ResourceArrayRemoveAll(ResourceArray * resarr,
+					   ResourceArrayRemoveCallback releasecb,
+					   bool isCommit)
+{
+	uint32		idx = 0;
+	char	   *itemptr;
+	uint32		itemsize = 1 << resarr->itemsizelg;
+
+	while (resarr->nitems > 0)
+	{
+		/*
+		 * Read next line as:
+		 *
+		 * itemptr = &(itemsarr[idx])
+		 *
+		 * We use binary shift since compiler doesn't know that itemsize is
+		 * always power of two. It would use multiplication instead of
+		 * efficient binary shift in code `idx * itemsize`.
+		 */
+		itemptr = resarr->itemsarr + (idx << resarr->itemsizelg);
+		if (memcmp(itemptr, RESOURCE_ARRAY_ZERO_ELEMENT, itemsize) == 0)
+		{
+			idx++;
+			continue;
+		}
+		releasecb(itemptr, isCommit);
+	}
+}
+
+/*
+ * Return ResourceArray to initial state
+ */
+static void
+ResourceArrayFree(ResourceArray * resarr)
+{
+	Assert(resarr->nitems == 0);
+
+	resarr->capacity = 0;
+	resarr->maxitems = 0;
+
+	if (!resarr->itemsarr)
+		return;
+
+	pfree(resarr->itemsarr);
+	resarr->itemsarr = NULL;
+}
+
+/*
  * To speed up bulk releasing or reassigning locks from a resource owner to
  * its parent, each resource owner has a small cache of locks it owns. The
  * lock manager has the same information in its local lock hash table, and
@@ -56,53 +373,22 @@ typedef struct ResourceOwnerData
 	ResourceOwner nextchild;	/* next child of same parent */
 	const char *name;			/* name (just for debugging) */
 
-	/* We have built-in support for remembering owned buffers */
-	int			nbuffers;		/* number of owned buffer pins */
-	Buffer	   *buffers;		/* dynamically allocated array */
-	int			maxbuffers;		/* currently allocated array size */
-
 	/* We can remember up to MAX_RESOWNER_LOCKS references to local locks. */
 	int			nlocks;			/* number of owned locks */
 	LOCALLOCK  *locks[MAX_RESOWNER_LOCKS];		/* list of owned locks */
 
-	/* We have built-in support for remembering catcache references */
-	int			ncatrefs;		/* number of owned catcache pins */
-	HeapTuple  *catrefs;		/* dynamically allocated array */
-	int			maxcatrefs;		/* currently allocated array size */
-
-	int			ncatlistrefs;	/* number of owned catcache-list pins */
-	CatCList  **catlistrefs;	/* dynamically allocated array */
-	int			maxcatlistrefs; /* currently allocated array size */
-
-	/* We have built-in support for remembering relcache references */
-	int			nrelrefs;		/* number of owned relcache pins */
-	Relation   *relrefs;		/* dynamically allocated array */
-	int			maxrelrefs;		/* currently allocated array size */
-
-	/* We have built-in support for remembering plancache references */
-	int			nplanrefs;		/* number of owned plancache pins */
-	CachedPlan **planrefs;		/* dynamically allocated array */
-	int			maxplanrefs;	/* currently allocated array size */
-
-	/* We have built-in support for remembering tupdesc references */
-	int			ntupdescs;		/* number of owned tupdesc references */
-	TupleDesc  *tupdescs;		/* dynamically allocated array */
-	int			maxtupdescs;	/* currently allocated array size */
-
-	/* We have built-in support for remembering snapshot references */
-	int			nsnapshots;		/* number of owned snapshot references */
-	Snapshot   *snapshots;		/* dynamically allocated array */
-	int			maxsnapshots;	/* currently allocated array size */
-
-	/* We have built-in support for remembering open temporary files */
-	int			nfiles;			/* number of owned temporary files */
-	File	   *files;			/* dynamically allocated array */
-	int			maxfiles;		/* currently allocated array size */
-
-	/* We have built-in support for remembering dynamic shmem segments */
-	int			ndsms;			/* number of owned shmem segments */
-	dsm_segment **dsms;			/* dynamically allocated array */
-	int			maxdsms;		/* currently allocated array size */
+	/* We have built-in support for remembering: */
+
+	ResourceArray catrefarr;	/* `HeapTuple`s */
+	ResourceArray catlistrefarr;	/* `ResourceOwner`s */
+	ResourceArray relrefarr;	/* `Relation`s */
+	ResourceArray planrefarr;	/* `CachedPlan*`s */
+	ResourceArray tupdescarr;	/* `TupleDesc`s */
+	ResourceArray snapshotarr;	/* `Snapshot`s */
+	ResourceArray dsmarr;		/* `dsm_segment*`s */
+	ResourceArray bufferarr;	/* `Buffer`s  */
+	ResourceArray filearr;		/* `File`s */
+
 }	ResourceOwnerData;
 
 
@@ -168,6 +454,16 @@ ResourceOwnerCreate(ResourceOwner parent, const char *name)
 		parent->firstchild = owner;
 	}
 
+	ResourceArrayInit(&(owner->catrefarr), sizeof(HeapTuple));
+	ResourceArrayInit(&(owner->catlistrefarr), sizeof(ResourceOwner));
+	ResourceArrayInit(&(owner->relrefarr), sizeof(Relation));
+	ResourceArrayInit(&(owner->planrefarr), sizeof(CachedPlan *));
+	ResourceArrayInit(&(owner->tupdescarr), sizeof(TupleDesc));
+	ResourceArrayInit(&(owner->snapshotarr), sizeof(Snapshot));
+	ResourceArrayInit(&(owner->dsmarr), sizeof(dsm_segment *));
+	ResourceArrayInit(&(owner->bufferarr), sizeof(Buffer));
+	ResourceArrayInit(&(owner->filearr), sizeof(File));
+
 	return owner;
 }
 
@@ -221,6 +517,96 @@ ResourceOwnerRelease(ResourceOwner owner,
 }
 
 static void
+ReleaseCachedPlanCallback(const void *dataref, bool isCommit)
+{
+	CachedPlan *res = *((CachedPlan **) dataref);
+
+	if (isCommit)
+		PrintPlanCacheLeakWarning(res);
+	ReleaseCachedPlan(res, true);
+}
+
+static void
+ReleaseDSMCallback(const void *dataref, bool isCommit)
+{
+	dsm_segment *res = *((dsm_segment **) dataref);
+
+	if (isCommit)
+		PrintDSMLeakWarning(res);
+	dsm_detach(res);
+}
+
+static void
+ReleaseTupleDescCallback(const void *dataref, bool isCommit)
+{
+	TupleDesc	res = *((TupleDesc *) dataref);
+
+	if (isCommit)
+		PrintTupleDescLeakWarning(res);
+	DecrTupleDescRefCount(res);
+}
+
+static void
+ReleaseSnapshotCallback(const void *dataref, bool isCommit)
+{
+	Snapshot	res = *((Snapshot *) dataref);
+
+	if (isCommit)
+		PrintSnapshotLeakWarning(res);
+	UnregisterSnapshot(res);
+}
+
+static void
+ReleaseRelationCallback(const void *dataref, bool isCommit)
+{
+	Relation	res = *((Relation *) dataref);
+
+	if (isCommit)
+		PrintRelCacheLeakWarning(res);
+	RelationClose(res);
+}
+
+static void
+ReleaseCatCacheListCallback(const void *dataref, bool isCommit)
+{
+	CatCList   *res = *((CatCList **) dataref);
+
+	if (isCommit)
+		PrintCatCacheListLeakWarning(res);
+	ReleaseCatCacheList(res);
+}
+
+static void
+ReleaseCatCacheCallback(const void *dataref, bool isCommit)
+{
+	HeapTuple	res = *((HeapTuple *) dataref);
+
+	if (isCommit)
+		PrintCatCacheLeakWarning(res);
+	ReleaseCatCache(res);
+}
+
+static void
+ReleaseBufferCallback(const void *dataref, bool isCommit)
+{
+	Buffer		res = *((Buffer *) dataref);
+
+	if (isCommit)
+		PrintBufferLeakWarning(res);
+	ReleaseBuffer(res);
+}
+
+static void
+ReleaseFileCallback(const void *dataref, bool isCommit)
+{
+	File		res = *((File *) dataref);
+
+	if (isCommit)
+		PrintFileLeakWarning(res);
+	FileClose(res);
+}
+
+static void
 ResourceOwnerReleaseInternal(ResourceOwner owner,
 							 ResourceReleasePhase phase,
 							 bool isCommit,
@@ -252,46 +638,17 @@ ResourceOwnerReleaseInternal(ResourceOwner owner,
 		 * During a commit, there shouldn't be any remaining pins --- that
 		 * would indicate failure to clean up the executor correctly --- so
 		 * issue warnings.  In the abort case, just clean up quietly.
-		 *
-		 * We are careful to do the releasing back-to-front, so as to avoid
-		 * O(N^2) behavior in ResourceOwnerForgetBuffer().
 		 */
-		while (owner->nbuffers > 0)
-		{
-			if (isCommit)
-				PrintBufferLeakWarning(owner->buffers[owner->nbuffers - 1]);
-			ReleaseBuffer(owner->buffers[owner->nbuffers - 1]);
-		}
+		ResourceArrayRemoveAll(&(owner->bufferarr),
+							   ReleaseBufferCallback, isCommit);
 
-		/*
-		 * Release relcache references.  Note that RelationClose will remove
-		 * the relref entry from my list, so I just have to iterate till there
-		 * are none.
-		 *
-		 * As with buffer pins, warn if any are left at commit time, and
-		 * release back-to-front for speed.
-		 */
-		while (owner->nrelrefs > 0)
-		{
-			if (isCommit)
-				PrintRelCacheLeakWarning(owner->relrefs[owner->nrelrefs - 1]);
-			RelationClose(owner->relrefs[owner->nrelrefs - 1]);
-		}
+		/* Ditto for relcache references. */
+		ResourceArrayRemoveAll(&(owner->relrefarr),
+							   ReleaseRelationCallback, isCommit);
 
-		/*
-		 * Release dynamic shared memory segments.  Note that dsm_detach()
-		 * will remove the segment from my list, so I just have to iterate
-		 * until there are none.
-		 *
-		 * As in the preceding cases, warn if there are leftover at commit
-		 * time.
-		 */
-		while (owner->ndsms > 0)
-		{
-			if (isCommit)
-				PrintDSMLeakWarning(owner->dsms[owner->ndsms - 1]);
-			dsm_detach(owner->dsms[owner->ndsms - 1]);
-		}
+		/* Ditto for dynamic shared memory segments */
+		ResourceArrayRemoveAll(&(owner->dsmarr),
+							   ReleaseDSMCallback, isCommit);
 	}
 	else if (phase == RESOURCE_RELEASE_LOCKS)
 	{
@@ -351,48 +708,28 @@ ResourceOwnerReleaseInternal(ResourceOwner owner,
 		 * As with buffer pins, warn if any are left at commit time, and
 		 * release back-to-front for speed.
 		 */
-		while (owner->ncatrefs > 0)
-		{
-			if (isCommit)
-				PrintCatCacheLeakWarning(owner->catrefs[owner->ncatrefs - 1]);
-			ReleaseCatCache(owner->catrefs[owner->ncatrefs - 1]);
-		}
+		ResourceArrayRemoveAll(&(owner->catrefarr),
+							   ReleaseCatCacheCallback, isCommit);
+
 		/* Ditto for catcache lists */
-		while (owner->ncatlistrefs > 0)
-		{
-			if (isCommit)
-				PrintCatCacheListLeakWarning(owner->catlistrefs[owner->ncatlistrefs - 1]);
-			ReleaseCatCacheList(owner->catlistrefs[owner->ncatlistrefs - 1]);
-		}
+		ResourceArrayRemoveAll(&(owner->catlistrefarr),
+							   ReleaseCatCacheListCallback, isCommit);
+
 		/* Ditto for plancache references */
-		while (owner->nplanrefs > 0)
-		{
-			if (isCommit)
-				PrintPlanCacheLeakWarning(owner->planrefs[owner->nplanrefs - 1]);
-			ReleaseCachedPlan(owner->planrefs[owner->nplanrefs - 1], true);
-		}
+		ResourceArrayRemoveAll(&(owner->planrefarr),
+							   ReleaseCachedPlanCallback, isCommit);
+
 		/* Ditto for tupdesc references */
-		while (owner->ntupdescs > 0)
-		{
-			if (isCommit)
-				PrintTupleDescLeakWarning(owner->tupdescs[owner->ntupdescs - 1]);
-			DecrTupleDescRefCount(owner->tupdescs[owner->ntupdescs - 1]);
-		}
+		ResourceArrayRemoveAll(&(owner->tupdescarr),
+							   ReleaseTupleDescCallback, isCommit);
+
 		/* Ditto for snapshot references */
-		while (owner->nsnapshots > 0)
-		{
-			if (isCommit)
-				PrintSnapshotLeakWarning(owner->snapshots[owner->nsnapshots - 1]);
-			UnregisterSnapshot(owner->snapshots[owner->nsnapshots - 1]);
-		}
+		ResourceArrayRemoveAll(&(owner->snapshotarr),
+							   ReleaseSnapshotCallback, isCommit);
 
 		/* Ditto for temporary files */
-		while (owner->nfiles > 0)
-		{
-			if (isCommit)
-				PrintFileLeakWarning(owner->files[owner->nfiles - 1]);
-			FileClose(owner->files[owner->nfiles - 1]);
-		}
+		ResourceArrayRemoveAll(&(owner->filearr),
+							   ReleaseFileCallback, isCommit);
 
 		/* Clean up index scans too */
 		ReleaseResources_hash();
@@ -418,16 +755,7 @@ ResourceOwnerDelete(ResourceOwner owner)
 	Assert(owner != CurrentResourceOwner);
 
 	/* And it better not own any resources, either */
-	Assert(owner->nbuffers == 0);
 	Assert(owner->nlocks == 0 || owner->nlocks == MAX_RESOWNER_LOCKS + 1);
-	Assert(owner->ncatrefs == 0);
-	Assert(owner->ncatlistrefs == 0);
-	Assert(owner->nrelrefs == 0);
-	Assert(owner->ndsms == 0);
-	Assert(owner->nplanrefs == 0);
-	Assert(owner->ntupdescs == 0);
-	Assert(owner->nsnapshots == 0);
-	Assert(owner->nfiles == 0);
 
 	/*
 	 * Delete children.  The recursive call will delink the child from me, so
@@ -444,25 +772,15 @@ ResourceOwnerDelete(ResourceOwner owner)
 	ResourceOwnerNewParent(owner, NULL);
 
 	/* And free the object. */
-	if (owner->buffers)
-		pfree(owner->buffers);
-	if (owner->catrefs)
-		pfree(owner->catrefs);
-	if (owner->catlistrefs)
-		pfree(owner->catlistrefs);
-	if (owner->relrefs)
-		pfree(owner->relrefs);
-	if (owner->planrefs)
-		pfree(owner->planrefs);
-	if (owner->tupdescs)
-		pfree(owner->tupdescs);
-	if (owner->snapshots)
-		pfree(owner->snapshots);
-	if (owner->files)
-		pfree(owner->files);
-	if (owner->dsms)
-		pfree(owner->dsms);
-
+	ResourceArrayFree(&(owner->catrefarr));
+	ResourceArrayFree(&(owner->catlistrefarr));
+	ResourceArrayFree(&(owner->relrefarr));
+	ResourceArrayFree(&(owner->planrefarr));
+	ResourceArrayFree(&(owner->tupdescarr));
+	ResourceArrayFree(&(owner->snapshotarr));
+	ResourceArrayFree(&(owner->dsmarr));
+	ResourceArrayFree(&(owner->bufferarr));
+	ResourceArrayFree(&(owner->filearr));
 	pfree(owner);
 }
 
@@ -575,26 +893,9 @@ UnregisterResourceReleaseCallback(ResourceReleaseCallback callback, void *arg)
 void
 ResourceOwnerEnlargeBuffers(ResourceOwner owner)
 {
-	int			newmax;
-
-	if (owner == NULL ||
-		owner->nbuffers < owner->maxbuffers)
-		return;					/* nothing to do */
-
-	if (owner->buffers == NULL)
-	{
-		newmax = 16;
-		owner->buffers = (Buffer *)
-			MemoryContextAlloc(TopMemoryContext, newmax * sizeof(Buffer));
-		owner->maxbuffers = newmax;
-	}
-	else
-	{
-		newmax = owner->maxbuffers * 2;
-		owner->buffers = (Buffer *)
-			repalloc(owner->buffers, newmax * sizeof(Buffer));
-		owner->maxbuffers = newmax;
-	}
+	if (owner == NULL)
+		return;
+	ResourceArrayEnlarge(&(owner->bufferarr));
 }
 
 /*
@@ -608,12 +909,9 @@ ResourceOwnerEnlargeBuffers(ResourceOwner owner)
 void
 ResourceOwnerRememberBuffer(ResourceOwner owner, Buffer buffer)
 {
-	if (owner != NULL)
-	{
-		Assert(owner->nbuffers < owner->maxbuffers);
-		owner->buffers[owner->nbuffers] = buffer;
-		owner->nbuffers++;
-	}
+	if (owner == NULL)
+		return;
+	ResourceArrayAdd(&(owner->bufferarr), (const void *) &buffer);
 }
 
 /*
@@ -625,33 +923,15 @@ ResourceOwnerRememberBuffer(ResourceOwner owner, Buffer buffer)
 void
 ResourceOwnerForgetBuffer(ResourceOwner owner, Buffer buffer)
 {
-	if (owner != NULL)
-	{
-		Buffer	   *buffers = owner->buffers;
-		int			nb1 = owner->nbuffers - 1;
-		int			i;
+	bool		res;
 
-		/*
-		 * Scan back-to-front because it's more likely we are releasing a
-		 * recently pinned buffer.  This isn't always the case of course, but
-		 * it's the way to bet.
-		 */
-		for (i = nb1; i >= 0; i--)
-		{
-			if (buffers[i] == buffer)
-			{
-				while (i < nb1)
-				{
-					buffers[i] = buffers[i + 1];
-					i++;
-				}
-				owner->nbuffers = nb1;
-				return;
-			}
-		}
+	if (owner == NULL)
+		return;
+
+	res = ResourceArrayRemove(&(owner->bufferarr), (const void *) &buffer);
+	if (!res)
 		elog(ERROR, "buffer %d is not owned by resource owner %s",
 			 buffer, owner->name);
-	}
 }
 
 /*
@@ -667,6 +947,8 @@ ResourceOwnerForgetBuffer(ResourceOwner owner, Buffer buffer)
 void
 ResourceOwnerRememberLock(ResourceOwner owner, LOCALLOCK *locallock)
 {
+	Assert(locallock != NULL);
+
 	if (owner->nlocks > MAX_RESOWNER_LOCKS)
 		return;					/* we have already overflowed */
 
@@ -714,25 +996,7 @@ ResourceOwnerForgetLock(ResourceOwner owner, LOCALLOCK *locallock)
 void
 ResourceOwnerEnlargeCatCacheRefs(ResourceOwner owner)
 {
-	int			newmax;
-
-	if (owner->ncatrefs < owner->maxcatrefs)
-		return;					/* nothing to do */
-
-	if (owner->catrefs == NULL)
-	{
-		newmax = 16;
-		owner->catrefs = (HeapTuple *)
-			MemoryContextAlloc(TopMemoryContext, newmax * sizeof(HeapTuple));
-		owner->maxcatrefs = newmax;
-	}
-	else
-	{
-		newmax = owner->maxcatrefs * 2;
-		owner->catrefs = (HeapTuple *)
-			repalloc(owner->catrefs, newmax * sizeof(HeapTuple));
-		owner->maxcatrefs = newmax;
-	}
+	ResourceArrayEnlarge(&(owner->catrefarr));
 }
 
 /*
@@ -743,9 +1007,7 @@ ResourceOwnerEnlargeCatCacheRefs(ResourceOwner owner)
 void
 ResourceOwnerRememberCatCacheRef(ResourceOwner owner, HeapTuple tuple)
 {
-	Assert(owner->ncatrefs < owner->maxcatrefs);
-	owner->catrefs[owner->ncatrefs] = tuple;
-	owner->ncatrefs++;
+	ResourceArrayAdd(&(owner->catrefarr), (const void *) &tuple);
 }
 
 /*
@@ -754,25 +1016,12 @@ ResourceOwnerRememberCatCacheRef(ResourceOwner owner, HeapTuple tuple)
 void
 ResourceOwnerForgetCatCacheRef(ResourceOwner owner, HeapTuple tuple)
 {
-	HeapTuple  *catrefs = owner->catrefs;
-	int			nc1 = owner->ncatrefs - 1;
-	int			i;
+	bool		res = ResourceArrayRemove(&(owner->catrefarr),
+										  (const void *) &tuple);
 
-	for (i = nc1; i >= 0; i--)
-	{
-		if (catrefs[i] == tuple)
-		{
-			while (i < nc1)
-			{
-				catrefs[i] = catrefs[i + 1];
-				i++;
-			}
-			owner->ncatrefs = nc1;
-			return;
-		}
-	}
-	elog(ERROR, "catcache reference %p is not owned by resource owner %s",
-		 tuple, owner->name);
+	if (!res)
+		elog(ERROR, "catcache reference %p is not owned by resource owner %s",
+			 tuple, owner->name);
 }
 
 /*
@@ -785,25 +1034,7 @@ ResourceOwnerForgetCatCacheRef(ResourceOwner owner, HeapTuple tuple)
 void
 ResourceOwnerEnlargeCatCacheListRefs(ResourceOwner owner)
 {
-	int			newmax;
-
-	if (owner->ncatlistrefs < owner->maxcatlistrefs)
-		return;					/* nothing to do */
-
-	if (owner->catlistrefs == NULL)
-	{
-		newmax = 16;
-		owner->catlistrefs = (CatCList **)
-			MemoryContextAlloc(TopMemoryContext, newmax * sizeof(CatCList *));
-		owner->maxcatlistrefs = newmax;
-	}
-	else
-	{
-		newmax = owner->maxcatlistrefs * 2;
-		owner->catlistrefs = (CatCList **)
-			repalloc(owner->catlistrefs, newmax * sizeof(CatCList *));
-		owner->maxcatlistrefs = newmax;
-	}
+	ResourceArrayEnlarge(&(owner->catlistrefarr));
 }
 
 /*
@@ -814,9 +1045,7 @@ ResourceOwnerEnlargeCatCacheListRefs(ResourceOwner owner)
 void
 ResourceOwnerRememberCatCacheListRef(ResourceOwner owner, CatCList *list)
 {
-	Assert(owner->ncatlistrefs < owner->maxcatlistrefs);
-	owner->catlistrefs[owner->ncatlistrefs] = list;
-	owner->ncatlistrefs++;
+	ResourceArrayAdd(&(owner->catlistrefarr), (const void *) &list);
 }
 
 /*
@@ -825,25 +1054,12 @@ ResourceOwnerRememberCatCacheListRef(ResourceOwner owner, CatCList *list)
 void
 ResourceOwnerForgetCatCacheListRef(ResourceOwner owner, CatCList *list)
 {
-	CatCList  **catlistrefs = owner->catlistrefs;
-	int			nc1 = owner->ncatlistrefs - 1;
-	int			i;
+	bool		res = ResourceArrayRemove(&(owner->catlistrefarr),
+										  (const void *) &list);
 
-	for (i = nc1; i >= 0; i--)
-	{
-		if (catlistrefs[i] == list)
-		{
-			while (i < nc1)
-			{
-				catlistrefs[i] = catlistrefs[i + 1];
-				i++;
-			}
-			owner->ncatlistrefs = nc1;
-			return;
-		}
-	}
-	elog(ERROR, "catcache list reference %p is not owned by resource owner %s",
-		 list, owner->name);
+	if (!res)
+		elog(ERROR, "catcache list reference %p is not owned by resource owner %s",
+			 list, owner->name);
 }
 
 /*
@@ -856,25 +1072,7 @@ ResourceOwnerForgetCatCacheListRef(ResourceOwner owner, CatCList *list)
 void
 ResourceOwnerEnlargeRelationRefs(ResourceOwner owner)
 {
-	int			newmax;
-
-	if (owner->nrelrefs < owner->maxrelrefs)
-		return;					/* nothing to do */
-
-	if (owner->relrefs == NULL)
-	{
-		newmax = 16;
-		owner->relrefs = (Relation *)
-			MemoryContextAlloc(TopMemoryContext, newmax * sizeof(Relation));
-		owner->maxrelrefs = newmax;
-	}
-	else
-	{
-		newmax = owner->maxrelrefs * 2;
-		owner->relrefs = (Relation *)
-			repalloc(owner->relrefs, newmax * sizeof(Relation));
-		owner->maxrelrefs = newmax;
-	}
+	ResourceArrayEnlarge(&(owner->relrefarr));
 }
 
 /*
@@ -885,9 +1083,7 @@ ResourceOwnerEnlargeRelationRefs(ResourceOwner owner)
 void
 ResourceOwnerRememberRelationRef(ResourceOwner owner, Relation rel)
 {
-	Assert(owner->nrelrefs < owner->maxrelrefs);
-	owner->relrefs[owner->nrelrefs] = rel;
-	owner->nrelrefs++;
+	ResourceArrayAdd(&(owner->relrefarr), (const void *) &rel);
 }
 
 /*
@@ -896,25 +1092,12 @@ ResourceOwnerRememberRelationRef(ResourceOwner owner, Relation rel)
 void
 ResourceOwnerForgetRelationRef(ResourceOwner owner, Relation rel)
 {
-	Relation   *relrefs = owner->relrefs;
-	int			nr1 = owner->nrelrefs - 1;
-	int			i;
+	bool		res = ResourceArrayRemove(&(owner->relrefarr),
+										  (const void *) &rel);
 
-	for (i = nr1; i >= 0; i--)
-	{
-		if (relrefs[i] == rel)
-		{
-			while (i < nr1)
-			{
-				relrefs[i] = relrefs[i + 1];
-				i++;
-			}
-			owner->nrelrefs = nr1;
-			return;
-		}
-	}
-	elog(ERROR, "relcache reference %s is not owned by resource owner %s",
-		 RelationGetRelationName(rel), owner->name);
+	if (!res)
+		elog(ERROR, "relcache reference %s is not owned by resource owner %s",
+			 RelationGetRelationName(rel), owner->name);
 }
 
 /*
@@ -937,25 +1120,7 @@ PrintRelCacheLeakWarning(Relation rel)
 void
 ResourceOwnerEnlargePlanCacheRefs(ResourceOwner owner)
 {
-	int			newmax;
-
-	if (owner->nplanrefs < owner->maxplanrefs)
-		return;					/* nothing to do */
-
-	if (owner->planrefs == NULL)
-	{
-		newmax = 16;
-		owner->planrefs = (CachedPlan **)
-			MemoryContextAlloc(TopMemoryContext, newmax * sizeof(CachedPlan *));
-		owner->maxplanrefs = newmax;
-	}
-	else
-	{
-		newmax = owner->maxplanrefs * 2;
-		owner->planrefs = (CachedPlan **)
-			repalloc(owner->planrefs, newmax * sizeof(CachedPlan *));
-		owner->maxplanrefs = newmax;
-	}
+	ResourceArrayEnlarge(&(owner->planrefarr));
 }
 
 /*
@@ -966,9 +1131,7 @@ ResourceOwnerEnlargePlanCacheRefs(ResourceOwner owner)
 void
 ResourceOwnerRememberPlanCacheRef(ResourceOwner owner, CachedPlan *plan)
 {
-	Assert(owner->nplanrefs < owner->maxplanrefs);
-	owner->planrefs[owner->nplanrefs] = plan;
-	owner->nplanrefs++;
+	ResourceArrayAdd(&(owner->planrefarr), (const void *) &plan);
 }
 
 /*
@@ -977,25 +1140,12 @@ ResourceOwnerRememberPlanCacheRef(ResourceOwner owner, CachedPlan *plan)
 void
 ResourceOwnerForgetPlanCacheRef(ResourceOwner owner, CachedPlan *plan)
 {
-	CachedPlan **planrefs = owner->planrefs;
-	int			np1 = owner->nplanrefs - 1;
-	int			i;
+	bool		res = ResourceArrayRemove(&(owner->planrefarr),
+										  (const void *) &plan);
 
-	for (i = np1; i >= 0; i--)
-	{
-		if (planrefs[i] == plan)
-		{
-			while (i < np1)
-			{
-				planrefs[i] = planrefs[i + 1];
-				i++;
-			}
-			owner->nplanrefs = np1;
-			return;
-		}
-	}
-	elog(ERROR, "plancache reference %p is not owned by resource owner %s",
-		 plan, owner->name);
+	if (!res)
+		elog(ERROR, "plancache reference %p is not owned by resource owner %s",
+			 plan, owner->name);
 }
 
 /*
@@ -1017,25 +1167,7 @@ PrintPlanCacheLeakWarning(CachedPlan *plan)
 void
 ResourceOwnerEnlargeTupleDescs(ResourceOwner owner)
 {
-	int			newmax;
-
-	if (owner->ntupdescs < owner->maxtupdescs)
-		return;					/* nothing to do */
-
-	if (owner->tupdescs == NULL)
-	{
-		newmax = 16;
-		owner->tupdescs = (TupleDesc *)
-			MemoryContextAlloc(TopMemoryContext, newmax * sizeof(TupleDesc));
-		owner->maxtupdescs = newmax;
-	}
-	else
-	{
-		newmax = owner->maxtupdescs * 2;
-		owner->tupdescs = (TupleDesc *)
-			repalloc(owner->tupdescs, newmax * sizeof(TupleDesc));
-		owner->maxtupdescs = newmax;
-	}
+	ResourceArrayEnlarge(&(owner->tupdescarr));
 }
 
 /*
@@ -1046,9 +1178,7 @@ ResourceOwnerEnlargeTupleDescs(ResourceOwner owner)
 void
 ResourceOwnerRememberTupleDesc(ResourceOwner owner, TupleDesc tupdesc)
 {
-	Assert(owner->ntupdescs < owner->maxtupdescs);
-	owner->tupdescs[owner->ntupdescs] = tupdesc;
-	owner->ntupdescs++;
+	ResourceArrayAdd(&(owner->tupdescarr), (const void *) &tupdesc);
 }
 
 /*
@@ -1057,25 +1187,12 @@ ResourceOwnerRememberTupleDesc(ResourceOwner owner, TupleDesc tupdesc)
 void
 ResourceOwnerForgetTupleDesc(ResourceOwner owner, TupleDesc tupdesc)
 {
-	TupleDesc  *tupdescs = owner->tupdescs;
-	int			nt1 = owner->ntupdescs - 1;
-	int			i;
+	bool		res = ResourceArrayRemove(&(owner->tupdescarr),
+										  (const void *) &tupdesc);
 
-	for (i = nt1; i >= 0; i--)
-	{
-		if (tupdescs[i] == tupdesc)
-		{
-			while (i < nt1)
-			{
-				tupdescs[i] = tupdescs[i + 1];
-				i++;
-			}
-			owner->ntupdescs = nt1;
-			return;
-		}
-	}
-	elog(ERROR, "tupdesc reference %p is not owned by resource owner %s",
-		 tupdesc, owner->name);
+	if (!res)
+		elog(ERROR, "tupdesc reference %p is not owned by resource owner %s",
+			 tupdesc, owner->name);
 }
 
 /*
@@ -1099,25 +1216,7 @@ PrintTupleDescLeakWarning(TupleDesc tupdesc)
 void
 ResourceOwnerEnlargeSnapshots(ResourceOwner owner)
 {
-	int			newmax;
-
-	if (owner->nsnapshots < owner->maxsnapshots)
-		return;					/* nothing to do */
-
-	if (owner->snapshots == NULL)
-	{
-		newmax = 16;
-		owner->snapshots = (Snapshot *)
-			MemoryContextAlloc(TopMemoryContext, newmax * sizeof(Snapshot));
-		owner->maxsnapshots = newmax;
-	}
-	else
-	{
-		newmax = owner->maxsnapshots * 2;
-		owner->snapshots = (Snapshot *)
-			repalloc(owner->snapshots, newmax * sizeof(Snapshot));
-		owner->maxsnapshots = newmax;
-	}
+	ResourceArrayEnlarge(&(owner->snapshotarr));
 }
 
 /*
@@ -1128,9 +1227,7 @@ ResourceOwnerEnlargeSnapshots(ResourceOwner owner)
 void
 ResourceOwnerRememberSnapshot(ResourceOwner owner, Snapshot snapshot)
 {
-	Assert(owner->nsnapshots < owner->maxsnapshots);
-	owner->snapshots[owner->nsnapshots] = snapshot;
-	owner->nsnapshots++;
+	ResourceArrayAdd(&(owner->snapshotarr), (const void *) &snapshot);
 }
 
 /*
@@ -1139,25 +1236,12 @@ ResourceOwnerRememberSnapshot(ResourceOwner owner, Snapshot snapshot)
 void
 ResourceOwnerForgetSnapshot(ResourceOwner owner, Snapshot snapshot)
 {
-	Snapshot   *snapshots = owner->snapshots;
-	int			ns1 = owner->nsnapshots - 1;
-	int			i;
+	bool		res = ResourceArrayRemove(&(owner->snapshotarr),
+										  (const void *) &snapshot);
 
-	for (i = ns1; i >= 0; i--)
-	{
-		if (snapshots[i] == snapshot)
-		{
-			while (i < ns1)
-			{
-				snapshots[i] = snapshots[i + 1];
-				i++;
-			}
-			owner->nsnapshots = ns1;
-			return;
-		}
-	}
-	elog(ERROR, "snapshot reference %p is not owned by resource owner %s",
-		 snapshot, owner->name);
+	if (!res)
+		elog(ERROR, "snapshot reference %p is not owned by resource owner %s",
+			 snapshot, owner->name);
 }
 
 /*
@@ -1182,25 +1266,7 @@ PrintSnapshotLeakWarning(Snapshot snapshot)
 void
 ResourceOwnerEnlargeFiles(ResourceOwner owner)
 {
-	int			newmax;
-
-	if (owner->nfiles < owner->maxfiles)
-		return;					/* nothing to do */
-
-	if (owner->files == NULL)
-	{
-		newmax = 16;
-		owner->files = (File *)
-			MemoryContextAlloc(TopMemoryContext, newmax * sizeof(File));
-		owner->maxfiles = newmax;
-	}
-	else
-	{
-		newmax = owner->maxfiles * 2;
-		owner->files = (File *)
-			repalloc(owner->files, newmax * sizeof(File));
-		owner->maxfiles = newmax;
-	}
+	ResourceArrayEnlarge(&(owner->filearr));
 }
 
 /*
@@ -1211,9 +1277,7 @@ ResourceOwnerEnlargeFiles(ResourceOwner owner)
 void
 ResourceOwnerRememberFile(ResourceOwner owner, File file)
 {
-	Assert(owner->nfiles < owner->maxfiles);
-	owner->files[owner->nfiles] = file;
-	owner->nfiles++;
+	ResourceArrayAdd(&(owner->filearr), (const void *) &file);
 }
 
 /*
@@ -1222,25 +1286,12 @@ ResourceOwnerRememberFile(ResourceOwner owner, File file)
 void
 ResourceOwnerForgetFile(ResourceOwner owner, File file)
 {
-	File	   *files = owner->files;
-	int			ns1 = owner->nfiles - 1;
-	int			i;
+	bool		res = ResourceArrayRemove(&(owner->filearr),
+										  (const void *) &file);
 
-	for (i = ns1; i >= 0; i--)
-	{
-		if (files[i] == file)
-		{
-			while (i < ns1)
-			{
-				files[i] = files[i + 1];
-				i++;
-			}
-			owner->nfiles = ns1;
-			return;
-		}
-	}
-	elog(ERROR, "temporery file %d is not owned by resource owner %s",
-		 file, owner->name);
+	if (!res)
+		elog(ERROR, "temporery file %d is not owned by resource owner %s",
+			 file, owner->name);
 }
 
 
@@ -1265,26 +1316,7 @@ PrintFileLeakWarning(File file)
 void
 ResourceOwnerEnlargeDSMs(ResourceOwner owner)
 {
-	int			newmax;
-
-	if (owner->ndsms < owner->maxdsms)
-		return;					/* nothing to do */
-
-	if (owner->dsms == NULL)
-	{
-		newmax = 16;
-		owner->dsms = (dsm_segment **)
-			MemoryContextAlloc(TopMemoryContext,
-							   newmax * sizeof(dsm_segment *));
-		owner->maxdsms = newmax;
-	}
-	else
-	{
-		newmax = owner->maxdsms * 2;
-		owner->dsms = (dsm_segment **)
-			repalloc(owner->dsms, newmax * sizeof(dsm_segment *));
-		owner->maxdsms = newmax;
-	}
+	ResourceArrayEnlarge(&(owner->dsmarr));
 }
 
 /*
@@ -1295,9 +1327,7 @@ ResourceOwnerEnlargeDSMs(ResourceOwner owner)
 void
 ResourceOwnerRememberDSM(ResourceOwner owner, dsm_segment *seg)
 {
-	Assert(owner->ndsms < owner->maxdsms);
-	owner->dsms[owner->ndsms] = seg;
-	owner->ndsms++;
+	ResourceArrayAdd(&(owner->dsmarr), (const void *) &seg);
 }
 
 /*
@@ -1306,26 +1336,12 @@ ResourceOwnerRememberDSM(ResourceOwner owner, dsm_segment *seg)
 void
 ResourceOwnerForgetDSM(ResourceOwner owner, dsm_segment *seg)
 {
-	dsm_segment **dsms = owner->dsms;
-	int			ns1 = owner->ndsms - 1;
-	int			i;
+	bool		res = ResourceArrayRemove(&(owner->dsmarr),
+										  (const void *) &seg);
 
-	for (i = ns1; i >= 0; i--)
-	{
-		if (dsms[i] == seg)
-		{
-			while (i < ns1)
-			{
-				dsms[i] = dsms[i + 1];
-				i++;
-			}
-			owner->ndsms = ns1;
-			return;
-		}
-	}
-	elog(ERROR,
-		 "dynamic shared memory segment %u is not owned by resource owner %s",
-		 dsm_segment_handle(seg), owner->name);
+	if (!res)
+		elog(ERROR, "dynamic shared memory segment %u is not owned by resource"
+			 " owner %s", dsm_segment_handle(seg), owner->name);
 }
 
 
#4Robert Haas
robertmhaas@gmail.com
In reply to: Aleksander Alekseev (#3)
Re: Patch: ResourceOwner optimization for tables with many partitions

On Wed, Dec 9, 2015 at 8:30 AM, Aleksander Alekseev
<a.alekseev@postgrespro.ru> wrote:

Hello, Robert

Thanks for your review. I believe I fixed items 1, 2 and 3 (see
attachment). Also I would like to clarify item 4.

4. It mixes together multiple ideas in a single patch, not only
introducing a hashing concept but also striping a brand-new layer of
abstraction across the resource-owner mechanism. I am not sure that
layer of abstraction is a very good idea, but if it needs to be done,
I think it should be a separate patch.

Do I right understand that you suggest following?

Current patch should be split in two parts. In first patch we create
and use ResourceArray with array-based implementation (abstraction
layer). Then we apply second patch which change ResourceArray
implementation to hashing based (optimization).

Well, sorta. To be honest, I think this patch is really ugly. If we
were going to do this then, yes, I would want to split the patch into
two parts along those lines. But actually I don't really want to do
it this way at all. It's not that I don't want the performance
benefits: I do. But the current code is really easy to read and
extremely simple, and this changes it into something that is a heck of
a lot harder to read and understand. I'm not sure exactly what to do
about that, but it seems like a problem.

--
Robert Haas
EnterpriseDB: http://www.enterprisedb.com
The Enterprise PostgreSQL Company

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

#5Tom Lane
tgl@sss.pgh.pa.us
In reply to: Robert Haas (#4)
Re: Patch: ResourceOwner optimization for tables with many partitions

Robert Haas <robertmhaas@gmail.com> writes:

Well, sorta. To be honest, I think this patch is really ugly. If we
were going to do this then, yes, I would want to split the patch into
two parts along those lines. But actually I don't really want to do
it this way at all. It's not that I don't want the performance
benefits: I do. But the current code is really easy to read and
extremely simple, and this changes it into something that is a heck of
a lot harder to read and understand. I'm not sure exactly what to do
about that, but it seems like a problem.

I haven't read the patch in any detail, but keep in mind that part of the
API contract for ResourceOwners is that ResourceOwnerRememberFoo() cannot
fail. Period. So any patch that makes those functions less than trivial
is going to be really suspect from a reliability standpoint. It would be
advisable for example that hash_any not suddenly become covered by the
"must not fail" requirement.

BTW, I do not think you can get away with the requirement that all-zeroes
isn't a valid resource representation. It might be okay today but it's
hardly future-proof.

regards, tom lane

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

#6Robert Haas
robertmhaas@gmail.com
In reply to: Tom Lane (#5)
Re: Patch: ResourceOwner optimization for tables with many partitions

On Thu, Dec 10, 2015 at 2:11 PM, Tom Lane <tgl@sss.pgh.pa.us> wrote:

Robert Haas <robertmhaas@gmail.com> writes:

Well, sorta. To be honest, I think this patch is really ugly. If we
were going to do this then, yes, I would want to split the patch into
two parts along those lines. But actually I don't really want to do
it this way at all. It's not that I don't want the performance
benefits: I do. But the current code is really easy to read and
extremely simple, and this changes it into something that is a heck of
a lot harder to read and understand. I'm not sure exactly what to do
about that, but it seems like a problem.

I haven't read the patch in any detail, but keep in mind that part of the
API contract for ResourceOwners is that ResourceOwnerRememberFoo() cannot
fail. Period. So any patch that makes those functions less than trivial
is going to be really suspect from a reliability standpoint. It would be
advisable for example that hash_any not suddenly become covered by the
"must not fail" requirement.

Hmm. I hadn't thought about that.

I wonder if there's a simpler approach to this whole problem. What
the patch aims to do is allow resources to be freed in arbitrary order
without slowdown. But do we really need that? Resource owners are
currently optimized for freeing resources in the order opposite to the
order of allocation. I bet in this case the order in which they are
freed isn't random, but is exactly in order of allocation. If so, we
might be able to either (a) jigger things so that freeing in order of
allocation is efficient or (b) jigger things so that the resources are
released in reverse order of allocation as the resource owner code
expects. That might be simpler and less invasive than this fix.

Then again, it's somehow appealing for higher-level code not to have
to care about this...

--
Robert Haas
EnterpriseDB: http://www.enterprisedb.com
The Enterprise PostgreSQL Company

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

#7Aleksander Alekseev
a.alekseev@postgrespro.ru
In reply to: Robert Haas (#6)
Re: Patch: ResourceOwner optimization for tables with many partitions

To be honest, I think this patch is really ugly. [...] I'm not sure
exactly what to do about that, but it seems like a problem.

I have an idea. There are actually two types of resources - int-like
(buffers, files) and void*-like (RelationRef, TupleDesc, ...). What if
I split ResourceArray into IntResourceArray and PointerResourceArray? It
would definitely solve ugliness problem --- no more memcpy's, char[]
buffers, etc.

It would be advisable for example that hash_any not suddenly become
covered by the "must not fail" requirement.

Frankly I can't think of any case when hash_any could or should fail.
Maybe we should just add a "must not fail" constraint to hash_any
description?

Also I could use some other hash implementation. It may be reasonable
in this case since size of data I would like to hash is small and known
in advance.

BTW, I do not think you can get away with the requirement that
all-zeroes isn't a valid resource representation. It might be okay
today but it's hardly future-proof.

Agree. I could store a value that should be considered as "zero" in
ResourceArray. It would be InvalidBuffer for buffers, -1 for files and
NULL for all void*-types. Does such solution sounds OK?

Best regards,
Aleksander

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

#8Aleksander Alekseev
a.alekseev@postgrespro.ru
In reply to: Aleksander Alekseev (#7)
Re: Patch: ResourceOwner optimization for tables with many partitions

It would be InvalidBuffer for buffers, -1 for files and NULL for all
void*-types. Does such solution sounds OK?

On second thought I believe there is no reason for storing anything for
void*-like types. I could just hardcode NULL in PointerResourceArray.

Best regards,
Aleksander

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

#9Aleksander Alekseev
a.alekseev@postgrespro.ru
In reply to: Robert Haas (#4)
2 attachment(s)
Re: Patch: ResourceOwner optimization for tables with many partitions

Hello, Robert

Here is my fix for item 4.

Best regards,
Aleksander

On Thu, 10 Dec 2015 11:37:23 -0500
Robert Haas <robertmhaas@gmail.com> wrote:

Show quoted text

On Wed, Dec 9, 2015 at 8:30 AM, Aleksander Alekseev
<a.alekseev@postgrespro.ru> wrote:

Hello, Robert

Thanks for your review. I believe I fixed items 1, 2 and 3 (see
attachment). Also I would like to clarify item 4.

4. It mixes together multiple ideas in a single patch, not only
introducing a hashing concept but also striping a brand-new layer
of abstraction across the resource-owner mechanism. I am not sure
that layer of abstraction is a very good idea, but if it needs to
be done, I think it should be a separate patch.

Do I right understand that you suggest following?

Current patch should be split in two parts. In first patch we create
and use ResourceArray with array-based implementation (abstraction
layer). Then we apply second patch which change ResourceArray
implementation to hashing based (optimization).

Well, sorta. To be honest, I think this patch is really ugly. If we
were going to do this then, yes, I would want to split the patch into
two parts along those lines. But actually I don't really want to do
it this way at all. It's not that I don't want the performance
benefits: I do. But the current code is really easy to read and
extremely simple, and this changes it into something that is a heck of
a lot harder to read and understand. I'm not sure exactly what to do
about that, but it seems like a problem.

Attachments:

resource-owner-optimization-v3-step1.patchtext/x-patchDownload
diff --git a/src/backend/utils/resowner/resowner.c b/src/backend/utils/resowner/resowner.c
index 0e7acbf..39324fe 100644
--- a/src/backend/utils/resowner/resowner.c
+++ b/src/backend/utils/resowner/resowner.c
@@ -29,6 +29,209 @@
 #include "utils/snapmgr.h"
 
 /*
+ * ResourceArray is a common structure for storing different types of resources.
+ *
+ * ResourceOwner can own `HeapTuple`s, `Relation`s, `Snapshot`s, etc. For
+ * each resource type there are procedures ResourceOwnerRemember* and
+ * ResourceOwnerForget*. There are also ResourceOwnerEnlarge* procedures
+ * which should be called before corresponding ResourceOwnerRemember* calls
+ * (see below). Internally each type of resource is stored in separate
+ * ResourceArray.
+ */
+typedef struct ResourceArray
+{
+	char	   *itemsarr;		/* buffer for storing values */
+	uint32		itemsizelg:2;	/* sizeof one item log 2 */
+	uint32		capacity:30;	/* capacity of array */
+	uint32		nitems;			/* how many items is stored in items array */
+}	ResourceArray;
+
+/*
+ * This number is used as initial size of resource array. If given number of
+ * items is not enough, we double array size and reallocate memory.
+ */
+#define RESARRAY_INIT_SIZE 16
+
+/*
+ * Such type of callback function is called when resource stored in
+ * ResourceArray is released using ResourceArrayFree. Callback should
+ * _actually_ release a resource so nitems value will be decremented.
+ */
+typedef void (*ResourceArrayRemoveCallback) (const void *ref, bool isCommit);
+
+/*
+ * Initialize ResourceArray
+ */
+static void
+ResourceArrayInit(ResourceArray * resarr, uint32 itemsize)
+{
+	Assert(itemsize == sizeof(int) || itemsize == sizeof(void *));
+	Assert(resarr->itemsarr == NULL);
+	Assert(resarr->capacity == 0);
+	Assert(resarr->nitems == 0);
+
+	resarr->itemsizelg = 0;
+	while (itemsize > 1)
+	{
+		resarr->itemsizelg++;
+		itemsize >>= 1;
+	}
+
+	Assert(resarr->itemsizelg == 2 || resarr->itemsizelg == 3);
+}
+
+/*
+ * Add a resource to ResourceArray
+ *
+ * Caller must have previously done ResourceArrayEnlarge()
+ */
+static void
+ResourceArrayAdd(ResourceArray * resarr, const void *dataref)
+{
+	char	   *itemptr;
+	uint32		itemsize = 1 << resarr->itemsizelg;
+
+	Assert(resarr->capacity > 0);
+	Assert(resarr->itemsarr != NULL);
+	Assert(resarr->nitems < resarr->capacity);
+
+	/*
+	 * Read next line as:
+	 *
+	 * itemptr = &(itemsarr[resarr->nitems])
+	 *
+	 * We use binary shift since compiler doesn't know that itemsize is always
+	 * power of two. It would use multiplication instead of efficient binary
+	 * shift in code `resarr->nitems * itemsize`.
+	 */
+	itemptr = resarr->itemsarr + (resarr->nitems << resarr->itemsizelg);
+	memcpy(itemptr, dataref, itemsize);
+	resarr->nitems++;
+}
+
+/*
+ * Remove a resource from ResourceArray
+ *
+ * Returns true on success, false if resource was not found
+ */
+static bool
+ResourceArrayRemove(ResourceArray * resarr, const void *dataref)
+{
+	Datum		idx;
+	char	   *itemptr;
+	char	   *lastitemptr;
+	uint32		itemsize = 1 << resarr->itemsizelg;
+
+	Assert(resarr->capacity > 0);
+	Assert(resarr->itemsarr != NULL);
+
+	lastitemptr = resarr->itemsarr +
+		((resarr->nitems - 1) << resarr->itemsizelg);
+	for (idx = 0; idx < resarr->nitems; idx++)
+	{
+		/*
+		 * Read next line as:
+		 *
+		 * itemptr = &(itemsarr[idx])
+		 *
+		 * We use binary shift since compiler doesn't know that itemsize is
+		 * always power of two. It would use multiplication instead of
+		 * efficient binary shift in code `idx * itemsize`.
+		 */
+		itemptr = resarr->itemsarr + (idx << resarr->itemsizelg);
+		if (memcmp(itemptr, dataref, itemsize) == 0)
+		{
+			memcpy(itemptr, lastitemptr, itemsize);
+			resarr->nitems--;
+			return true;
+		}
+	}
+
+	return false;
+}
+
+/*
+ * Make sure there is a room for at least one more resource in an array.
+ *
+ * This is separate from actually inserting a resource because if we run out
+ * of memory, it's critical to do so *before* acquiring the resource.
+ */
+static void
+ResourceArrayEnlarge(ResourceArray * resarr)
+{
+	uint32		oldcap;
+	char	   *olditemsarr;
+	char	   *olditemptr;
+	uint32		itemsize = 1 << resarr->itemsizelg;
+
+	Assert(resarr->itemsizelg != 0);
+
+	if (resarr->nitems < resarr->capacity)
+		return;					/* nothing to do */
+
+	olditemsarr = resarr->itemsarr;
+	oldcap = resarr->capacity;
+
+	resarr->capacity = oldcap > 0 ? oldcap * 2 : RESARRAY_INIT_SIZE;
+	resarr->itemsarr = (char *)
+		MemoryContextAllocZero(TopMemoryContext,
+							   resarr->capacity * itemsize);
+	resarr->nitems = 0;
+
+	if (olditemsarr != NULL)
+	{
+		while (oldcap > 0)
+		{
+			oldcap--;
+
+			/*
+			 * Read next line as:
+			 *
+			 * olditemptr = &(olditemsarr[oldcap])
+			 *
+			 * We use binary shift since compiler doesn't know that itemsize
+			 * is always power of two. It would use multiplication instead of
+			 * efficient binary shift in code `oldcap * itemsize`.
+			 */
+			olditemptr = olditemsarr + (oldcap << resarr->itemsizelg);
+			ResourceArrayAdd(resarr, olditemptr);
+		}
+		pfree(olditemsarr);
+	}
+}
+
+/*
+ * Remove all resources in array and call a callback function for each release.
+ */
+static void
+ResourceArrayRemoveAll(ResourceArray * resarr,
+					   ResourceArrayRemoveCallback releasecb,
+					   bool isCommit)
+{
+	while (resarr->nitems > 0)
+	{
+		releasecb(resarr->itemsarr, isCommit);
+	}
+}
+
+/*
+ * Return ResourceArray to initial state
+ */
+static void
+ResourceArrayFree(ResourceArray * resarr)
+{
+	Assert(resarr->nitems == 0);
+
+	resarr->capacity = 0;
+
+	if (!resarr->itemsarr)
+		return;
+
+	pfree(resarr->itemsarr);
+	resarr->itemsarr = NULL;
+}
+
+/*
  * To speed up bulk releasing or reassigning locks from a resource owner to
  * its parent, each resource owner has a small cache of locks it owns. The
  * lock manager has the same information in its local lock hash table, and
@@ -56,53 +259,22 @@ typedef struct ResourceOwnerData
 	ResourceOwner nextchild;	/* next child of same parent */
 	const char *name;			/* name (just for debugging) */
 
-	/* We have built-in support for remembering owned buffers */
-	int			nbuffers;		/* number of owned buffer pins */
-	Buffer	   *buffers;		/* dynamically allocated array */
-	int			maxbuffers;		/* currently allocated array size */
-
 	/* We can remember up to MAX_RESOWNER_LOCKS references to local locks. */
 	int			nlocks;			/* number of owned locks */
 	LOCALLOCK  *locks[MAX_RESOWNER_LOCKS];		/* list of owned locks */
 
-	/* We have built-in support for remembering catcache references */
-	int			ncatrefs;		/* number of owned catcache pins */
-	HeapTuple  *catrefs;		/* dynamically allocated array */
-	int			maxcatrefs;		/* currently allocated array size */
-
-	int			ncatlistrefs;	/* number of owned catcache-list pins */
-	CatCList  **catlistrefs;	/* dynamically allocated array */
-	int			maxcatlistrefs; /* currently allocated array size */
-
-	/* We have built-in support for remembering relcache references */
-	int			nrelrefs;		/* number of owned relcache pins */
-	Relation   *relrefs;		/* dynamically allocated array */
-	int			maxrelrefs;		/* currently allocated array size */
-
-	/* We have built-in support for remembering plancache references */
-	int			nplanrefs;		/* number of owned plancache pins */
-	CachedPlan **planrefs;		/* dynamically allocated array */
-	int			maxplanrefs;	/* currently allocated array size */
-
-	/* We have built-in support for remembering tupdesc references */
-	int			ntupdescs;		/* number of owned tupdesc references */
-	TupleDesc  *tupdescs;		/* dynamically allocated array */
-	int			maxtupdescs;	/* currently allocated array size */
-
-	/* We have built-in support for remembering snapshot references */
-	int			nsnapshots;		/* number of owned snapshot references */
-	Snapshot   *snapshots;		/* dynamically allocated array */
-	int			maxsnapshots;	/* currently allocated array size */
-
-	/* We have built-in support for remembering open temporary files */
-	int			nfiles;			/* number of owned temporary files */
-	File	   *files;			/* dynamically allocated array */
-	int			maxfiles;		/* currently allocated array size */
-
-	/* We have built-in support for remembering dynamic shmem segments */
-	int			ndsms;			/* number of owned shmem segments */
-	dsm_segment **dsms;			/* dynamically allocated array */
-	int			maxdsms;		/* currently allocated array size */
+	/* We have built-in support for remembering: */
+
+	ResourceArray catrefarr;	/* `HeapTuple`s */
+	ResourceArray catlistrefarr;	/* `ResourceOwner`s */
+	ResourceArray relrefarr;	/* `Relation`s */
+	ResourceArray planrefarr;	/* `CachedPlan*`s */
+	ResourceArray tupdescarr;	/* `TupleDesc`s */
+	ResourceArray snapshotarr;	/* `Snapshot`s */
+	ResourceArray dsmarr;		/* `dsm_segment*`s */
+	ResourceArray bufferarr;	/* `Buffer`s  */
+	ResourceArray filearr;		/* `File`s */
+
 }	ResourceOwnerData;
 
 
@@ -168,6 +340,16 @@ ResourceOwnerCreate(ResourceOwner parent, const char *name)
 		parent->firstchild = owner;
 	}
 
+	ResourceArrayInit(&(owner->catrefarr), sizeof(HeapTuple));
+	ResourceArrayInit(&(owner->catlistrefarr), sizeof(ResourceOwner));
+	ResourceArrayInit(&(owner->relrefarr), sizeof(Relation));
+	ResourceArrayInit(&(owner->planrefarr), sizeof(CachedPlan *));
+	ResourceArrayInit(&(owner->tupdescarr), sizeof(TupleDesc));
+	ResourceArrayInit(&(owner->snapshotarr), sizeof(Snapshot));
+	ResourceArrayInit(&(owner->dsmarr), sizeof(dsm_segment *));
+	ResourceArrayInit(&(owner->bufferarr), sizeof(Buffer));
+	ResourceArrayInit(&(owner->filearr), sizeof(File));
+
 	return owner;
 }
 
@@ -221,6 +403,96 @@ ResourceOwnerRelease(ResourceOwner owner,
 }
 
 static void
+ReleaseCachedPlanCallback(const void *dataref, bool isCommit)
+{
+	CachedPlan *res = *((CachedPlan **) dataref);
+
+	if (isCommit)
+		PrintPlanCacheLeakWarning(res);
+	ReleaseCachedPlan(res, true);
+}
+
+static void
+ReleaseDSMCallback(const void *dataref, bool isCommit)
+{
+	dsm_segment *res = *((dsm_segment **) dataref);
+
+	if (isCommit)
+		PrintDSMLeakWarning(res);
+	dsm_detach(res);
+}
+
+static void
+ReleaseTupleDescCallback(const void *dataref, bool isCommit)
+{
+	TupleDesc	res = *((TupleDesc *) dataref);
+
+	if (isCommit)
+		PrintTupleDescLeakWarning(res);
+	DecrTupleDescRefCount(res);
+}
+
+static void
+ReleaseSnapshotCallback(const void *dataref, bool isCommit)
+{
+	Snapshot	res = *((Snapshot *) dataref);
+
+	if (isCommit)
+		PrintSnapshotLeakWarning(res);
+	UnregisterSnapshot(res);
+}
+
+static void
+ReleaseRelationCallback(const void *dataref, bool isCommit)
+{
+	Relation	res = *((Relation *) dataref);
+
+	if (isCommit)
+		PrintRelCacheLeakWarning(res);
+	RelationClose(res);
+}
+
+static void
+ReleaseCatCacheListCallback(const void *dataref, bool isCommit)
+{
+	CatCList   *res = *((CatCList **) dataref);
+
+	if (isCommit)
+		PrintCatCacheListLeakWarning(res);
+	ReleaseCatCacheList(res);
+}
+
+static void
+ReleaseCatCacheCallback(const void *dataref, bool isCommit)
+{
+	HeapTuple	res = *((HeapTuple *) dataref);
+
+	if (isCommit)
+		PrintCatCacheLeakWarning(res);
+	ReleaseCatCache(res);
+}
+
+static void
+ReleaseBufferCallback(const void *dataref, bool isCommit)
+{
+	Buffer		res = *((Buffer *) dataref);
+
+	if (isCommit)
+		PrintBufferLeakWarning(res);
+	ReleaseBuffer(res);
+}
+
+static void
+ReleaseFileCallback(const void *dataref, bool isCommit)
+{
+	File		res = *((File *) dataref);
+
+	if (isCommit)
+		PrintFileLeakWarning(res);
+	FileClose(res);
+}
+
+static void
 ResourceOwnerReleaseInternal(ResourceOwner owner,
 							 ResourceReleasePhase phase,
 							 bool isCommit,
@@ -252,46 +524,17 @@ ResourceOwnerReleaseInternal(ResourceOwner owner,
 		 * During a commit, there shouldn't be any remaining pins --- that
 		 * would indicate failure to clean up the executor correctly --- so
 		 * issue warnings.  In the abort case, just clean up quietly.
-		 *
-		 * We are careful to do the releasing back-to-front, so as to avoid
-		 * O(N^2) behavior in ResourceOwnerForgetBuffer().
 		 */
-		while (owner->nbuffers > 0)
-		{
-			if (isCommit)
-				PrintBufferLeakWarning(owner->buffers[owner->nbuffers - 1]);
-			ReleaseBuffer(owner->buffers[owner->nbuffers - 1]);
-		}
+		ResourceArrayRemoveAll(&(owner->bufferarr),
+							   ReleaseBufferCallback, isCommit);
 
-		/*
-		 * Release relcache references.  Note that RelationClose will remove
-		 * the relref entry from my list, so I just have to iterate till there
-		 * are none.
-		 *
-		 * As with buffer pins, warn if any are left at commit time, and
-		 * release back-to-front for speed.
-		 */
-		while (owner->nrelrefs > 0)
-		{
-			if (isCommit)
-				PrintRelCacheLeakWarning(owner->relrefs[owner->nrelrefs - 1]);
-			RelationClose(owner->relrefs[owner->nrelrefs - 1]);
-		}
+		/* Ditto for relcache references. */
+		ResourceArrayRemoveAll(&(owner->relrefarr),
+							   ReleaseRelationCallback, isCommit);
 
-		/*
-		 * Release dynamic shared memory segments.  Note that dsm_detach()
-		 * will remove the segment from my list, so I just have to iterate
-		 * until there are none.
-		 *
-		 * As in the preceding cases, warn if there are leftover at commit
-		 * time.
-		 */
-		while (owner->ndsms > 0)
-		{
-			if (isCommit)
-				PrintDSMLeakWarning(owner->dsms[owner->ndsms - 1]);
-			dsm_detach(owner->dsms[owner->ndsms - 1]);
-		}
+		/* Ditto for dynamic shared memory segments */
+		ResourceArrayRemoveAll(&(owner->dsmarr),
+							   ReleaseDSMCallback, isCommit);
 	}
 	else if (phase == RESOURCE_RELEASE_LOCKS)
 	{
@@ -351,48 +594,28 @@ ResourceOwnerReleaseInternal(ResourceOwner owner,
 		 * As with buffer pins, warn if any are left at commit time, and
 		 * release back-to-front for speed.
 		 */
-		while (owner->ncatrefs > 0)
-		{
-			if (isCommit)
-				PrintCatCacheLeakWarning(owner->catrefs[owner->ncatrefs - 1]);
-			ReleaseCatCache(owner->catrefs[owner->ncatrefs - 1]);
-		}
+		ResourceArrayRemoveAll(&(owner->catrefarr),
+							   ReleaseCatCacheCallback, isCommit);
+
 		/* Ditto for catcache lists */
-		while (owner->ncatlistrefs > 0)
-		{
-			if (isCommit)
-				PrintCatCacheListLeakWarning(owner->catlistrefs[owner->ncatlistrefs - 1]);
-			ReleaseCatCacheList(owner->catlistrefs[owner->ncatlistrefs - 1]);
-		}
+		ResourceArrayRemoveAll(&(owner->catlistrefarr),
+							   ReleaseCatCacheListCallback, isCommit);
+
 		/* Ditto for plancache references */
-		while (owner->nplanrefs > 0)
-		{
-			if (isCommit)
-				PrintPlanCacheLeakWarning(owner->planrefs[owner->nplanrefs - 1]);
-			ReleaseCachedPlan(owner->planrefs[owner->nplanrefs - 1], true);
-		}
+		ResourceArrayRemoveAll(&(owner->planrefarr),
+							   ReleaseCachedPlanCallback, isCommit);
+
 		/* Ditto for tupdesc references */
-		while (owner->ntupdescs > 0)
-		{
-			if (isCommit)
-				PrintTupleDescLeakWarning(owner->tupdescs[owner->ntupdescs - 1]);
-			DecrTupleDescRefCount(owner->tupdescs[owner->ntupdescs - 1]);
-		}
+		ResourceArrayRemoveAll(&(owner->tupdescarr),
+							   ReleaseTupleDescCallback, isCommit);
+
 		/* Ditto for snapshot references */
-		while (owner->nsnapshots > 0)
-		{
-			if (isCommit)
-				PrintSnapshotLeakWarning(owner->snapshots[owner->nsnapshots - 1]);
-			UnregisterSnapshot(owner->snapshots[owner->nsnapshots - 1]);
-		}
+		ResourceArrayRemoveAll(&(owner->snapshotarr),
+							   ReleaseSnapshotCallback, isCommit);
 
 		/* Ditto for temporary files */
-		while (owner->nfiles > 0)
-		{
-			if (isCommit)
-				PrintFileLeakWarning(owner->files[owner->nfiles - 1]);
-			FileClose(owner->files[owner->nfiles - 1]);
-		}
+		ResourceArrayRemoveAll(&(owner->filearr),
+							   ReleaseFileCallback, isCommit);
 
 		/* Clean up index scans too */
 		ReleaseResources_hash();
@@ -418,16 +641,7 @@ ResourceOwnerDelete(ResourceOwner owner)
 	Assert(owner != CurrentResourceOwner);
 
 	/* And it better not own any resources, either */
-	Assert(owner->nbuffers == 0);
 	Assert(owner->nlocks == 0 || owner->nlocks == MAX_RESOWNER_LOCKS + 1);
-	Assert(owner->ncatrefs == 0);
-	Assert(owner->ncatlistrefs == 0);
-	Assert(owner->nrelrefs == 0);
-	Assert(owner->ndsms == 0);
-	Assert(owner->nplanrefs == 0);
-	Assert(owner->ntupdescs == 0);
-	Assert(owner->nsnapshots == 0);
-	Assert(owner->nfiles == 0);
 
 	/*
 	 * Delete children.  The recursive call will delink the child from me, so
@@ -444,25 +658,15 @@ ResourceOwnerDelete(ResourceOwner owner)
 	ResourceOwnerNewParent(owner, NULL);
 
 	/* And free the object. */
-	if (owner->buffers)
-		pfree(owner->buffers);
-	if (owner->catrefs)
-		pfree(owner->catrefs);
-	if (owner->catlistrefs)
-		pfree(owner->catlistrefs);
-	if (owner->relrefs)
-		pfree(owner->relrefs);
-	if (owner->planrefs)
-		pfree(owner->planrefs);
-	if (owner->tupdescs)
-		pfree(owner->tupdescs);
-	if (owner->snapshots)
-		pfree(owner->snapshots);
-	if (owner->files)
-		pfree(owner->files);
-	if (owner->dsms)
-		pfree(owner->dsms);
-
+	ResourceArrayFree(&(owner->catrefarr));
+	ResourceArrayFree(&(owner->catlistrefarr));
+	ResourceArrayFree(&(owner->relrefarr));
+	ResourceArrayFree(&(owner->planrefarr));
+	ResourceArrayFree(&(owner->tupdescarr));
+	ResourceArrayFree(&(owner->snapshotarr));
+	ResourceArrayFree(&(owner->dsmarr));
+	ResourceArrayFree(&(owner->bufferarr));
+	ResourceArrayFree(&(owner->filearr));
 	pfree(owner);
 }
 
@@ -575,26 +779,9 @@ UnregisterResourceReleaseCallback(ResourceReleaseCallback callback, void *arg)
 void
 ResourceOwnerEnlargeBuffers(ResourceOwner owner)
 {
-	int			newmax;
-
-	if (owner == NULL ||
-		owner->nbuffers < owner->maxbuffers)
-		return;					/* nothing to do */
-
-	if (owner->buffers == NULL)
-	{
-		newmax = 16;
-		owner->buffers = (Buffer *)
-			MemoryContextAlloc(TopMemoryContext, newmax * sizeof(Buffer));
-		owner->maxbuffers = newmax;
-	}
-	else
-	{
-		newmax = owner->maxbuffers * 2;
-		owner->buffers = (Buffer *)
-			repalloc(owner->buffers, newmax * sizeof(Buffer));
-		owner->maxbuffers = newmax;
-	}
+	if (owner == NULL)
+		return;
+	ResourceArrayEnlarge(&(owner->bufferarr));
 }
 
 /*
@@ -608,12 +795,9 @@ ResourceOwnerEnlargeBuffers(ResourceOwner owner)
 void
 ResourceOwnerRememberBuffer(ResourceOwner owner, Buffer buffer)
 {
-	if (owner != NULL)
-	{
-		Assert(owner->nbuffers < owner->maxbuffers);
-		owner->buffers[owner->nbuffers] = buffer;
-		owner->nbuffers++;
-	}
+	if (owner == NULL)
+		return;
+	ResourceArrayAdd(&(owner->bufferarr), (const void *) &buffer);
 }
 
 /*
@@ -625,33 +809,15 @@ ResourceOwnerRememberBuffer(ResourceOwner owner, Buffer buffer)
 void
 ResourceOwnerForgetBuffer(ResourceOwner owner, Buffer buffer)
 {
-	if (owner != NULL)
-	{
-		Buffer	   *buffers = owner->buffers;
-		int			nb1 = owner->nbuffers - 1;
-		int			i;
+	bool		res;
 
-		/*
-		 * Scan back-to-front because it's more likely we are releasing a
-		 * recently pinned buffer.  This isn't always the case of course, but
-		 * it's the way to bet.
-		 */
-		for (i = nb1; i >= 0; i--)
-		{
-			if (buffers[i] == buffer)
-			{
-				while (i < nb1)
-				{
-					buffers[i] = buffers[i + 1];
-					i++;
-				}
-				owner->nbuffers = nb1;
-				return;
-			}
-		}
+	if (owner == NULL)
+		return;
+
+	res = ResourceArrayRemove(&(owner->bufferarr), (const void *) &buffer);
+	if (!res)
 		elog(ERROR, "buffer %d is not owned by resource owner %s",
 			 buffer, owner->name);
-	}
 }
 
 /*
@@ -667,6 +833,8 @@ ResourceOwnerForgetBuffer(ResourceOwner owner, Buffer buffer)
 void
 ResourceOwnerRememberLock(ResourceOwner owner, LOCALLOCK *locallock)
 {
+	Assert(locallock != NULL);
+
 	if (owner->nlocks > MAX_RESOWNER_LOCKS)
 		return;					/* we have already overflowed */
 
@@ -714,25 +882,7 @@ ResourceOwnerForgetLock(ResourceOwner owner, LOCALLOCK *locallock)
 void
 ResourceOwnerEnlargeCatCacheRefs(ResourceOwner owner)
 {
-	int			newmax;
-
-	if (owner->ncatrefs < owner->maxcatrefs)
-		return;					/* nothing to do */
-
-	if (owner->catrefs == NULL)
-	{
-		newmax = 16;
-		owner->catrefs = (HeapTuple *)
-			MemoryContextAlloc(TopMemoryContext, newmax * sizeof(HeapTuple));
-		owner->maxcatrefs = newmax;
-	}
-	else
-	{
-		newmax = owner->maxcatrefs * 2;
-		owner->catrefs = (HeapTuple *)
-			repalloc(owner->catrefs, newmax * sizeof(HeapTuple));
-		owner->maxcatrefs = newmax;
-	}
+	ResourceArrayEnlarge(&(owner->catrefarr));
 }
 
 /*
@@ -743,9 +893,7 @@ ResourceOwnerEnlargeCatCacheRefs(ResourceOwner owner)
 void
 ResourceOwnerRememberCatCacheRef(ResourceOwner owner, HeapTuple tuple)
 {
-	Assert(owner->ncatrefs < owner->maxcatrefs);
-	owner->catrefs[owner->ncatrefs] = tuple;
-	owner->ncatrefs++;
+	ResourceArrayAdd(&(owner->catrefarr), (const void *) &tuple);
 }
 
 /*
@@ -754,25 +902,12 @@ ResourceOwnerRememberCatCacheRef(ResourceOwner owner, HeapTuple tuple)
 void
 ResourceOwnerForgetCatCacheRef(ResourceOwner owner, HeapTuple tuple)
 {
-	HeapTuple  *catrefs = owner->catrefs;
-	int			nc1 = owner->ncatrefs - 1;
-	int			i;
+	bool		res = ResourceArrayRemove(&(owner->catrefarr),
+										  (const void *) &tuple);
 
-	for (i = nc1; i >= 0; i--)
-	{
-		if (catrefs[i] == tuple)
-		{
-			while (i < nc1)
-			{
-				catrefs[i] = catrefs[i + 1];
-				i++;
-			}
-			owner->ncatrefs = nc1;
-			return;
-		}
-	}
-	elog(ERROR, "catcache reference %p is not owned by resource owner %s",
-		 tuple, owner->name);
+	if (!res)
+		elog(ERROR, "catcache reference %p is not owned by resource owner %s",
+			 tuple, owner->name);
 }
 
 /*
@@ -785,25 +920,7 @@ ResourceOwnerForgetCatCacheRef(ResourceOwner owner, HeapTuple tuple)
 void
 ResourceOwnerEnlargeCatCacheListRefs(ResourceOwner owner)
 {
-	int			newmax;
-
-	if (owner->ncatlistrefs < owner->maxcatlistrefs)
-		return;					/* nothing to do */
-
-	if (owner->catlistrefs == NULL)
-	{
-		newmax = 16;
-		owner->catlistrefs = (CatCList **)
-			MemoryContextAlloc(TopMemoryContext, newmax * sizeof(CatCList *));
-		owner->maxcatlistrefs = newmax;
-	}
-	else
-	{
-		newmax = owner->maxcatlistrefs * 2;
-		owner->catlistrefs = (CatCList **)
-			repalloc(owner->catlistrefs, newmax * sizeof(CatCList *));
-		owner->maxcatlistrefs = newmax;
-	}
+	ResourceArrayEnlarge(&(owner->catlistrefarr));
 }
 
 /*
@@ -814,9 +931,7 @@ ResourceOwnerEnlargeCatCacheListRefs(ResourceOwner owner)
 void
 ResourceOwnerRememberCatCacheListRef(ResourceOwner owner, CatCList *list)
 {
-	Assert(owner->ncatlistrefs < owner->maxcatlistrefs);
-	owner->catlistrefs[owner->ncatlistrefs] = list;
-	owner->ncatlistrefs++;
+	ResourceArrayAdd(&(owner->catlistrefarr), (const void *) &list);
 }
 
 /*
@@ -825,25 +940,12 @@ ResourceOwnerRememberCatCacheListRef(ResourceOwner owner, CatCList *list)
 void
 ResourceOwnerForgetCatCacheListRef(ResourceOwner owner, CatCList *list)
 {
-	CatCList  **catlistrefs = owner->catlistrefs;
-	int			nc1 = owner->ncatlistrefs - 1;
-	int			i;
+	bool		res = ResourceArrayRemove(&(owner->catlistrefarr),
+										  (const void *) &list);
 
-	for (i = nc1; i >= 0; i--)
-	{
-		if (catlistrefs[i] == list)
-		{
-			while (i < nc1)
-			{
-				catlistrefs[i] = catlistrefs[i + 1];
-				i++;
-			}
-			owner->ncatlistrefs = nc1;
-			return;
-		}
-	}
-	elog(ERROR, "catcache list reference %p is not owned by resource owner %s",
-		 list, owner->name);
+	if (!res)
+		elog(ERROR, "catcache list reference %p is not owned by resource owner %s",
+			 list, owner->name);
 }
 
 /*
@@ -856,25 +958,7 @@ ResourceOwnerForgetCatCacheListRef(ResourceOwner owner, CatCList *list)
 void
 ResourceOwnerEnlargeRelationRefs(ResourceOwner owner)
 {
-	int			newmax;
-
-	if (owner->nrelrefs < owner->maxrelrefs)
-		return;					/* nothing to do */
-
-	if (owner->relrefs == NULL)
-	{
-		newmax = 16;
-		owner->relrefs = (Relation *)
-			MemoryContextAlloc(TopMemoryContext, newmax * sizeof(Relation));
-		owner->maxrelrefs = newmax;
-	}
-	else
-	{
-		newmax = owner->maxrelrefs * 2;
-		owner->relrefs = (Relation *)
-			repalloc(owner->relrefs, newmax * sizeof(Relation));
-		owner->maxrelrefs = newmax;
-	}
+	ResourceArrayEnlarge(&(owner->relrefarr));
 }
 
 /*
@@ -885,9 +969,7 @@ ResourceOwnerEnlargeRelationRefs(ResourceOwner owner)
 void
 ResourceOwnerRememberRelationRef(ResourceOwner owner, Relation rel)
 {
-	Assert(owner->nrelrefs < owner->maxrelrefs);
-	owner->relrefs[owner->nrelrefs] = rel;
-	owner->nrelrefs++;
+	ResourceArrayAdd(&(owner->relrefarr), (const void *) &rel);
 }
 
 /*
@@ -896,25 +978,12 @@ ResourceOwnerRememberRelationRef(ResourceOwner owner, Relation rel)
 void
 ResourceOwnerForgetRelationRef(ResourceOwner owner, Relation rel)
 {
-	Relation   *relrefs = owner->relrefs;
-	int			nr1 = owner->nrelrefs - 1;
-	int			i;
+	bool		res = ResourceArrayRemove(&(owner->relrefarr),
+										  (const void *) &rel);
 
-	for (i = nr1; i >= 0; i--)
-	{
-		if (relrefs[i] == rel)
-		{
-			while (i < nr1)
-			{
-				relrefs[i] = relrefs[i + 1];
-				i++;
-			}
-			owner->nrelrefs = nr1;
-			return;
-		}
-	}
-	elog(ERROR, "relcache reference %s is not owned by resource owner %s",
-		 RelationGetRelationName(rel), owner->name);
+	if (!res)
+		elog(ERROR, "relcache reference %s is not owned by resource owner %s",
+			 RelationGetRelationName(rel), owner->name);
 }
 
 /*
@@ -937,25 +1006,7 @@ PrintRelCacheLeakWarning(Relation rel)
 void
 ResourceOwnerEnlargePlanCacheRefs(ResourceOwner owner)
 {
-	int			newmax;
-
-	if (owner->nplanrefs < owner->maxplanrefs)
-		return;					/* nothing to do */
-
-	if (owner->planrefs == NULL)
-	{
-		newmax = 16;
-		owner->planrefs = (CachedPlan **)
-			MemoryContextAlloc(TopMemoryContext, newmax * sizeof(CachedPlan *));
-		owner->maxplanrefs = newmax;
-	}
-	else
-	{
-		newmax = owner->maxplanrefs * 2;
-		owner->planrefs = (CachedPlan **)
-			repalloc(owner->planrefs, newmax * sizeof(CachedPlan *));
-		owner->maxplanrefs = newmax;
-	}
+	ResourceArrayEnlarge(&(owner->planrefarr));
 }
 
 /*
@@ -966,9 +1017,7 @@ ResourceOwnerEnlargePlanCacheRefs(ResourceOwner owner)
 void
 ResourceOwnerRememberPlanCacheRef(ResourceOwner owner, CachedPlan *plan)
 {
-	Assert(owner->nplanrefs < owner->maxplanrefs);
-	owner->planrefs[owner->nplanrefs] = plan;
-	owner->nplanrefs++;
+	ResourceArrayAdd(&(owner->planrefarr), (const void *) &plan);
 }
 
 /*
@@ -977,25 +1026,12 @@ ResourceOwnerRememberPlanCacheRef(ResourceOwner owner, CachedPlan *plan)
 void
 ResourceOwnerForgetPlanCacheRef(ResourceOwner owner, CachedPlan *plan)
 {
-	CachedPlan **planrefs = owner->planrefs;
-	int			np1 = owner->nplanrefs - 1;
-	int			i;
+	bool		res = ResourceArrayRemove(&(owner->planrefarr),
+										  (const void *) &plan);
 
-	for (i = np1; i >= 0; i--)
-	{
-		if (planrefs[i] == plan)
-		{
-			while (i < np1)
-			{
-				planrefs[i] = planrefs[i + 1];
-				i++;
-			}
-			owner->nplanrefs = np1;
-			return;
-		}
-	}
-	elog(ERROR, "plancache reference %p is not owned by resource owner %s",
-		 plan, owner->name);
+	if (!res)
+		elog(ERROR, "plancache reference %p is not owned by resource owner %s",
+			 plan, owner->name);
 }
 
 /*
@@ -1017,25 +1053,7 @@ PrintPlanCacheLeakWarning(CachedPlan *plan)
 void
 ResourceOwnerEnlargeTupleDescs(ResourceOwner owner)
 {
-	int			newmax;
-
-	if (owner->ntupdescs < owner->maxtupdescs)
-		return;					/* nothing to do */
-
-	if (owner->tupdescs == NULL)
-	{
-		newmax = 16;
-		owner->tupdescs = (TupleDesc *)
-			MemoryContextAlloc(TopMemoryContext, newmax * sizeof(TupleDesc));
-		owner->maxtupdescs = newmax;
-	}
-	else
-	{
-		newmax = owner->maxtupdescs * 2;
-		owner->tupdescs = (TupleDesc *)
-			repalloc(owner->tupdescs, newmax * sizeof(TupleDesc));
-		owner->maxtupdescs = newmax;
-	}
+	ResourceArrayEnlarge(&(owner->tupdescarr));
 }
 
 /*
@@ -1046,9 +1064,7 @@ ResourceOwnerEnlargeTupleDescs(ResourceOwner owner)
 void
 ResourceOwnerRememberTupleDesc(ResourceOwner owner, TupleDesc tupdesc)
 {
-	Assert(owner->ntupdescs < owner->maxtupdescs);
-	owner->tupdescs[owner->ntupdescs] = tupdesc;
-	owner->ntupdescs++;
+	ResourceArrayAdd(&(owner->tupdescarr), (const void *) &tupdesc);
 }
 
 /*
@@ -1057,25 +1073,12 @@ ResourceOwnerRememberTupleDesc(ResourceOwner owner, TupleDesc tupdesc)
 void
 ResourceOwnerForgetTupleDesc(ResourceOwner owner, TupleDesc tupdesc)
 {
-	TupleDesc  *tupdescs = owner->tupdescs;
-	int			nt1 = owner->ntupdescs - 1;
-	int			i;
+	bool		res = ResourceArrayRemove(&(owner->tupdescarr),
+										  (const void *) &tupdesc);
 
-	for (i = nt1; i >= 0; i--)
-	{
-		if (tupdescs[i] == tupdesc)
-		{
-			while (i < nt1)
-			{
-				tupdescs[i] = tupdescs[i + 1];
-				i++;
-			}
-			owner->ntupdescs = nt1;
-			return;
-		}
-	}
-	elog(ERROR, "tupdesc reference %p is not owned by resource owner %s",
-		 tupdesc, owner->name);
+	if (!res)
+		elog(ERROR, "tupdesc reference %p is not owned by resource owner %s",
+			 tupdesc, owner->name);
 }
 
 /*
@@ -1099,25 +1102,7 @@ PrintTupleDescLeakWarning(TupleDesc tupdesc)
 void
 ResourceOwnerEnlargeSnapshots(ResourceOwner owner)
 {
-	int			newmax;
-
-	if (owner->nsnapshots < owner->maxsnapshots)
-		return;					/* nothing to do */
-
-	if (owner->snapshots == NULL)
-	{
-		newmax = 16;
-		owner->snapshots = (Snapshot *)
-			MemoryContextAlloc(TopMemoryContext, newmax * sizeof(Snapshot));
-		owner->maxsnapshots = newmax;
-	}
-	else
-	{
-		newmax = owner->maxsnapshots * 2;
-		owner->snapshots = (Snapshot *)
-			repalloc(owner->snapshots, newmax * sizeof(Snapshot));
-		owner->maxsnapshots = newmax;
-	}
+	ResourceArrayEnlarge(&(owner->snapshotarr));
 }
 
 /*
@@ -1128,9 +1113,7 @@ ResourceOwnerEnlargeSnapshots(ResourceOwner owner)
 void
 ResourceOwnerRememberSnapshot(ResourceOwner owner, Snapshot snapshot)
 {
-	Assert(owner->nsnapshots < owner->maxsnapshots);
-	owner->snapshots[owner->nsnapshots] = snapshot;
-	owner->nsnapshots++;
+	ResourceArrayAdd(&(owner->snapshotarr), (const void *) &snapshot);
 }
 
 /*
@@ -1139,25 +1122,12 @@ ResourceOwnerRememberSnapshot(ResourceOwner owner, Snapshot snapshot)
 void
 ResourceOwnerForgetSnapshot(ResourceOwner owner, Snapshot snapshot)
 {
-	Snapshot   *snapshots = owner->snapshots;
-	int			ns1 = owner->nsnapshots - 1;
-	int			i;
+	bool		res = ResourceArrayRemove(&(owner->snapshotarr),
+										  (const void *) &snapshot);
 
-	for (i = ns1; i >= 0; i--)
-	{
-		if (snapshots[i] == snapshot)
-		{
-			while (i < ns1)
-			{
-				snapshots[i] = snapshots[i + 1];
-				i++;
-			}
-			owner->nsnapshots = ns1;
-			return;
-		}
-	}
-	elog(ERROR, "snapshot reference %p is not owned by resource owner %s",
-		 snapshot, owner->name);
+	if (!res)
+		elog(ERROR, "snapshot reference %p is not owned by resource owner %s",
+			 snapshot, owner->name);
 }
 
 /*
@@ -1182,25 +1152,7 @@ PrintSnapshotLeakWarning(Snapshot snapshot)
 void
 ResourceOwnerEnlargeFiles(ResourceOwner owner)
 {
-	int			newmax;
-
-	if (owner->nfiles < owner->maxfiles)
-		return;					/* nothing to do */
-
-	if (owner->files == NULL)
-	{
-		newmax = 16;
-		owner->files = (File *)
-			MemoryContextAlloc(TopMemoryContext, newmax * sizeof(File));
-		owner->maxfiles = newmax;
-	}
-	else
-	{
-		newmax = owner->maxfiles * 2;
-		owner->files = (File *)
-			repalloc(owner->files, newmax * sizeof(File));
-		owner->maxfiles = newmax;
-	}
+	ResourceArrayEnlarge(&(owner->filearr));
 }
 
 /*
@@ -1211,9 +1163,7 @@ ResourceOwnerEnlargeFiles(ResourceOwner owner)
 void
 ResourceOwnerRememberFile(ResourceOwner owner, File file)
 {
-	Assert(owner->nfiles < owner->maxfiles);
-	owner->files[owner->nfiles] = file;
-	owner->nfiles++;
+	ResourceArrayAdd(&(owner->filearr), (const void *) &file);
 }
 
 /*
@@ -1222,25 +1172,12 @@ ResourceOwnerRememberFile(ResourceOwner owner, File file)
 void
 ResourceOwnerForgetFile(ResourceOwner owner, File file)
 {
-	File	   *files = owner->files;
-	int			ns1 = owner->nfiles - 1;
-	int			i;
+	bool		res = ResourceArrayRemove(&(owner->filearr),
+										  (const void *) &file);
 
-	for (i = ns1; i >= 0; i--)
-	{
-		if (files[i] == file)
-		{
-			while (i < ns1)
-			{
-				files[i] = files[i + 1];
-				i++;
-			}
-			owner->nfiles = ns1;
-			return;
-		}
-	}
-	elog(ERROR, "temporery file %d is not owned by resource owner %s",
-		 file, owner->name);
+	if (!res)
+		elog(ERROR, "temporery file %d is not owned by resource owner %s",
+			 file, owner->name);
 }
 
 
@@ -1265,26 +1202,7 @@ PrintFileLeakWarning(File file)
 void
 ResourceOwnerEnlargeDSMs(ResourceOwner owner)
 {
-	int			newmax;
-
-	if (owner->ndsms < owner->maxdsms)
-		return;					/* nothing to do */
-
-	if (owner->dsms == NULL)
-	{
-		newmax = 16;
-		owner->dsms = (dsm_segment **)
-			MemoryContextAlloc(TopMemoryContext,
-							   newmax * sizeof(dsm_segment *));
-		owner->maxdsms = newmax;
-	}
-	else
-	{
-		newmax = owner->maxdsms * 2;
-		owner->dsms = (dsm_segment **)
-			repalloc(owner->dsms, newmax * sizeof(dsm_segment *));
-		owner->maxdsms = newmax;
-	}
+	ResourceArrayEnlarge(&(owner->dsmarr));
 }
 
 /*
@@ -1295,9 +1213,7 @@ ResourceOwnerEnlargeDSMs(ResourceOwner owner)
 void
 ResourceOwnerRememberDSM(ResourceOwner owner, dsm_segment *seg)
 {
-	Assert(owner->ndsms < owner->maxdsms);
-	owner->dsms[owner->ndsms] = seg;
-	owner->ndsms++;
+	ResourceArrayAdd(&(owner->dsmarr), (const void *) &seg);
 }
 
 /*
@@ -1306,26 +1222,12 @@ ResourceOwnerRememberDSM(ResourceOwner owner, dsm_segment *seg)
 void
 ResourceOwnerForgetDSM(ResourceOwner owner, dsm_segment *seg)
 {
-	dsm_segment **dsms = owner->dsms;
-	int			ns1 = owner->ndsms - 1;
-	int			i;
+	bool		res = ResourceArrayRemove(&(owner->dsmarr),
+										  (const void *) &seg);
 
-	for (i = ns1; i >= 0; i--)
-	{
-		if (dsms[i] == seg)
-		{
-			while (i < ns1)
-			{
-				dsms[i] = dsms[i + 1];
-				i++;
-			}
-			owner->ndsms = ns1;
-			return;
-		}
-	}
-	elog(ERROR,
-		 "dynamic shared memory segment %u is not owned by resource owner %s",
-		 dsm_segment_handle(seg), owner->name);
+	if (!res)
+		elog(ERROR, "dynamic shared memory segment %u is not owned by resource"
+			 " owner %s", dsm_segment_handle(seg), owner->name);
 }
 
 
resource-owner-optimization-v3-step2.patchtext/x-patchDownload
diff --git a/src/backend/utils/resowner/resowner.c b/src/backend/utils/resowner/resowner.c
index 39324fe..d2b37d5 100644
--- a/src/backend/utils/resowner/resowner.c
+++ b/src/backend/utils/resowner/resowner.c
@@ -37,6 +37,27 @@
  * which should be called before corresponding ResourceOwnerRemember* calls
  * (see below). Internally each type of resource is stored in separate
  * ResourceArray.
+ *
+ * There are two major reasons for using ResourceArray instead of, say,
+ * regular C arrays.
+ *
+ * Firstly we would like to prevent code duplication. For instance
+ * ResourceArray provides generic Remember/Forget/Enlarge procedures, so
+ * corresponding ResourceOwner* procedures are just a typesafe wrappers for
+ * these procedures. Note that different resources have different sizeof. Thus
+ * ResourceArray should know size of resource and store all data in char[]
+ * buffer.
+ *
+ * Secondly ResourceArray must be more efficient than regular C array.
+ * Previous implementation of ResourceOwner used arrays. It had O(1) complexity
+ * of Remember procedures and O(N) complexity of Forget procedures where N is a
+ * number of remembered resources. It turned out that such implementation
+ * creates a bottleneck in some cases, e.g. when working with partitioned
+ * tables which have a lot of (say, thousands) partitions. New implementation
+ * in general could be considered a hash table with some specific optimizations.
+ * It has O(1) complexity of both Remember and Forget procedures and
+ * apparently doesn't cause any performance degradation compared to previous
+ * implementation.
  */
 typedef struct ResourceArray
 {
@@ -44,21 +65,60 @@ typedef struct ResourceArray
 	uint32		itemsizelg:2;	/* sizeof one item log 2 */
 	uint32		capacity:30;	/* capacity of array */
 	uint32		nitems;			/* how many items is stored in items array */
+	uint32		maxitems;		/* precalculated RESARRAY_MAX_ITEMS(capacity) */
 }	ResourceArray;
 
 /*
  * This number is used as initial size of resource array. If given number of
  * items is not enough, we double array size and reallocate memory.
+ *
+ * Should be power of two since we use (arrsize -1) as mask for hash value.
+ *
  */
 #define RESARRAY_INIT_SIZE 16
 
 /*
+ * How many items could be stored in a resource array of given capacity. If
+ * this number is reached we need to resize an array to prevent hash collisions.
+ *
+ * This computation actually costs only two additions and one binary shift.
+ */
+#define RESARRAY_MAX_ITEMS(capacity) ((capacity)*3/4)
+
+/*
  * Such type of callback function is called when resource stored in
  * ResourceArray is released using ResourceArrayFree. Callback should
  * _actually_ release a resource so nitems value will be decremented.
  */
 typedef void (*ResourceArrayRemoveCallback) (const void *ref, bool isCommit);
 
+/* Used as argument to memcmp to determine if ResourceArray[i] is free. */
+static const char RESOURCE_ARRAY_ZERO_ELEMENT[sizeof(void *)] =
+{
+	0
+};
+
+/*
+ * Calculate hash_any of given data. For uint32 values use faster hash_uint32.
+ */
+static Datum
+ResourceArrayHash(const void *data, int size)
+{
+	uint32		tmp;
+
+	Assert(size == sizeof(uint32) || size == sizeof(void *));
+
+	if (size == sizeof(uint32))
+	{
+		tmp = *((const uint32 *) data);
+		return hash_uint32(tmp);
+	}
+	else
+	{
+		return hash_any(data, size);
+	}
+}
+
 /*
  * Initialize ResourceArray
  */
@@ -69,6 +129,7 @@ ResourceArrayInit(ResourceArray * resarr, uint32 itemsize)
 	Assert(resarr->itemsarr == NULL);
 	Assert(resarr->capacity == 0);
 	Assert(resarr->nitems == 0);
+	Assert(resarr->maxitems == 0);
 
 	resarr->itemsizelg = 0;
 	while (itemsize > 1)
@@ -89,22 +150,42 @@ static void
 ResourceArrayAdd(ResourceArray * resarr, const void *dataref)
 {
 	char	   *itemptr;
+	Datum		idx;
+	Datum		mask = resarr->capacity - 1;
 	uint32		itemsize = 1 << resarr->itemsizelg;
 
+	Assert(memcmp(dataref, RESOURCE_ARRAY_ZERO_ELEMENT, itemsize) != 0);
+	Assert(resarr->maxitems > resarr->nitems);
 	Assert(resarr->capacity > 0);
 	Assert(resarr->itemsarr != NULL);
-	Assert(resarr->nitems < resarr->capacity);
 
 	/*
-	 * Read next line as:
-	 *
-	 * itemptr = &(itemsarr[resarr->nitems])
-	 *
-	 * We use binary shift since compiler doesn't know that itemsize is always
-	 * power of two. It would use multiplication instead of efficient binary
-	 * shift in code `resarr->nitems * itemsize`.
+	 * Hashing is quite expensive, so we use it only for large arrays. For
+	 * small arrays we just use a linear scan.
 	 */
-	itemptr = resarr->itemsarr + (resarr->nitems << resarr->itemsizelg);
+	if (resarr->capacity == RESARRAY_INIT_SIZE)
+		idx = resarr->nitems;
+	else
+		idx = ResourceArrayHash(dataref, itemsize);
+	idx &= mask;
+
+	while (true)
+	{
+		/*
+		 * Read next line as:
+		 *
+		 * itemptr = &(itemsarr[idx])
+		 *
+		 * We use binary shift since compiler doesn't know that itemsize is
+		 * always power of two. It would use multiplication instead of
+		 * efficient binary shift in code `idx * itemsize`.
+		 */
+		itemptr = resarr->itemsarr + (idx << resarr->itemsizelg);
+		if (memcmp(itemptr, RESOURCE_ARRAY_ZERO_ELEMENT, itemsize) == 0)
+			break;
+		idx = (idx + 1) & mask;
+	}
+
 	memcpy(itemptr, dataref, itemsize);
 	resarr->nitems++;
 }
@@ -117,17 +198,27 @@ ResourceArrayAdd(ResourceArray * resarr, const void *dataref)
 static bool
 ResourceArrayRemove(ResourceArray * resarr, const void *dataref)
 {
-	Datum		idx;
 	char	   *itemptr;
-	char	   *lastitemptr;
+	Datum		idx;
+	uint32		i;
+	Datum		mask = resarr->capacity - 1;
 	uint32		itemsize = 1 << resarr->itemsizelg;
 
+	Assert(memcmp(dataref, RESOURCE_ARRAY_ZERO_ELEMENT, itemsize) != 0);
 	Assert(resarr->capacity > 0);
 	Assert(resarr->itemsarr != NULL);
 
-	lastitemptr = resarr->itemsarr +
-		((resarr->nitems - 1) << resarr->itemsizelg);
-	for (idx = 0; idx < resarr->nitems; idx++)
+	/*
+	 * Hashing is quite expensive, so we use it only for large arrays. For
+	 * small arrays we use a linear scan.
+	 */
+	if (resarr->capacity == RESARRAY_INIT_SIZE)
+		idx = 0;
+	else
+		idx = ResourceArrayHash(dataref, itemsize);
+	idx &= mask;
+
+	for (i = 0; i < resarr->capacity; i++)
 	{
 		/*
 		 * Read next line as:
@@ -141,10 +232,11 @@ ResourceArrayRemove(ResourceArray * resarr, const void *dataref)
 		itemptr = resarr->itemsarr + (idx << resarr->itemsizelg);
 		if (memcmp(itemptr, dataref, itemsize) == 0)
 		{
-			memcpy(itemptr, lastitemptr, itemsize);
+			memset(itemptr, 0, itemsize);
 			resarr->nitems--;
 			return true;
 		}
+		idx = (idx + 1) & mask;
 	}
 
 	return false;
@@ -166,7 +258,7 @@ ResourceArrayEnlarge(ResourceArray * resarr)
 
 	Assert(resarr->itemsizelg != 0);
 
-	if (resarr->nitems < resarr->capacity)
+	if (resarr->nitems < resarr->maxitems)
 		return;					/* nothing to do */
 
 	olditemsarr = resarr->itemsarr;
@@ -176,6 +268,7 @@ ResourceArrayEnlarge(ResourceArray * resarr)
 	resarr->itemsarr = (char *)
 		MemoryContextAllocZero(TopMemoryContext,
 							   resarr->capacity * itemsize);
+	resarr->maxitems = RESARRAY_MAX_ITEMS(resarr->capacity);
 	resarr->nitems = 0;
 
 	if (olditemsarr != NULL)
@@ -194,7 +287,8 @@ ResourceArrayEnlarge(ResourceArray * resarr)
 			 * efficient binary shift in code `oldcap * itemsize`.
 			 */
 			olditemptr = olditemsarr + (oldcap << resarr->itemsizelg);
-			ResourceArrayAdd(resarr, olditemptr);
+			if (memcmp(olditemptr, RESOURCE_ARRAY_ZERO_ELEMENT, itemsize) != 0)
+				ResourceArrayAdd(resarr, olditemptr);
 		}
 		pfree(olditemsarr);
 	}
@@ -208,9 +302,28 @@ ResourceArrayRemoveAll(ResourceArray * resarr,
 					   ResourceArrayRemoveCallback releasecb,
 					   bool isCommit)
 {
+	uint32		idx = 0;
+	char	   *itemptr;
+	uint32		itemsize = 1 << resarr->itemsizelg;
+
 	while (resarr->nitems > 0)
 	{
-		releasecb(resarr->itemsarr, isCommit);
+		/*
+		 * Read next line as:
+		 *
+		 * itemptr = &(itemsarr[idx])
+		 *
+		 * We use binary shift since compiler doesn't know that itemsize is
+		 * always power of two. It would use multiplication instead of
+		 * efficient binary shift in code `idx * itemsize`.
+		 */
+		itemptr = resarr->itemsarr + (idx << resarr->itemsizelg);
+		if (memcmp(itemptr, RESOURCE_ARRAY_ZERO_ELEMENT, itemsize) == 0)
+		{
+			idx++;
+			continue;
+		}
+		releasecb(itemptr, isCommit);
 	}
 }
 
@@ -223,6 +336,7 @@ ResourceArrayFree(ResourceArray * resarr)
 	Assert(resarr->nitems == 0);
 
 	resarr->capacity = 0;
+	resarr->maxitems = 0;
 
 	if (!resarr->itemsarr)
 		return;
#10Robert Haas
robertmhaas@gmail.com
In reply to: Aleksander Alekseev (#9)
Re: Patch: ResourceOwner optimization for tables with many partitions

On Mon, Dec 14, 2015 at 6:47 AM, Aleksander Alekseev
<a.alekseev@postgrespro.ru> wrote:

Here is my fix for item 4.

I don't know, I'm still not very comfortable with this. And Tom
didn't like dictating that hash_any() must be no-fail, though I'm not
sure why.

Let's wait to see what others think. I kind of hope there's a way of
getting the benefits we want here without so much code churn.

--
Robert Haas
EnterpriseDB: http://www.enterprisedb.com
The Enterprise PostgreSQL Company

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

#11Tom Lane
tgl@sss.pgh.pa.us
In reply to: Robert Haas (#10)
Re: Patch: ResourceOwner optimization for tables with many partitions

Robert Haas <robertmhaas@gmail.com> writes:

I don't know, I'm still not very comfortable with this. And Tom
didn't like dictating that hash_any() must be no-fail, though I'm not
sure why.

What I definitely didn't like was assuming at a distance that it would
be no-fail. If we're to depend on that, the patch had better attach
a comment saying so to the header comments of the function(s) it's
assuming that about. Otherwise, somebody could hack up hashfunc.c
in a way that breaks the assumption, without any clue that some code
in a very-far-away module is critically reliant on it.

Let's wait to see what others think.

A few observations:

* This bit is too cute by half, if not three-quarters:

+	uint32		itemsizelg:2;	/* sizeof one item log 2 */
+	uint32		capacity:30;	/* capacity of array */

Is there a good reason to assume that the only things we'll ever store
in these arrays are of size no more than 8 bytes? Are we so desperate
to save space that we cannot spare two separate words for itemsize and
capacity? (ISTM it's a good bet that the extra code for accessing these
bitfields occupies more space than would be saved, considering how few
ResourceOwners typically exist at one time.) Let's just make it a couple
of ints and be done. Actually, maybe nitems and capacity should be
size_t, just in case.

* An alternative design would be to forget itemsizelg altogether and insist
that everything stored in the resource arrays be a Datum, which could then
be coerced to/from some form of integer or some form of pointer as
appropriate. That would waste some space in the int case, but it would
considerably simplify both the ResourceArray code and the APIs to it,
which might be worth the price of assuming we'll never store anything
bigger than 8 bytes. It also would make this look more like some
existing APIs such as the on_exit callbacks.

* A lot of the code churn comes from the insistence on defining callbacks,
which I'm dubious that we need. We could instead have a function that is
"get any convenient one of the array elements" and revise the loops in
ResourceOwnerReleaseInternal to be like

while ((item = getconvenientitem(resourcearray)))
{
drop item in exactly the same way as before
}

I find that preferable to the proposed ResourceArrayRemoveAll

+	while (resarr->nitems > 0)
+	{
+		releasecb(resarr->itemsarr, isCommit);
+	}

which certainly looks like it's an infinite loop; it's assuming (again
with no documentation) that the callback function will cause the array
to get smaller somehow. With the existing coding, it's much more clear
why we think the loops will terminate.

* The reason that ResourceOwnerReleaseInternal was not horribly
inefficient was that its notion of "any convenient one" of the items
to be deleted next was in fact the one that the corresponding Forget
function would examine first, thus avoiding an O(N^2) cost to
re-identify the item to be dropped. I think we should make an effort
to be more explicit about that connection in any rewrite. In particular,
it looks to me like when a hash array is in use, things will get slower
not faster because we'll be adding a hash lookup step to each forget
operation. Maybe we should consider adjusting the APIs so that that
can be avoided. Or possibly we could have internal state in the
ResourceArrays that says "we expect this item to be dropped in a moment,
check that before going to the trouble of a hash lookup".

* Actually, I'm not convinced that the proposed reimplementation of
ResourceArrayRemove isn't horribly slow much of the time. It sure
looks like it could degrade to a linear search very easily.

* I still say that the assumption embodied as RESOURCE_ARRAY_ZERO_ELEMENT
(ie that no valid entry is all-zero-bits) is pretty unacceptable. It
might work for pointers, but I don't like it for resources represented
by integer indexes.

regards, tom lane

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

#12Aleksander Alekseev
a.alekseev@postgrespro.ru
In reply to: Tom Lane (#11)
2 attachment(s)
Re: Patch: ResourceOwner optimization for tables with many partitions

I believe I fixed all flaws mentioned so far (see attachment).

Also I did a new benchmark to make sure that new patch makes PostgreSQL
work faster and doesn't cause performance degradation in some cases.

"usual pgbench" row corresponds to `pgbench -j 8 -c 8 -T 30 pgbench`
performed on a 4-core PC.

"N partitions" rows correspond to a benchmark described in a first
message of this thread performed on the same PC. N is and argument
given to gen.pl script i.e. number of partitions in generated
partitioned table.

Here are results (3 tests, TPS excluding connections establishing):

Test | Before | After | Delta avg
----------------|-----------|-----------|-------------
| 295.7 | 295.0 |
usual pgbench | 303.1 | 299.6 | ~ 0%
| 297.7 | 302.7 |
----------------|-----------|-----------|-------------
| 28022.3 | 27956.1 |
10 partitions | 27550.1 | 28916.9 | ~ 0%
| 28617.0 | 28022.9 |
----------------|-----------|-----------|-------------
| 3021.4 | 3184.0 |
100 partitions | 2949.1 | 3120.1 | 3% more TPS
| 2870.6 | 2825.2 |
----------------|-----------|-----------|-------------
| 106.7 | 158.6 |
1000 partitions | 105.2 | 168.4 | 53% more TPS
| 105.9 | 162.0 |

On Fri, 18 Dec 2015 13:39:01 -0500
Tom Lane <tgl@sss.pgh.pa.us> wrote:

Show quoted text

Robert Haas <robertmhaas@gmail.com> writes:

I don't know, I'm still not very comfortable with this. And Tom
didn't like dictating that hash_any() must be no-fail, though I'm
not sure why.

What I definitely didn't like was assuming at a distance that it would
be no-fail. If we're to depend on that, the patch had better attach
a comment saying so to the header comments of the function(s) it's
assuming that about. Otherwise, somebody could hack up hashfunc.c
in a way that breaks the assumption, without any clue that some code
in a very-far-away module is critically reliant on it.

Let's wait to see what others think.

A few observations:

* This bit is too cute by half, if not three-quarters:

+ uint32 itemsizelg:2; /* sizeof one
item log 2 */
+ uint32 capacity:30; /* capacity of
array */

Is there a good reason to assume that the only things we'll ever store
in these arrays are of size no more than 8 bytes? Are we so desperate
to save space that we cannot spare two separate words for itemsize and
capacity? (ISTM it's a good bet that the extra code for accessing
these bitfields occupies more space than would be saved, considering
how few ResourceOwners typically exist at one time.) Let's just make
it a couple of ints and be done. Actually, maybe nitems and capacity
should be size_t, just in case.

* An alternative design would be to forget itemsizelg altogether and
insist that everything stored in the resource arrays be a Datum,
which could then be coerced to/from some form of integer or some form
of pointer as appropriate. That would waste some space in the int
case, but it would considerably simplify both the ResourceArray code
and the APIs to it, which might be worth the price of assuming we'll
never store anything bigger than 8 bytes. It also would make this
look more like some existing APIs such as the on_exit callbacks.

* A lot of the code churn comes from the insistence on defining
callbacks, which I'm dubious that we need. We could instead have a
function that is "get any convenient one of the array elements" and
revise the loops in ResourceOwnerReleaseInternal to be like

while ((item = getconvenientitem(resourcearray)))
{
drop item in exactly the same way as before
}

I find that preferable to the proposed ResourceArrayRemoveAll

+	while (resarr->nitems > 0)
+	{
+		releasecb(resarr->itemsarr, isCommit);
+	}

which certainly looks like it's an infinite loop; it's assuming (again
with no documentation) that the callback function will cause the array
to get smaller somehow. With the existing coding, it's much more
clear why we think the loops will terminate.

* The reason that ResourceOwnerReleaseInternal was not horribly
inefficient was that its notion of "any convenient one" of the items
to be deleted next was in fact the one that the corresponding Forget
function would examine first, thus avoiding an O(N^2) cost to
re-identify the item to be dropped. I think we should make an effort
to be more explicit about that connection in any rewrite. In
particular, it looks to me like when a hash array is in use, things
will get slower not faster because we'll be adding a hash lookup step
to each forget operation. Maybe we should consider adjusting the
APIs so that that can be avoided. Or possibly we could have internal
state in the ResourceArrays that says "we expect this item to be
dropped in a moment, check that before going to the trouble of a hash
lookup".

* Actually, I'm not convinced that the proposed reimplementation of
ResourceArrayRemove isn't horribly slow much of the time. It sure
looks like it could degrade to a linear search very easily.

* I still say that the assumption embodied as
RESOURCE_ARRAY_ZERO_ELEMENT (ie that no valid entry is all-zero-bits)
is pretty unacceptable. It might work for pointers, but I don't like
it for resources represented by integer indexes.

regards, tom lane

Attachments:

resource-owner-optimization-v4-step1.patchtext/x-patchDownload
diff --git a/src/backend/access/hash/hashfunc.c b/src/backend/access/hash/hashfunc.c
index 9ee654e..5a00a03 100644
--- a/src/backend/access/hash/hashfunc.c
+++ b/src/backend/access/hash/hashfunc.c
@@ -297,6 +297,9 @@ hashvarlena(PG_FUNCTION_ARGS)
  * of 2.  There is no need to do mod a prime (mod is sooo slow!).
  * If you need less than 32 bits, use a bitmask.
  *
+ * This procedure never fails. Its important. Some code notably ResourceOwner
+ * relies on this.
+ *
  * Note: we could easily change this function to return a 64-bit hash value
  * by using the final values of both b and c.  b is perhaps a little less
  * well mixed than c, however.
diff --git a/src/backend/utils/resowner/resowner.c b/src/backend/utils/resowner/resowner.c
index 0e7acbf..abed36c 100644
--- a/src/backend/utils/resowner/resowner.c
+++ b/src/backend/utils/resowner/resowner.c
@@ -29,6 +29,219 @@
 #include "utils/snapmgr.h"
 
 /*
+ * ResourceArray is a common structure for storing different types of resources.
+ *
+ * ResourceOwner can own `HeapTuple`s, `Relation`s, `Snapshot`s, etc. For
+ * each resource type there are procedures ResourceOwnerRemember* and
+ * ResourceOwnerForget*. There are also ResourceOwnerEnlarge* procedures
+ * which should be called before corresponding ResourceOwnerRemember* calls
+ * (see below). Internally each type of resource is stored in separate
+ * ResourceArray.
+ *
+ * There are two major reasons for using ResourceArray instead of, say,
+ * regular C arrays.
+ *
+ * Firstly we would like to prevent code duplication. For instance
+ * ResourceArray provides generic Remember/Forget/Enlarge procedures, so
+ * corresponding ResourceOwner* procedures are just a typesafe wrappers for
+ * these procedures.
+ *
+ * Secondly ResourceArray must be more efficient than regular C array.
+ * Current implementation in general could be considered a hash table. It has
+ * O(1) complexity of both Remember and Forget procedures.
+ */
+typedef struct ResourceArray
+{
+	Datum	   *itemsarr;		/* buffer for storing values */
+	Datum		invalidval;		/* value that is considered invalid */
+	uint32		capacity;		/* capacity of array */
+	uint32		nitems;			/* how many items is stored in items array */
+	uint32		maxitems;		/* precalculated RESARRAY_MAX_ITEMS(capacity) */
+	uint32		lastidx;		/* index of last item returned by GetAny */
+}	ResourceArray;
+
+/*
+ * This number is used as initial size of resource array. If given number of
+ * items is not enough, we double array size and reallocate memory.
+ *
+ * Should be power of two since we use (arrsize - 1) as mask for hash value.
+ *
+ */
+#define RESARRAY_INIT_SIZE 16
+
+/*
+ * How many items could be stored in a resource array of given capacity. If
+ * this number is reached we need to resize an array to prevent hash collisions.
+ *
+ * This computation actually costs only two additions and one binary shift.
+ */
+#define RESARRAY_MAX_ITEMS(capacity) ((capacity)*3/4)
+
+/*
+ * Initialize ResourceArray
+ */
+static void
+ResourceArrayInit(ResourceArray * resarr, Datum invalidval)
+{
+	Assert(resarr->itemsarr == NULL);
+	Assert(resarr->capacity == 0);
+	Assert(resarr->nitems == 0);
+	Assert(resarr->maxitems == 0);
+	Assert(resarr->invalidval == 0);
+	Assert(resarr->lastidx == 0);
+
+	resarr->invalidval = invalidval;
+}
+
+/*
+ * Add a resource to ResourceArray
+ *
+ * Caller must have previously done ResourceArrayEnlarge()
+ */
+static void
+ResourceArrayAdd(ResourceArray * resarr, Datum data)
+{
+	Datum		idx;
+	Datum		mask = resarr->capacity - 1;
+
+	Assert(resarr->maxitems > resarr->nitems);
+	Assert(resarr->capacity > 0);
+	Assert(resarr->itemsarr != NULL);
+	Assert(data != resarr->invalidval);
+
+	idx = hash_any((void *) &data, sizeof(data)) & mask;
+
+	while (true)
+	{
+		if (resarr->itemsarr[idx] == resarr->invalidval)
+			break;
+		idx = (idx + 1) & mask;
+	}
+
+	resarr->itemsarr[idx] = data;
+	resarr->nitems++;
+}
+
+/*
+ * Remove a resource from ResourceArray
+ *
+ * Returns true on success, false if resource was not found
+ */
+static bool
+ResourceArrayRemove(ResourceArray * resarr, Datum data)
+{
+	uint32		i;
+	Datum		idx;
+	Datum		mask = resarr->capacity - 1;
+
+	Assert(resarr->capacity > 0);
+	Assert(resarr->itemsarr != NULL);
+	Assert(data != resarr->invalidval);
+
+	idx = hash_any((void *) &data, sizeof(data)) & mask;
+	for (i = 0; i < resarr->capacity; i++)
+	{
+		if (resarr->itemsarr[idx] == data)
+		{
+			resarr->itemsarr[idx] = resarr->invalidval;
+			resarr->nitems--;
+			return true;
+		}
+		idx = (idx + 1) & mask;
+	}
+
+	return false;
+}
+
+/*
+ * Make sure there is a room for at least one more resource in an array.
+ *
+ * This is separate from actually inserting a resource because if we run out
+ * of memory, it's critical to do so *before* acquiring the resource.
+ */
+static void
+ResourceArrayEnlarge(ResourceArray * resarr)
+{
+	uint32		i,
+				oldcap;
+	Datum	   *olditemsarr;
+
+	if (resarr->nitems < resarr->maxitems)
+		return;					/* nothing to do */
+
+	olditemsarr = resarr->itemsarr;
+	oldcap = resarr->capacity;
+
+	resarr->capacity = oldcap > 0 ? oldcap * 2 : RESARRAY_INIT_SIZE;
+	resarr->itemsarr = (Datum *)
+		MemoryContextAlloc(TopMemoryContext,
+						   resarr->capacity * sizeof(Datum));
+	resarr->maxitems = RESARRAY_MAX_ITEMS(resarr->capacity);
+	resarr->nitems = 0;
+
+	for (i = 0; i < resarr->capacity; i++)
+		resarr->itemsarr[i] = resarr->invalidval;
+
+	if (olditemsarr != NULL)
+	{
+		while (oldcap > 0)
+		{
+			oldcap--;
+			if (olditemsarr[oldcap] != resarr->invalidval)
+				ResourceArrayAdd(resarr, olditemsarr[oldcap]);
+		}
+		pfree(olditemsarr);
+	}
+}
+
+/*
+ * Get any convenient element.
+ *
+ * Returns true on success, false on failure.
+ */
+static bool
+ResourceArrayGetAny(ResourceArray * resarr, Datum *out)
+{
+	uint32		mask;
+
+	if (resarr->nitems == 0)
+		return false;
+
+	Assert(resarr->capacity > 0);
+	mask = resarr->capacity - 1;
+
+	for (;;)
+	{
+		resarr->lastidx = resarr->lastidx & mask;
+		if (resarr->itemsarr[resarr->lastidx] != resarr->invalidval)
+			break;
+
+		resarr->lastidx++;
+	}
+
+	*out = resarr->itemsarr[resarr->lastidx];
+	return true;
+}
+
+/*
+ * Return ResourceArray to initial state
+ */
+static void
+ResourceArrayFree(ResourceArray * resarr)
+{
+	Assert(resarr->nitems == 0);
+
+	resarr->capacity = 0;
+	resarr->maxitems = 0;
+
+	if (!resarr->itemsarr)
+		return;
+
+	pfree(resarr->itemsarr);
+	resarr->itemsarr = NULL;
+}
+
+/*
  * To speed up bulk releasing or reassigning locks from a resource owner to
  * its parent, each resource owner has a small cache of locks it owns. The
  * lock manager has the same information in its local lock hash table, and
@@ -56,53 +269,22 @@ typedef struct ResourceOwnerData
 	ResourceOwner nextchild;	/* next child of same parent */
 	const char *name;			/* name (just for debugging) */
 
-	/* We have built-in support for remembering owned buffers */
-	int			nbuffers;		/* number of owned buffer pins */
-	Buffer	   *buffers;		/* dynamically allocated array */
-	int			maxbuffers;		/* currently allocated array size */
-
 	/* We can remember up to MAX_RESOWNER_LOCKS references to local locks. */
 	int			nlocks;			/* number of owned locks */
 	LOCALLOCK  *locks[MAX_RESOWNER_LOCKS];		/* list of owned locks */
 
-	/* We have built-in support for remembering catcache references */
-	int			ncatrefs;		/* number of owned catcache pins */
-	HeapTuple  *catrefs;		/* dynamically allocated array */
-	int			maxcatrefs;		/* currently allocated array size */
-
-	int			ncatlistrefs;	/* number of owned catcache-list pins */
-	CatCList  **catlistrefs;	/* dynamically allocated array */
-	int			maxcatlistrefs; /* currently allocated array size */
-
-	/* We have built-in support for remembering relcache references */
-	int			nrelrefs;		/* number of owned relcache pins */
-	Relation   *relrefs;		/* dynamically allocated array */
-	int			maxrelrefs;		/* currently allocated array size */
-
-	/* We have built-in support for remembering plancache references */
-	int			nplanrefs;		/* number of owned plancache pins */
-	CachedPlan **planrefs;		/* dynamically allocated array */
-	int			maxplanrefs;	/* currently allocated array size */
-
-	/* We have built-in support for remembering tupdesc references */
-	int			ntupdescs;		/* number of owned tupdesc references */
-	TupleDesc  *tupdescs;		/* dynamically allocated array */
-	int			maxtupdescs;	/* currently allocated array size */
-
-	/* We have built-in support for remembering snapshot references */
-	int			nsnapshots;		/* number of owned snapshot references */
-	Snapshot   *snapshots;		/* dynamically allocated array */
-	int			maxsnapshots;	/* currently allocated array size */
-
-	/* We have built-in support for remembering open temporary files */
-	int			nfiles;			/* number of owned temporary files */
-	File	   *files;			/* dynamically allocated array */
-	int			maxfiles;		/* currently allocated array size */
-
-	/* We have built-in support for remembering dynamic shmem segments */
-	int			ndsms;			/* number of owned shmem segments */
-	dsm_segment **dsms;			/* dynamically allocated array */
-	int			maxdsms;		/* currently allocated array size */
+	/* We have built-in support for remembering: */
+
+	ResourceArray catrefarr;	/* `HeapTuple`s */
+	ResourceArray catlistrefarr;	/* `ResourceOwner`s */
+	ResourceArray relrefarr;	/* `Relation`s */
+	ResourceArray planrefarr;	/* `CachedPlan*`s */
+	ResourceArray tupdescarr;	/* `TupleDesc`s */
+	ResourceArray snapshotarr;	/* `Snapshot`s */
+	ResourceArray dsmarr;		/* `dsm_segment*`s */
+	ResourceArray bufferarr;	/* `Buffer`s  */
+	ResourceArray filearr;		/* `File`s */
+
 }	ResourceOwnerData;
 
 
@@ -168,6 +350,16 @@ ResourceOwnerCreate(ResourceOwner parent, const char *name)
 		parent->firstchild = owner;
 	}
 
+	ResourceArrayInit(&(owner->catrefarr), (Datum) (NULL));
+	ResourceArrayInit(&(owner->catlistrefarr), (Datum) (NULL));
+	ResourceArrayInit(&(owner->relrefarr), (Datum) (NULL));
+	ResourceArrayInit(&(owner->planrefarr), (Datum) (NULL));
+	ResourceArrayInit(&(owner->tupdescarr), (Datum) (NULL));
+	ResourceArrayInit(&(owner->snapshotarr), (Datum) (NULL));
+	ResourceArrayInit(&(owner->dsmarr), (Datum) (NULL));
+	ResourceArrayInit(&(owner->bufferarr), (Datum) (InvalidBuffer));
+	ResourceArrayInit(&(owner->filearr), (Datum) (-1));
+
 	return owner;
 }
 
@@ -229,6 +421,7 @@ ResourceOwnerReleaseInternal(ResourceOwner owner,
 	ResourceOwner child;
 	ResourceOwner save;
 	ResourceReleaseCallbackItem *item;
+	Datum		foundres;
 
 	/* Recurse to handle descendants */
 	for (child = owner->firstchild; child != NULL; child = child->nextchild)
@@ -252,45 +445,34 @@ ResourceOwnerReleaseInternal(ResourceOwner owner,
 		 * During a commit, there shouldn't be any remaining pins --- that
 		 * would indicate failure to clean up the executor correctly --- so
 		 * issue warnings.  In the abort case, just clean up quietly.
-		 *
-		 * We are careful to do the releasing back-to-front, so as to avoid
-		 * O(N^2) behavior in ResourceOwnerForgetBuffer().
 		 */
-		while (owner->nbuffers > 0)
+		while (ResourceArrayGetAny(&(owner->bufferarr), &foundres))
 		{
+			Buffer		res = (Buffer) foundres;
+
 			if (isCommit)
-				PrintBufferLeakWarning(owner->buffers[owner->nbuffers - 1]);
-			ReleaseBuffer(owner->buffers[owner->nbuffers - 1]);
+				PrintBufferLeakWarning(res);
+			ReleaseBuffer(res);
 		}
 
-		/*
-		 * Release relcache references.  Note that RelationClose will remove
-		 * the relref entry from my list, so I just have to iterate till there
-		 * are none.
-		 *
-		 * As with buffer pins, warn if any are left at commit time, and
-		 * release back-to-front for speed.
-		 */
-		while (owner->nrelrefs > 0)
+		/* Ditto for relcache references. */
+		while (ResourceArrayGetAny(&(owner->relrefarr), &foundres))
 		{
+			Relation	res = (Relation) foundres;
+
 			if (isCommit)
-				PrintRelCacheLeakWarning(owner->relrefs[owner->nrelrefs - 1]);
-			RelationClose(owner->relrefs[owner->nrelrefs - 1]);
+				PrintRelCacheLeakWarning(res);
+			RelationClose(res);
 		}
 
-		/*
-		 * Release dynamic shared memory segments.  Note that dsm_detach()
-		 * will remove the segment from my list, so I just have to iterate
-		 * until there are none.
-		 *
-		 * As in the preceding cases, warn if there are leftover at commit
-		 * time.
-		 */
-		while (owner->ndsms > 0)
+		/* Ditto for dynamic shared memory segments */
+		while (ResourceArrayGetAny(&(owner->dsmarr), &foundres))
 		{
+			dsm_segment *res = (dsm_segment *) foundres;
+
 			if (isCommit)
-				PrintDSMLeakWarning(owner->dsms[owner->ndsms - 1]);
-			dsm_detach(owner->dsms[owner->ndsms - 1]);
+				PrintDSMLeakWarning(res);
+			dsm_detach(res);
 		}
 	}
 	else if (phase == RESOURCE_RELEASE_LOCKS)
@@ -351,47 +533,63 @@ ResourceOwnerReleaseInternal(ResourceOwner owner,
 		 * As with buffer pins, warn if any are left at commit time, and
 		 * release back-to-front for speed.
 		 */
-		while (owner->ncatrefs > 0)
+		while (ResourceArrayGetAny(&(owner->catrefarr), &foundres))
 		{
+			HeapTuple	res = (HeapTuple) foundres;
+
 			if (isCommit)
-				PrintCatCacheLeakWarning(owner->catrefs[owner->ncatrefs - 1]);
-			ReleaseCatCache(owner->catrefs[owner->ncatrefs - 1]);
+				PrintCatCacheLeakWarning(res);
+			ReleaseCatCache(res);
 		}
+
 		/* Ditto for catcache lists */
-		while (owner->ncatlistrefs > 0)
+		while (ResourceArrayGetAny(&(owner->catlistrefarr), &foundres))
 		{
+			CatCList   *res = (CatCList *) foundres;
+
 			if (isCommit)
-				PrintCatCacheListLeakWarning(owner->catlistrefs[owner->ncatlistrefs - 1]);
-			ReleaseCatCacheList(owner->catlistrefs[owner->ncatlistrefs - 1]);
+				PrintCatCacheListLeakWarning(res);
+			ReleaseCatCacheList(res);
 		}
+
 		/* Ditto for plancache references */
-		while (owner->nplanrefs > 0)
+		while (ResourceArrayGetAny(&(owner->planrefarr), &foundres))
 		{
+			CachedPlan *res = (CachedPlan *) foundres;
+
 			if (isCommit)
-				PrintPlanCacheLeakWarning(owner->planrefs[owner->nplanrefs - 1]);
-			ReleaseCachedPlan(owner->planrefs[owner->nplanrefs - 1], true);
+				PrintPlanCacheLeakWarning(res);
+			ReleaseCachedPlan(res, true);
 		}
+
 		/* Ditto for tupdesc references */
-		while (owner->ntupdescs > 0)
+		while (ResourceArrayGetAny(&(owner->tupdescarr), &foundres))
 		{
+			TupleDesc	res = (TupleDesc) foundres;
+
 			if (isCommit)
-				PrintTupleDescLeakWarning(owner->tupdescs[owner->ntupdescs - 1]);
-			DecrTupleDescRefCount(owner->tupdescs[owner->ntupdescs - 1]);
+				PrintTupleDescLeakWarning(res);
+			DecrTupleDescRefCount(res);
 		}
+
 		/* Ditto for snapshot references */
-		while (owner->nsnapshots > 0)
+		while (ResourceArrayGetAny(&(owner->snapshotarr), &foundres))
 		{
+			Snapshot	res = (Snapshot) foundres;
+
 			if (isCommit)
-				PrintSnapshotLeakWarning(owner->snapshots[owner->nsnapshots - 1]);
-			UnregisterSnapshot(owner->snapshots[owner->nsnapshots - 1]);
+				PrintSnapshotLeakWarning(res);
+			UnregisterSnapshot(res);
 		}
 
 		/* Ditto for temporary files */
-		while (owner->nfiles > 0)
+		while (ResourceArrayGetAny(&(owner->filearr), &foundres))
 		{
+			File		res = (File) foundres;
+
 			if (isCommit)
-				PrintFileLeakWarning(owner->files[owner->nfiles - 1]);
-			FileClose(owner->files[owner->nfiles - 1]);
+				PrintFileLeakWarning(res);
+			FileClose(res);
 		}
 
 		/* Clean up index scans too */
@@ -418,16 +616,7 @@ ResourceOwnerDelete(ResourceOwner owner)
 	Assert(owner != CurrentResourceOwner);
 
 	/* And it better not own any resources, either */
-	Assert(owner->nbuffers == 0);
 	Assert(owner->nlocks == 0 || owner->nlocks == MAX_RESOWNER_LOCKS + 1);
-	Assert(owner->ncatrefs == 0);
-	Assert(owner->ncatlistrefs == 0);
-	Assert(owner->nrelrefs == 0);
-	Assert(owner->ndsms == 0);
-	Assert(owner->nplanrefs == 0);
-	Assert(owner->ntupdescs == 0);
-	Assert(owner->nsnapshots == 0);
-	Assert(owner->nfiles == 0);
 
 	/*
 	 * Delete children.  The recursive call will delink the child from me, so
@@ -444,25 +633,15 @@ ResourceOwnerDelete(ResourceOwner owner)
 	ResourceOwnerNewParent(owner, NULL);
 
 	/* And free the object. */
-	if (owner->buffers)
-		pfree(owner->buffers);
-	if (owner->catrefs)
-		pfree(owner->catrefs);
-	if (owner->catlistrefs)
-		pfree(owner->catlistrefs);
-	if (owner->relrefs)
-		pfree(owner->relrefs);
-	if (owner->planrefs)
-		pfree(owner->planrefs);
-	if (owner->tupdescs)
-		pfree(owner->tupdescs);
-	if (owner->snapshots)
-		pfree(owner->snapshots);
-	if (owner->files)
-		pfree(owner->files);
-	if (owner->dsms)
-		pfree(owner->dsms);
-
+	ResourceArrayFree(&(owner->catrefarr));
+	ResourceArrayFree(&(owner->catlistrefarr));
+	ResourceArrayFree(&(owner->relrefarr));
+	ResourceArrayFree(&(owner->planrefarr));
+	ResourceArrayFree(&(owner->tupdescarr));
+	ResourceArrayFree(&(owner->snapshotarr));
+	ResourceArrayFree(&(owner->dsmarr));
+	ResourceArrayFree(&(owner->bufferarr));
+	ResourceArrayFree(&(owner->filearr));
 	pfree(owner);
 }
 
@@ -575,26 +754,9 @@ UnregisterResourceReleaseCallback(ResourceReleaseCallback callback, void *arg)
 void
 ResourceOwnerEnlargeBuffers(ResourceOwner owner)
 {
-	int			newmax;
-
-	if (owner == NULL ||
-		owner->nbuffers < owner->maxbuffers)
-		return;					/* nothing to do */
-
-	if (owner->buffers == NULL)
-	{
-		newmax = 16;
-		owner->buffers = (Buffer *)
-			MemoryContextAlloc(TopMemoryContext, newmax * sizeof(Buffer));
-		owner->maxbuffers = newmax;
-	}
-	else
-	{
-		newmax = owner->maxbuffers * 2;
-		owner->buffers = (Buffer *)
-			repalloc(owner->buffers, newmax * sizeof(Buffer));
-		owner->maxbuffers = newmax;
-	}
+	if (owner == NULL)
+		return;
+	ResourceArrayEnlarge(&(owner->bufferarr));
 }
 
 /*
@@ -608,12 +770,9 @@ ResourceOwnerEnlargeBuffers(ResourceOwner owner)
 void
 ResourceOwnerRememberBuffer(ResourceOwner owner, Buffer buffer)
 {
-	if (owner != NULL)
-	{
-		Assert(owner->nbuffers < owner->maxbuffers);
-		owner->buffers[owner->nbuffers] = buffer;
-		owner->nbuffers++;
-	}
+	if (owner == NULL)
+		return;
+	ResourceArrayAdd(&(owner->bufferarr), (Datum) buffer);
 }
 
 /*
@@ -625,33 +784,15 @@ ResourceOwnerRememberBuffer(ResourceOwner owner, Buffer buffer)
 void
 ResourceOwnerForgetBuffer(ResourceOwner owner, Buffer buffer)
 {
-	if (owner != NULL)
-	{
-		Buffer	   *buffers = owner->buffers;
-		int			nb1 = owner->nbuffers - 1;
-		int			i;
+	bool		res;
 
-		/*
-		 * Scan back-to-front because it's more likely we are releasing a
-		 * recently pinned buffer.  This isn't always the case of course, but
-		 * it's the way to bet.
-		 */
-		for (i = nb1; i >= 0; i--)
-		{
-			if (buffers[i] == buffer)
-			{
-				while (i < nb1)
-				{
-					buffers[i] = buffers[i + 1];
-					i++;
-				}
-				owner->nbuffers = nb1;
-				return;
-			}
-		}
+	if (owner == NULL)
+		return;
+
+	res = ResourceArrayRemove(&(owner->bufferarr), (Datum) buffer);
+	if (!res)
 		elog(ERROR, "buffer %d is not owned by resource owner %s",
 			 buffer, owner->name);
-	}
 }
 
 /*
@@ -667,6 +808,8 @@ ResourceOwnerForgetBuffer(ResourceOwner owner, Buffer buffer)
 void
 ResourceOwnerRememberLock(ResourceOwner owner, LOCALLOCK *locallock)
 {
+	Assert(locallock != NULL);
+
 	if (owner->nlocks > MAX_RESOWNER_LOCKS)
 		return;					/* we have already overflowed */
 
@@ -714,25 +857,7 @@ ResourceOwnerForgetLock(ResourceOwner owner, LOCALLOCK *locallock)
 void
 ResourceOwnerEnlargeCatCacheRefs(ResourceOwner owner)
 {
-	int			newmax;
-
-	if (owner->ncatrefs < owner->maxcatrefs)
-		return;					/* nothing to do */
-
-	if (owner->catrefs == NULL)
-	{
-		newmax = 16;
-		owner->catrefs = (HeapTuple *)
-			MemoryContextAlloc(TopMemoryContext, newmax * sizeof(HeapTuple));
-		owner->maxcatrefs = newmax;
-	}
-	else
-	{
-		newmax = owner->maxcatrefs * 2;
-		owner->catrefs = (HeapTuple *)
-			repalloc(owner->catrefs, newmax * sizeof(HeapTuple));
-		owner->maxcatrefs = newmax;
-	}
+	ResourceArrayEnlarge(&(owner->catrefarr));
 }
 
 /*
@@ -743,9 +868,7 @@ ResourceOwnerEnlargeCatCacheRefs(ResourceOwner owner)
 void
 ResourceOwnerRememberCatCacheRef(ResourceOwner owner, HeapTuple tuple)
 {
-	Assert(owner->ncatrefs < owner->maxcatrefs);
-	owner->catrefs[owner->ncatrefs] = tuple;
-	owner->ncatrefs++;
+	ResourceArrayAdd(&(owner->catrefarr), (Datum) tuple);
 }
 
 /*
@@ -754,25 +877,12 @@ ResourceOwnerRememberCatCacheRef(ResourceOwner owner, HeapTuple tuple)
 void
 ResourceOwnerForgetCatCacheRef(ResourceOwner owner, HeapTuple tuple)
 {
-	HeapTuple  *catrefs = owner->catrefs;
-	int			nc1 = owner->ncatrefs - 1;
-	int			i;
+	bool		res = ResourceArrayRemove(&(owner->catrefarr),
+										  (Datum) tuple);
 
-	for (i = nc1; i >= 0; i--)
-	{
-		if (catrefs[i] == tuple)
-		{
-			while (i < nc1)
-			{
-				catrefs[i] = catrefs[i + 1];
-				i++;
-			}
-			owner->ncatrefs = nc1;
-			return;
-		}
-	}
-	elog(ERROR, "catcache reference %p is not owned by resource owner %s",
-		 tuple, owner->name);
+	if (!res)
+		elog(ERROR, "catcache reference %p is not owned by resource owner %s",
+			 tuple, owner->name);
 }
 
 /*
@@ -785,25 +895,7 @@ ResourceOwnerForgetCatCacheRef(ResourceOwner owner, HeapTuple tuple)
 void
 ResourceOwnerEnlargeCatCacheListRefs(ResourceOwner owner)
 {
-	int			newmax;
-
-	if (owner->ncatlistrefs < owner->maxcatlistrefs)
-		return;					/* nothing to do */
-
-	if (owner->catlistrefs == NULL)
-	{
-		newmax = 16;
-		owner->catlistrefs = (CatCList **)
-			MemoryContextAlloc(TopMemoryContext, newmax * sizeof(CatCList *));
-		owner->maxcatlistrefs = newmax;
-	}
-	else
-	{
-		newmax = owner->maxcatlistrefs * 2;
-		owner->catlistrefs = (CatCList **)
-			repalloc(owner->catlistrefs, newmax * sizeof(CatCList *));
-		owner->maxcatlistrefs = newmax;
-	}
+	ResourceArrayEnlarge(&(owner->catlistrefarr));
 }
 
 /*
@@ -814,9 +906,7 @@ ResourceOwnerEnlargeCatCacheListRefs(ResourceOwner owner)
 void
 ResourceOwnerRememberCatCacheListRef(ResourceOwner owner, CatCList *list)
 {
-	Assert(owner->ncatlistrefs < owner->maxcatlistrefs);
-	owner->catlistrefs[owner->ncatlistrefs] = list;
-	owner->ncatlistrefs++;
+	ResourceArrayAdd(&(owner->catlistrefarr), (Datum) list);
 }
 
 /*
@@ -825,25 +915,12 @@ ResourceOwnerRememberCatCacheListRef(ResourceOwner owner, CatCList *list)
 void
 ResourceOwnerForgetCatCacheListRef(ResourceOwner owner, CatCList *list)
 {
-	CatCList  **catlistrefs = owner->catlistrefs;
-	int			nc1 = owner->ncatlistrefs - 1;
-	int			i;
+	bool		res = ResourceArrayRemove(&(owner->catlistrefarr),
+										  (Datum) list);
 
-	for (i = nc1; i >= 0; i--)
-	{
-		if (catlistrefs[i] == list)
-		{
-			while (i < nc1)
-			{
-				catlistrefs[i] = catlistrefs[i + 1];
-				i++;
-			}
-			owner->ncatlistrefs = nc1;
-			return;
-		}
-	}
-	elog(ERROR, "catcache list reference %p is not owned by resource owner %s",
-		 list, owner->name);
+	if (!res)
+		elog(ERROR, "catcache list reference %p is not owned by resource owner %s",
+			 list, owner->name);
 }
 
 /*
@@ -856,25 +933,7 @@ ResourceOwnerForgetCatCacheListRef(ResourceOwner owner, CatCList *list)
 void
 ResourceOwnerEnlargeRelationRefs(ResourceOwner owner)
 {
-	int			newmax;
-
-	if (owner->nrelrefs < owner->maxrelrefs)
-		return;					/* nothing to do */
-
-	if (owner->relrefs == NULL)
-	{
-		newmax = 16;
-		owner->relrefs = (Relation *)
-			MemoryContextAlloc(TopMemoryContext, newmax * sizeof(Relation));
-		owner->maxrelrefs = newmax;
-	}
-	else
-	{
-		newmax = owner->maxrelrefs * 2;
-		owner->relrefs = (Relation *)
-			repalloc(owner->relrefs, newmax * sizeof(Relation));
-		owner->maxrelrefs = newmax;
-	}
+	ResourceArrayEnlarge(&(owner->relrefarr));
 }
 
 /*
@@ -885,9 +944,7 @@ ResourceOwnerEnlargeRelationRefs(ResourceOwner owner)
 void
 ResourceOwnerRememberRelationRef(ResourceOwner owner, Relation rel)
 {
-	Assert(owner->nrelrefs < owner->maxrelrefs);
-	owner->relrefs[owner->nrelrefs] = rel;
-	owner->nrelrefs++;
+	ResourceArrayAdd(&(owner->relrefarr), (Datum) rel);
 }
 
 /*
@@ -896,25 +953,12 @@ ResourceOwnerRememberRelationRef(ResourceOwner owner, Relation rel)
 void
 ResourceOwnerForgetRelationRef(ResourceOwner owner, Relation rel)
 {
-	Relation   *relrefs = owner->relrefs;
-	int			nr1 = owner->nrelrefs - 1;
-	int			i;
+	bool		res = ResourceArrayRemove(&(owner->relrefarr),
+										  (Datum) rel);
 
-	for (i = nr1; i >= 0; i--)
-	{
-		if (relrefs[i] == rel)
-		{
-			while (i < nr1)
-			{
-				relrefs[i] = relrefs[i + 1];
-				i++;
-			}
-			owner->nrelrefs = nr1;
-			return;
-		}
-	}
-	elog(ERROR, "relcache reference %s is not owned by resource owner %s",
-		 RelationGetRelationName(rel), owner->name);
+	if (!res)
+		elog(ERROR, "relcache reference %s is not owned by resource owner %s",
+			 RelationGetRelationName(rel), owner->name);
 }
 
 /*
@@ -937,25 +981,7 @@ PrintRelCacheLeakWarning(Relation rel)
 void
 ResourceOwnerEnlargePlanCacheRefs(ResourceOwner owner)
 {
-	int			newmax;
-
-	if (owner->nplanrefs < owner->maxplanrefs)
-		return;					/* nothing to do */
-
-	if (owner->planrefs == NULL)
-	{
-		newmax = 16;
-		owner->planrefs = (CachedPlan **)
-			MemoryContextAlloc(TopMemoryContext, newmax * sizeof(CachedPlan *));
-		owner->maxplanrefs = newmax;
-	}
-	else
-	{
-		newmax = owner->maxplanrefs * 2;
-		owner->planrefs = (CachedPlan **)
-			repalloc(owner->planrefs, newmax * sizeof(CachedPlan *));
-		owner->maxplanrefs = newmax;
-	}
+	ResourceArrayEnlarge(&(owner->planrefarr));
 }
 
 /*
@@ -966,9 +992,7 @@ ResourceOwnerEnlargePlanCacheRefs(ResourceOwner owner)
 void
 ResourceOwnerRememberPlanCacheRef(ResourceOwner owner, CachedPlan *plan)
 {
-	Assert(owner->nplanrefs < owner->maxplanrefs);
-	owner->planrefs[owner->nplanrefs] = plan;
-	owner->nplanrefs++;
+	ResourceArrayAdd(&(owner->planrefarr), (Datum) plan);
 }
 
 /*
@@ -977,25 +1001,12 @@ ResourceOwnerRememberPlanCacheRef(ResourceOwner owner, CachedPlan *plan)
 void
 ResourceOwnerForgetPlanCacheRef(ResourceOwner owner, CachedPlan *plan)
 {
-	CachedPlan **planrefs = owner->planrefs;
-	int			np1 = owner->nplanrefs - 1;
-	int			i;
+	bool		res = ResourceArrayRemove(&(owner->planrefarr),
+										  (Datum) plan);
 
-	for (i = np1; i >= 0; i--)
-	{
-		if (planrefs[i] == plan)
-		{
-			while (i < np1)
-			{
-				planrefs[i] = planrefs[i + 1];
-				i++;
-			}
-			owner->nplanrefs = np1;
-			return;
-		}
-	}
-	elog(ERROR, "plancache reference %p is not owned by resource owner %s",
-		 plan, owner->name);
+	if (!res)
+		elog(ERROR, "plancache reference %p is not owned by resource owner %s",
+			 plan, owner->name);
 }
 
 /*
@@ -1017,25 +1028,7 @@ PrintPlanCacheLeakWarning(CachedPlan *plan)
 void
 ResourceOwnerEnlargeTupleDescs(ResourceOwner owner)
 {
-	int			newmax;
-
-	if (owner->ntupdescs < owner->maxtupdescs)
-		return;					/* nothing to do */
-
-	if (owner->tupdescs == NULL)
-	{
-		newmax = 16;
-		owner->tupdescs = (TupleDesc *)
-			MemoryContextAlloc(TopMemoryContext, newmax * sizeof(TupleDesc));
-		owner->maxtupdescs = newmax;
-	}
-	else
-	{
-		newmax = owner->maxtupdescs * 2;
-		owner->tupdescs = (TupleDesc *)
-			repalloc(owner->tupdescs, newmax * sizeof(TupleDesc));
-		owner->maxtupdescs = newmax;
-	}
+	ResourceArrayEnlarge(&(owner->tupdescarr));
 }
 
 /*
@@ -1046,9 +1039,7 @@ ResourceOwnerEnlargeTupleDescs(ResourceOwner owner)
 void
 ResourceOwnerRememberTupleDesc(ResourceOwner owner, TupleDesc tupdesc)
 {
-	Assert(owner->ntupdescs < owner->maxtupdescs);
-	owner->tupdescs[owner->ntupdescs] = tupdesc;
-	owner->ntupdescs++;
+	ResourceArrayAdd(&(owner->tupdescarr), (Datum) tupdesc);
 }
 
 /*
@@ -1057,25 +1048,12 @@ ResourceOwnerRememberTupleDesc(ResourceOwner owner, TupleDesc tupdesc)
 void
 ResourceOwnerForgetTupleDesc(ResourceOwner owner, TupleDesc tupdesc)
 {
-	TupleDesc  *tupdescs = owner->tupdescs;
-	int			nt1 = owner->ntupdescs - 1;
-	int			i;
+	bool		res = ResourceArrayRemove(&(owner->tupdescarr),
+										  (Datum) tupdesc);
 
-	for (i = nt1; i >= 0; i--)
-	{
-		if (tupdescs[i] == tupdesc)
-		{
-			while (i < nt1)
-			{
-				tupdescs[i] = tupdescs[i + 1];
-				i++;
-			}
-			owner->ntupdescs = nt1;
-			return;
-		}
-	}
-	elog(ERROR, "tupdesc reference %p is not owned by resource owner %s",
-		 tupdesc, owner->name);
+	if (!res)
+		elog(ERROR, "tupdesc reference %p is not owned by resource owner %s",
+			 tupdesc, owner->name);
 }
 
 /*
@@ -1099,25 +1077,7 @@ PrintTupleDescLeakWarning(TupleDesc tupdesc)
 void
 ResourceOwnerEnlargeSnapshots(ResourceOwner owner)
 {
-	int			newmax;
-
-	if (owner->nsnapshots < owner->maxsnapshots)
-		return;					/* nothing to do */
-
-	if (owner->snapshots == NULL)
-	{
-		newmax = 16;
-		owner->snapshots = (Snapshot *)
-			MemoryContextAlloc(TopMemoryContext, newmax * sizeof(Snapshot));
-		owner->maxsnapshots = newmax;
-	}
-	else
-	{
-		newmax = owner->maxsnapshots * 2;
-		owner->snapshots = (Snapshot *)
-			repalloc(owner->snapshots, newmax * sizeof(Snapshot));
-		owner->maxsnapshots = newmax;
-	}
+	ResourceArrayEnlarge(&(owner->snapshotarr));
 }
 
 /*
@@ -1128,9 +1088,7 @@ ResourceOwnerEnlargeSnapshots(ResourceOwner owner)
 void
 ResourceOwnerRememberSnapshot(ResourceOwner owner, Snapshot snapshot)
 {
-	Assert(owner->nsnapshots < owner->maxsnapshots);
-	owner->snapshots[owner->nsnapshots] = snapshot;
-	owner->nsnapshots++;
+	ResourceArrayAdd(&(owner->snapshotarr), (Datum) snapshot);
 }
 
 /*
@@ -1139,25 +1097,12 @@ ResourceOwnerRememberSnapshot(ResourceOwner owner, Snapshot snapshot)
 void
 ResourceOwnerForgetSnapshot(ResourceOwner owner, Snapshot snapshot)
 {
-	Snapshot   *snapshots = owner->snapshots;
-	int			ns1 = owner->nsnapshots - 1;
-	int			i;
+	bool		res = ResourceArrayRemove(&(owner->snapshotarr),
+										  (Datum) snapshot);
 
-	for (i = ns1; i >= 0; i--)
-	{
-		if (snapshots[i] == snapshot)
-		{
-			while (i < ns1)
-			{
-				snapshots[i] = snapshots[i + 1];
-				i++;
-			}
-			owner->nsnapshots = ns1;
-			return;
-		}
-	}
-	elog(ERROR, "snapshot reference %p is not owned by resource owner %s",
-		 snapshot, owner->name);
+	if (!res)
+		elog(ERROR, "snapshot reference %p is not owned by resource owner %s",
+			 snapshot, owner->name);
 }
 
 /*
@@ -1182,25 +1127,7 @@ PrintSnapshotLeakWarning(Snapshot snapshot)
 void
 ResourceOwnerEnlargeFiles(ResourceOwner owner)
 {
-	int			newmax;
-
-	if (owner->nfiles < owner->maxfiles)
-		return;					/* nothing to do */
-
-	if (owner->files == NULL)
-	{
-		newmax = 16;
-		owner->files = (File *)
-			MemoryContextAlloc(TopMemoryContext, newmax * sizeof(File));
-		owner->maxfiles = newmax;
-	}
-	else
-	{
-		newmax = owner->maxfiles * 2;
-		owner->files = (File *)
-			repalloc(owner->files, newmax * sizeof(File));
-		owner->maxfiles = newmax;
-	}
+	ResourceArrayEnlarge(&(owner->filearr));
 }
 
 /*
@@ -1211,9 +1138,7 @@ ResourceOwnerEnlargeFiles(ResourceOwner owner)
 void
 ResourceOwnerRememberFile(ResourceOwner owner, File file)
 {
-	Assert(owner->nfiles < owner->maxfiles);
-	owner->files[owner->nfiles] = file;
-	owner->nfiles++;
+	ResourceArrayAdd(&(owner->filearr), (Datum) file);
 }
 
 /*
@@ -1222,25 +1147,12 @@ ResourceOwnerRememberFile(ResourceOwner owner, File file)
 void
 ResourceOwnerForgetFile(ResourceOwner owner, File file)
 {
-	File	   *files = owner->files;
-	int			ns1 = owner->nfiles - 1;
-	int			i;
+	bool		res = ResourceArrayRemove(&(owner->filearr),
+										  (Datum) file);
 
-	for (i = ns1; i >= 0; i--)
-	{
-		if (files[i] == file)
-		{
-			while (i < ns1)
-			{
-				files[i] = files[i + 1];
-				i++;
-			}
-			owner->nfiles = ns1;
-			return;
-		}
-	}
-	elog(ERROR, "temporery file %d is not owned by resource owner %s",
-		 file, owner->name);
+	if (!res)
+		elog(ERROR, "temporary file %d is not owned by resource owner %s",
+			 file, owner->name);
 }
 
 
@@ -1265,26 +1177,7 @@ PrintFileLeakWarning(File file)
 void
 ResourceOwnerEnlargeDSMs(ResourceOwner owner)
 {
-	int			newmax;
-
-	if (owner->ndsms < owner->maxdsms)
-		return;					/* nothing to do */
-
-	if (owner->dsms == NULL)
-	{
-		newmax = 16;
-		owner->dsms = (dsm_segment **)
-			MemoryContextAlloc(TopMemoryContext,
-							   newmax * sizeof(dsm_segment *));
-		owner->maxdsms = newmax;
-	}
-	else
-	{
-		newmax = owner->maxdsms * 2;
-		owner->dsms = (dsm_segment **)
-			repalloc(owner->dsms, newmax * sizeof(dsm_segment *));
-		owner->maxdsms = newmax;
-	}
+	ResourceArrayEnlarge(&(owner->dsmarr));
 }
 
 /*
@@ -1295,9 +1188,7 @@ ResourceOwnerEnlargeDSMs(ResourceOwner owner)
 void
 ResourceOwnerRememberDSM(ResourceOwner owner, dsm_segment *seg)
 {
-	Assert(owner->ndsms < owner->maxdsms);
-	owner->dsms[owner->ndsms] = seg;
-	owner->ndsms++;
+	ResourceArrayAdd(&(owner->dsmarr), (Datum) seg);
 }
 
 /*
@@ -1306,26 +1197,12 @@ ResourceOwnerRememberDSM(ResourceOwner owner, dsm_segment *seg)
 void
 ResourceOwnerForgetDSM(ResourceOwner owner, dsm_segment *seg)
 {
-	dsm_segment **dsms = owner->dsms;
-	int			ns1 = owner->ndsms - 1;
-	int			i;
+	bool		res = ResourceArrayRemove(&(owner->dsmarr),
+										  (Datum) seg);
 
-	for (i = ns1; i >= 0; i--)
-	{
-		if (dsms[i] == seg)
-		{
-			while (i < ns1)
-			{
-				dsms[i] = dsms[i + 1];
-				i++;
-			}
-			owner->ndsms = ns1;
-			return;
-		}
-	}
-	elog(ERROR,
-		 "dynamic shared memory segment %u is not owned by resource owner %s",
-		 dsm_segment_handle(seg), owner->name);
+	if (!res)
+		elog(ERROR, "dynamic shared memory segment %u is not owned by resource"
+			 " owner %s", dsm_segment_handle(seg), owner->name);
 }
 
 
resource-owner-optimization-v4-step2.patchtext/x-patchDownload
diff --git a/src/backend/access/hash/hashfunc.c b/src/backend/access/hash/hashfunc.c
index 9ee654e..5a00a03 100644
--- a/src/backend/access/hash/hashfunc.c
+++ b/src/backend/access/hash/hashfunc.c
@@ -297,6 +297,9 @@ hashvarlena(PG_FUNCTION_ARGS)
  * of 2.  There is no need to do mod a prime (mod is sooo slow!).
  * If you need less than 32 bits, use a bitmask.
  *
+ * This procedure never fails. Its important. Some code notably ResourceOwner
+ * relies on this.
+ *
  * Note: we could easily change this function to return a 64-bit hash value
  * by using the final values of both b and c.  b is perhaps a little less
  * well mixed than c, however.
diff --git a/src/backend/utils/resowner/resowner.c b/src/backend/utils/resowner/resowner.c
index 3330c8d..abed36c 100644
--- a/src/backend/utils/resowner/resowner.c
+++ b/src/backend/utils/resowner/resowner.c
@@ -37,29 +37,60 @@
  * which should be called before corresponding ResourceOwnerRemember* calls
  * (see below). Internally each type of resource is stored in separate
  * ResourceArray.
+ *
+ * There are two major reasons for using ResourceArray instead of, say,
+ * regular C arrays.
+ *
+ * Firstly we would like to prevent code duplication. For instance
+ * ResourceArray provides generic Remember/Forget/Enlarge procedures, so
+ * corresponding ResourceOwner* procedures are just a typesafe wrappers for
+ * these procedures.
+ *
+ * Secondly ResourceArray must be more efficient than regular C array.
+ * Current implementation in general could be considered a hash table. It has
+ * O(1) complexity of both Remember and Forget procedures.
  */
 typedef struct ResourceArray
 {
 	Datum	   *itemsarr;		/* buffer for storing values */
+	Datum		invalidval;		/* value that is considered invalid */
 	uint32		capacity;		/* capacity of array */
 	uint32		nitems;			/* how many items is stored in items array */
+	uint32		maxitems;		/* precalculated RESARRAY_MAX_ITEMS(capacity) */
+	uint32		lastidx;		/* index of last item returned by GetAny */
 }	ResourceArray;
 
 /*
  * This number is used as initial size of resource array. If given number of
  * items is not enough, we double array size and reallocate memory.
+ *
+ * Should be power of two since we use (arrsize - 1) as mask for hash value.
+ *
  */
 #define RESARRAY_INIT_SIZE 16
 
 /*
+ * How many items could be stored in a resource array of given capacity. If
+ * this number is reached we need to resize an array to prevent hash collisions.
+ *
+ * This computation actually costs only two additions and one binary shift.
+ */
+#define RESARRAY_MAX_ITEMS(capacity) ((capacity)*3/4)
+
+/*
  * Initialize ResourceArray
  */
 static void
-ResourceArrayInit(ResourceArray * resarr)
+ResourceArrayInit(ResourceArray * resarr, Datum invalidval)
 {
 	Assert(resarr->itemsarr == NULL);
 	Assert(resarr->capacity == 0);
 	Assert(resarr->nitems == 0);
+	Assert(resarr->maxitems == 0);
+	Assert(resarr->invalidval == 0);
+	Assert(resarr->lastidx == 0);
+
+	resarr->invalidval = invalidval;
 }
 
 /*
@@ -70,11 +101,24 @@ ResourceArrayInit(ResourceArray * resarr)
 static void
 ResourceArrayAdd(ResourceArray * resarr, Datum data)
 {
+	Datum		idx;
+	Datum		mask = resarr->capacity - 1;
+
+	Assert(resarr->maxitems > resarr->nitems);
 	Assert(resarr->capacity > 0);
 	Assert(resarr->itemsarr != NULL);
-	Assert(resarr->nitems < resarr->capacity);
+	Assert(data != resarr->invalidval);
+
+	idx = hash_any((void *) &data, sizeof(data)) & mask;
+
+	while (true)
+	{
+		if (resarr->itemsarr[idx] == resarr->invalidval)
+			break;
+		idx = (idx + 1) & mask;
+	}
 
-	resarr->itemsarr[resarr->nitems] = data;
+	resarr->itemsarr[idx] = data;
 	resarr->nitems++;
 }
 
@@ -86,24 +130,24 @@ ResourceArrayAdd(ResourceArray * resarr, Datum data)
 static bool
 ResourceArrayRemove(ResourceArray * resarr, Datum data)
 {
-	int			i,
-				j,
-				lastidx;
+	uint32		i;
+	Datum		idx;
+	Datum		mask = resarr->capacity - 1;
 
 	Assert(resarr->capacity > 0);
 	Assert(resarr->itemsarr != NULL);
+	Assert(data != resarr->invalidval);
 
-	lastidx = ((int) resarr->nitems) - 1;
-
-	for (i = lastidx; i >= 0; i--)
+	idx = hash_any((void *) &data, sizeof(data)) & mask;
+	for (i = 0; i < resarr->capacity; i++)
 	{
-		if (resarr->itemsarr[i] == data)
+		if (resarr->itemsarr[idx] == data)
 		{
-			for (j = i; j < lastidx; j++)
-				resarr->itemsarr[j] = resarr->itemsarr[j + 1];
+			resarr->itemsarr[idx] = resarr->invalidval;
 			resarr->nitems--;
 			return true;
 		}
+		idx = (idx + 1) & mask;
 	}
 
 	return false;
@@ -119,27 +163,33 @@ static void
 ResourceArrayEnlarge(ResourceArray * resarr)
 {
 	uint32		i,
-				oldcap,
-				oldnitems;
+				oldcap;
 	Datum	   *olditemsarr;
 
-	if (resarr->nitems < resarr->capacity)
+	if (resarr->nitems < resarr->maxitems)
 		return;					/* nothing to do */
 
 	olditemsarr = resarr->itemsarr;
 	oldcap = resarr->capacity;
-	oldnitems = resarr->nitems;
 
 	resarr->capacity = oldcap > 0 ? oldcap * 2 : RESARRAY_INIT_SIZE;
 	resarr->itemsarr = (Datum *)
 		MemoryContextAlloc(TopMemoryContext,
 						   resarr->capacity * sizeof(Datum));
+	resarr->maxitems = RESARRAY_MAX_ITEMS(resarr->capacity);
 	resarr->nitems = 0;
 
+	for (i = 0; i < resarr->capacity; i++)
+		resarr->itemsarr[i] = resarr->invalidval;
+
 	if (olditemsarr != NULL)
 	{
-		for (i = 0; i < oldnitems; i++)
-			ResourceArrayAdd(resarr, olditemsarr[i]);
+		while (oldcap > 0)
+		{
+			oldcap--;
+			if (olditemsarr[oldcap] != resarr->invalidval)
+				ResourceArrayAdd(resarr, olditemsarr[oldcap]);
+		}
 		pfree(olditemsarr);
 	}
 }
@@ -152,12 +202,24 @@ ResourceArrayEnlarge(ResourceArray * resarr)
 static bool
 ResourceArrayGetAny(ResourceArray * resarr, Datum *out)
 {
+	uint32		mask;
+
 	if (resarr->nitems == 0)
 		return false;
 
 	Assert(resarr->capacity > 0);
+	mask = resarr->capacity - 1;
+
+	for (;;)
+	{
+		resarr->lastidx = resarr->lastidx & mask;
+		if (resarr->itemsarr[resarr->lastidx] != resarr->invalidval)
+			break;
+
+		resarr->lastidx++;
+	}
 
-	*out = resarr->itemsarr[resarr->nitems - 1];
+	*out = resarr->itemsarr[resarr->lastidx];
 	return true;
 }
 
@@ -170,6 +232,7 @@ ResourceArrayFree(ResourceArray * resarr)
 	Assert(resarr->nitems == 0);
 
 	resarr->capacity = 0;
+	resarr->maxitems = 0;
 
 	if (!resarr->itemsarr)
 		return;
@@ -287,15 +350,15 @@ ResourceOwnerCreate(ResourceOwner parent, const char *name)
 		parent->firstchild = owner;
 	}
 
-	ResourceArrayInit(&(owner->catrefarr));
-	ResourceArrayInit(&(owner->catlistrefarr));
-	ResourceArrayInit(&(owner->relrefarr));
-	ResourceArrayInit(&(owner->planrefarr));
-	ResourceArrayInit(&(owner->tupdescarr));
-	ResourceArrayInit(&(owner->snapshotarr));
-	ResourceArrayInit(&(owner->dsmarr));
-	ResourceArrayInit(&(owner->bufferarr));
-	ResourceArrayInit(&(owner->filearr));
+	ResourceArrayInit(&(owner->catrefarr), (Datum) (NULL));
+	ResourceArrayInit(&(owner->catlistrefarr), (Datum) (NULL));
+	ResourceArrayInit(&(owner->relrefarr), (Datum) (NULL));
+	ResourceArrayInit(&(owner->planrefarr), (Datum) (NULL));
+	ResourceArrayInit(&(owner->tupdescarr), (Datum) (NULL));
+	ResourceArrayInit(&(owner->snapshotarr), (Datum) (NULL));
+	ResourceArrayInit(&(owner->dsmarr), (Datum) (NULL));
+	ResourceArrayInit(&(owner->bufferarr), (Datum) (InvalidBuffer));
+	ResourceArrayInit(&(owner->filearr), (Datum) (-1));
 
 	return owner;
 }
#13Aleksander Alekseev
a.alekseev@postgrespro.ru
In reply to: Aleksander Alekseev (#12)
2 attachment(s)
Re: Patch: ResourceOwner optimization for tables with many partitions

Oops, wrong patches - here are correct ones.

Attachments:

resource-owner-optimization-v4-step1.patchtext/x-patchDownload
diff --git a/src/backend/utils/resowner/resowner.c b/src/backend/utils/resowner/resowner.c
index 0e7acbf..3330c8d 100644
--- a/src/backend/utils/resowner/resowner.c
+++ b/src/backend/utils/resowner/resowner.c
@@ -29,6 +29,156 @@
 #include "utils/snapmgr.h"
 
 /*
+ * ResourceArray is a common structure for storing different types of resources.
+ *
+ * ResourceOwner can own `HeapTuple`s, `Relation`s, `Snapshot`s, etc. For
+ * each resource type there are procedures ResourceOwnerRemember* and
+ * ResourceOwnerForget*. There are also ResourceOwnerEnlarge* procedures
+ * which should be called before corresponding ResourceOwnerRemember* calls
+ * (see below). Internally each type of resource is stored in separate
+ * ResourceArray.
+ */
+typedef struct ResourceArray
+{
+	Datum	   *itemsarr;		/* buffer for storing values */
+	uint32		capacity;		/* capacity of array */
+	uint32		nitems;			/* how many items is stored in items array */
+}	ResourceArray;
+
+/*
+ * This number is used as initial size of resource array. If given number of
+ * items is not enough, we double array size and reallocate memory.
+ */
+#define RESARRAY_INIT_SIZE 16
+
+/*
+ * Initialize ResourceArray
+ */
+static void
+ResourceArrayInit(ResourceArray * resarr)
+{
+	Assert(resarr->itemsarr == NULL);
+	Assert(resarr->capacity == 0);
+	Assert(resarr->nitems == 0);
+}
+
+/*
+ * Add a resource to ResourceArray
+ *
+ * Caller must have previously done ResourceArrayEnlarge()
+ */
+static void
+ResourceArrayAdd(ResourceArray * resarr, Datum data)
+{
+	Assert(resarr->capacity > 0);
+	Assert(resarr->itemsarr != NULL);
+	Assert(resarr->nitems < resarr->capacity);
+
+	resarr->itemsarr[resarr->nitems] = data;
+	resarr->nitems++;
+}
+
+/*
+ * Remove a resource from ResourceArray
+ *
+ * Returns true on success, false if resource was not found
+ */
+static bool
+ResourceArrayRemove(ResourceArray * resarr, Datum data)
+{
+	int			i,
+				j,
+				lastidx;
+
+	Assert(resarr->capacity > 0);
+	Assert(resarr->itemsarr != NULL);
+
+	lastidx = ((int) resarr->nitems) - 1;
+
+	for (i = lastidx; i >= 0; i--)
+	{
+		if (resarr->itemsarr[i] == data)
+		{
+			for (j = i; j < lastidx; j++)
+				resarr->itemsarr[j] = resarr->itemsarr[j + 1];
+			resarr->nitems--;
+			return true;
+		}
+	}
+
+	return false;
+}
+
+/*
+ * Make sure there is a room for at least one more resource in an array.
+ *
+ * This is separate from actually inserting a resource because if we run out
+ * of memory, it's critical to do so *before* acquiring the resource.
+ */
+static void
+ResourceArrayEnlarge(ResourceArray * resarr)
+{
+	uint32		i,
+				oldcap,
+				oldnitems;
+	Datum	   *olditemsarr;
+
+	if (resarr->nitems < resarr->capacity)
+		return;					/* nothing to do */
+
+	olditemsarr = resarr->itemsarr;
+	oldcap = resarr->capacity;
+	oldnitems = resarr->nitems;
+
+	resarr->capacity = oldcap > 0 ? oldcap * 2 : RESARRAY_INIT_SIZE;
+	resarr->itemsarr = (Datum *)
+		MemoryContextAlloc(TopMemoryContext,
+						   resarr->capacity * sizeof(Datum));
+	resarr->nitems = 0;
+
+	if (olditemsarr != NULL)
+	{
+		for (i = 0; i < oldnitems; i++)
+			ResourceArrayAdd(resarr, olditemsarr[i]);
+		pfree(olditemsarr);
+	}
+}
+
+/*
+ * Get any convenient element.
+ *
+ * Returns true on success, false on failure.
+ */
+static bool
+ResourceArrayGetAny(ResourceArray * resarr, Datum *out)
+{
+	if (resarr->nitems == 0)
+		return false;
+
+	Assert(resarr->capacity > 0);
+
+	*out = resarr->itemsarr[resarr->nitems - 1];
+	return true;
+}
+
+/*
+ * Return ResourceArray to initial state
+ */
+static void
+ResourceArrayFree(ResourceArray * resarr)
+{
+	Assert(resarr->nitems == 0);
+
+	resarr->capacity = 0;
+
+	if (!resarr->itemsarr)
+		return;
+
+	pfree(resarr->itemsarr);
+	resarr->itemsarr = NULL;
+}
+
+/*
  * To speed up bulk releasing or reassigning locks from a resource owner to
  * its parent, each resource owner has a small cache of locks it owns. The
  * lock manager has the same information in its local lock hash table, and
@@ -56,53 +206,22 @@ typedef struct ResourceOwnerData
 	ResourceOwner nextchild;	/* next child of same parent */
 	const char *name;			/* name (just for debugging) */
 
-	/* We have built-in support for remembering owned buffers */
-	int			nbuffers;		/* number of owned buffer pins */
-	Buffer	   *buffers;		/* dynamically allocated array */
-	int			maxbuffers;		/* currently allocated array size */
-
 	/* We can remember up to MAX_RESOWNER_LOCKS references to local locks. */
 	int			nlocks;			/* number of owned locks */
 	LOCALLOCK  *locks[MAX_RESOWNER_LOCKS];		/* list of owned locks */
 
-	/* We have built-in support for remembering catcache references */
-	int			ncatrefs;		/* number of owned catcache pins */
-	HeapTuple  *catrefs;		/* dynamically allocated array */
-	int			maxcatrefs;		/* currently allocated array size */
-
-	int			ncatlistrefs;	/* number of owned catcache-list pins */
-	CatCList  **catlistrefs;	/* dynamically allocated array */
-	int			maxcatlistrefs; /* currently allocated array size */
-
-	/* We have built-in support for remembering relcache references */
-	int			nrelrefs;		/* number of owned relcache pins */
-	Relation   *relrefs;		/* dynamically allocated array */
-	int			maxrelrefs;		/* currently allocated array size */
-
-	/* We have built-in support for remembering plancache references */
-	int			nplanrefs;		/* number of owned plancache pins */
-	CachedPlan **planrefs;		/* dynamically allocated array */
-	int			maxplanrefs;	/* currently allocated array size */
-
-	/* We have built-in support for remembering tupdesc references */
-	int			ntupdescs;		/* number of owned tupdesc references */
-	TupleDesc  *tupdescs;		/* dynamically allocated array */
-	int			maxtupdescs;	/* currently allocated array size */
-
-	/* We have built-in support for remembering snapshot references */
-	int			nsnapshots;		/* number of owned snapshot references */
-	Snapshot   *snapshots;		/* dynamically allocated array */
-	int			maxsnapshots;	/* currently allocated array size */
-
-	/* We have built-in support for remembering open temporary files */
-	int			nfiles;			/* number of owned temporary files */
-	File	   *files;			/* dynamically allocated array */
-	int			maxfiles;		/* currently allocated array size */
-
-	/* We have built-in support for remembering dynamic shmem segments */
-	int			ndsms;			/* number of owned shmem segments */
-	dsm_segment **dsms;			/* dynamically allocated array */
-	int			maxdsms;		/* currently allocated array size */
+	/* We have built-in support for remembering: */
+
+	ResourceArray catrefarr;	/* `HeapTuple`s */
+	ResourceArray catlistrefarr;	/* `ResourceOwner`s */
+	ResourceArray relrefarr;	/* `Relation`s */
+	ResourceArray planrefarr;	/* `CachedPlan*`s */
+	ResourceArray tupdescarr;	/* `TupleDesc`s */
+	ResourceArray snapshotarr;	/* `Snapshot`s */
+	ResourceArray dsmarr;		/* `dsm_segment*`s */
+	ResourceArray bufferarr;	/* `Buffer`s  */
+	ResourceArray filearr;		/* `File`s */
+
 }	ResourceOwnerData;
 
 
@@ -168,6 +287,16 @@ ResourceOwnerCreate(ResourceOwner parent, const char *name)
 		parent->firstchild = owner;
 	}
 
+	ResourceArrayInit(&(owner->catrefarr));
+	ResourceArrayInit(&(owner->catlistrefarr));
+	ResourceArrayInit(&(owner->relrefarr));
+	ResourceArrayInit(&(owner->planrefarr));
+	ResourceArrayInit(&(owner->tupdescarr));
+	ResourceArrayInit(&(owner->snapshotarr));
+	ResourceArrayInit(&(owner->dsmarr));
+	ResourceArrayInit(&(owner->bufferarr));
+	ResourceArrayInit(&(owner->filearr));
+
 	return owner;
 }
 
@@ -229,6 +358,7 @@ ResourceOwnerReleaseInternal(ResourceOwner owner,
 	ResourceOwner child;
 	ResourceOwner save;
 	ResourceReleaseCallbackItem *item;
+	Datum		foundres;
 
 	/* Recurse to handle descendants */
 	for (child = owner->firstchild; child != NULL; child = child->nextchild)
@@ -252,45 +382,34 @@ ResourceOwnerReleaseInternal(ResourceOwner owner,
 		 * During a commit, there shouldn't be any remaining pins --- that
 		 * would indicate failure to clean up the executor correctly --- so
 		 * issue warnings.  In the abort case, just clean up quietly.
-		 *
-		 * We are careful to do the releasing back-to-front, so as to avoid
-		 * O(N^2) behavior in ResourceOwnerForgetBuffer().
 		 */
-		while (owner->nbuffers > 0)
+		while (ResourceArrayGetAny(&(owner->bufferarr), &foundres))
 		{
+			Buffer		res = (Buffer) foundres;
+
 			if (isCommit)
-				PrintBufferLeakWarning(owner->buffers[owner->nbuffers - 1]);
-			ReleaseBuffer(owner->buffers[owner->nbuffers - 1]);
+				PrintBufferLeakWarning(res);
+			ReleaseBuffer(res);
 		}
 
-		/*
-		 * Release relcache references.  Note that RelationClose will remove
-		 * the relref entry from my list, so I just have to iterate till there
-		 * are none.
-		 *
-		 * As with buffer pins, warn if any are left at commit time, and
-		 * release back-to-front for speed.
-		 */
-		while (owner->nrelrefs > 0)
+		/* Ditto for relcache references. */
+		while (ResourceArrayGetAny(&(owner->relrefarr), &foundres))
 		{
+			Relation	res = (Relation) foundres;
+
 			if (isCommit)
-				PrintRelCacheLeakWarning(owner->relrefs[owner->nrelrefs - 1]);
-			RelationClose(owner->relrefs[owner->nrelrefs - 1]);
+				PrintRelCacheLeakWarning(res);
+			RelationClose(res);
 		}
 
-		/*
-		 * Release dynamic shared memory segments.  Note that dsm_detach()
-		 * will remove the segment from my list, so I just have to iterate
-		 * until there are none.
-		 *
-		 * As in the preceding cases, warn if there are leftover at commit
-		 * time.
-		 */
-		while (owner->ndsms > 0)
+		/* Ditto for dynamic shared memory segments */
+		while (ResourceArrayGetAny(&(owner->dsmarr), &foundres))
 		{
+			dsm_segment *res = (dsm_segment *) foundres;
+
 			if (isCommit)
-				PrintDSMLeakWarning(owner->dsms[owner->ndsms - 1]);
-			dsm_detach(owner->dsms[owner->ndsms - 1]);
+				PrintDSMLeakWarning(res);
+			dsm_detach(res);
 		}
 	}
 	else if (phase == RESOURCE_RELEASE_LOCKS)
@@ -351,47 +470,63 @@ ResourceOwnerReleaseInternal(ResourceOwner owner,
 		 * As with buffer pins, warn if any are left at commit time, and
 		 * release back-to-front for speed.
 		 */
-		while (owner->ncatrefs > 0)
+		while (ResourceArrayGetAny(&(owner->catrefarr), &foundres))
 		{
+			HeapTuple	res = (HeapTuple) foundres;
+
 			if (isCommit)
-				PrintCatCacheLeakWarning(owner->catrefs[owner->ncatrefs - 1]);
-			ReleaseCatCache(owner->catrefs[owner->ncatrefs - 1]);
+				PrintCatCacheLeakWarning(res);
+			ReleaseCatCache(res);
 		}
+
 		/* Ditto for catcache lists */
-		while (owner->ncatlistrefs > 0)
+		while (ResourceArrayGetAny(&(owner->catlistrefarr), &foundres))
 		{
+			CatCList   *res = (CatCList *) foundres;
+
 			if (isCommit)
-				PrintCatCacheListLeakWarning(owner->catlistrefs[owner->ncatlistrefs - 1]);
-			ReleaseCatCacheList(owner->catlistrefs[owner->ncatlistrefs - 1]);
+				PrintCatCacheListLeakWarning(res);
+			ReleaseCatCacheList(res);
 		}
+
 		/* Ditto for plancache references */
-		while (owner->nplanrefs > 0)
+		while (ResourceArrayGetAny(&(owner->planrefarr), &foundres))
 		{
+			CachedPlan *res = (CachedPlan *) foundres;
+
 			if (isCommit)
-				PrintPlanCacheLeakWarning(owner->planrefs[owner->nplanrefs - 1]);
-			ReleaseCachedPlan(owner->planrefs[owner->nplanrefs - 1], true);
+				PrintPlanCacheLeakWarning(res);
+			ReleaseCachedPlan(res, true);
 		}
+
 		/* Ditto for tupdesc references */
-		while (owner->ntupdescs > 0)
+		while (ResourceArrayGetAny(&(owner->tupdescarr), &foundres))
 		{
+			TupleDesc	res = (TupleDesc) foundres;
+
 			if (isCommit)
-				PrintTupleDescLeakWarning(owner->tupdescs[owner->ntupdescs - 1]);
-			DecrTupleDescRefCount(owner->tupdescs[owner->ntupdescs - 1]);
+				PrintTupleDescLeakWarning(res);
+			DecrTupleDescRefCount(res);
 		}
+
 		/* Ditto for snapshot references */
-		while (owner->nsnapshots > 0)
+		while (ResourceArrayGetAny(&(owner->snapshotarr), &foundres))
 		{
+			Snapshot	res = (Snapshot) foundres;
+
 			if (isCommit)
-				PrintSnapshotLeakWarning(owner->snapshots[owner->nsnapshots - 1]);
-			UnregisterSnapshot(owner->snapshots[owner->nsnapshots - 1]);
+				PrintSnapshotLeakWarning(res);
+			UnregisterSnapshot(res);
 		}
 
 		/* Ditto for temporary files */
-		while (owner->nfiles > 0)
+		while (ResourceArrayGetAny(&(owner->filearr), &foundres))
 		{
+			File		res = (File) foundres;
+
 			if (isCommit)
-				PrintFileLeakWarning(owner->files[owner->nfiles - 1]);
-			FileClose(owner->files[owner->nfiles - 1]);
+				PrintFileLeakWarning(res);
+			FileClose(res);
 		}
 
 		/* Clean up index scans too */
@@ -418,16 +553,7 @@ ResourceOwnerDelete(ResourceOwner owner)
 	Assert(owner != CurrentResourceOwner);
 
 	/* And it better not own any resources, either */
-	Assert(owner->nbuffers == 0);
 	Assert(owner->nlocks == 0 || owner->nlocks == MAX_RESOWNER_LOCKS + 1);
-	Assert(owner->ncatrefs == 0);
-	Assert(owner->ncatlistrefs == 0);
-	Assert(owner->nrelrefs == 0);
-	Assert(owner->ndsms == 0);
-	Assert(owner->nplanrefs == 0);
-	Assert(owner->ntupdescs == 0);
-	Assert(owner->nsnapshots == 0);
-	Assert(owner->nfiles == 0);
 
 	/*
 	 * Delete children.  The recursive call will delink the child from me, so
@@ -444,25 +570,15 @@ ResourceOwnerDelete(ResourceOwner owner)
 	ResourceOwnerNewParent(owner, NULL);
 
 	/* And free the object. */
-	if (owner->buffers)
-		pfree(owner->buffers);
-	if (owner->catrefs)
-		pfree(owner->catrefs);
-	if (owner->catlistrefs)
-		pfree(owner->catlistrefs);
-	if (owner->relrefs)
-		pfree(owner->relrefs);
-	if (owner->planrefs)
-		pfree(owner->planrefs);
-	if (owner->tupdescs)
-		pfree(owner->tupdescs);
-	if (owner->snapshots)
-		pfree(owner->snapshots);
-	if (owner->files)
-		pfree(owner->files);
-	if (owner->dsms)
-		pfree(owner->dsms);
-
+	ResourceArrayFree(&(owner->catrefarr));
+	ResourceArrayFree(&(owner->catlistrefarr));
+	ResourceArrayFree(&(owner->relrefarr));
+	ResourceArrayFree(&(owner->planrefarr));
+	ResourceArrayFree(&(owner->tupdescarr));
+	ResourceArrayFree(&(owner->snapshotarr));
+	ResourceArrayFree(&(owner->dsmarr));
+	ResourceArrayFree(&(owner->bufferarr));
+	ResourceArrayFree(&(owner->filearr));
 	pfree(owner);
 }
 
@@ -575,26 +691,9 @@ UnregisterResourceReleaseCallback(ResourceReleaseCallback callback, void *arg)
 void
 ResourceOwnerEnlargeBuffers(ResourceOwner owner)
 {
-	int			newmax;
-
-	if (owner == NULL ||
-		owner->nbuffers < owner->maxbuffers)
-		return;					/* nothing to do */
-
-	if (owner->buffers == NULL)
-	{
-		newmax = 16;
-		owner->buffers = (Buffer *)
-			MemoryContextAlloc(TopMemoryContext, newmax * sizeof(Buffer));
-		owner->maxbuffers = newmax;
-	}
-	else
-	{
-		newmax = owner->maxbuffers * 2;
-		owner->buffers = (Buffer *)
-			repalloc(owner->buffers, newmax * sizeof(Buffer));
-		owner->maxbuffers = newmax;
-	}
+	if (owner == NULL)
+		return;
+	ResourceArrayEnlarge(&(owner->bufferarr));
 }
 
 /*
@@ -608,12 +707,9 @@ ResourceOwnerEnlargeBuffers(ResourceOwner owner)
 void
 ResourceOwnerRememberBuffer(ResourceOwner owner, Buffer buffer)
 {
-	if (owner != NULL)
-	{
-		Assert(owner->nbuffers < owner->maxbuffers);
-		owner->buffers[owner->nbuffers] = buffer;
-		owner->nbuffers++;
-	}
+	if (owner == NULL)
+		return;
+	ResourceArrayAdd(&(owner->bufferarr), (Datum) buffer);
 }
 
 /*
@@ -625,33 +721,15 @@ ResourceOwnerRememberBuffer(ResourceOwner owner, Buffer buffer)
 void
 ResourceOwnerForgetBuffer(ResourceOwner owner, Buffer buffer)
 {
-	if (owner != NULL)
-	{
-		Buffer	   *buffers = owner->buffers;
-		int			nb1 = owner->nbuffers - 1;
-		int			i;
+	bool		res;
 
-		/*
-		 * Scan back-to-front because it's more likely we are releasing a
-		 * recently pinned buffer.  This isn't always the case of course, but
-		 * it's the way to bet.
-		 */
-		for (i = nb1; i >= 0; i--)
-		{
-			if (buffers[i] == buffer)
-			{
-				while (i < nb1)
-				{
-					buffers[i] = buffers[i + 1];
-					i++;
-				}
-				owner->nbuffers = nb1;
-				return;
-			}
-		}
+	if (owner == NULL)
+		return;
+
+	res = ResourceArrayRemove(&(owner->bufferarr), (Datum) buffer);
+	if (!res)
 		elog(ERROR, "buffer %d is not owned by resource owner %s",
 			 buffer, owner->name);
-	}
 }
 
 /*
@@ -667,6 +745,8 @@ ResourceOwnerForgetBuffer(ResourceOwner owner, Buffer buffer)
 void
 ResourceOwnerRememberLock(ResourceOwner owner, LOCALLOCK *locallock)
 {
+	Assert(locallock != NULL);
+
 	if (owner->nlocks > MAX_RESOWNER_LOCKS)
 		return;					/* we have already overflowed */
 
@@ -714,25 +794,7 @@ ResourceOwnerForgetLock(ResourceOwner owner, LOCALLOCK *locallock)
 void
 ResourceOwnerEnlargeCatCacheRefs(ResourceOwner owner)
 {
-	int			newmax;
-
-	if (owner->ncatrefs < owner->maxcatrefs)
-		return;					/* nothing to do */
-
-	if (owner->catrefs == NULL)
-	{
-		newmax = 16;
-		owner->catrefs = (HeapTuple *)
-			MemoryContextAlloc(TopMemoryContext, newmax * sizeof(HeapTuple));
-		owner->maxcatrefs = newmax;
-	}
-	else
-	{
-		newmax = owner->maxcatrefs * 2;
-		owner->catrefs = (HeapTuple *)
-			repalloc(owner->catrefs, newmax * sizeof(HeapTuple));
-		owner->maxcatrefs = newmax;
-	}
+	ResourceArrayEnlarge(&(owner->catrefarr));
 }
 
 /*
@@ -743,9 +805,7 @@ ResourceOwnerEnlargeCatCacheRefs(ResourceOwner owner)
 void
 ResourceOwnerRememberCatCacheRef(ResourceOwner owner, HeapTuple tuple)
 {
-	Assert(owner->ncatrefs < owner->maxcatrefs);
-	owner->catrefs[owner->ncatrefs] = tuple;
-	owner->ncatrefs++;
+	ResourceArrayAdd(&(owner->catrefarr), (Datum) tuple);
 }
 
 /*
@@ -754,25 +814,12 @@ ResourceOwnerRememberCatCacheRef(ResourceOwner owner, HeapTuple tuple)
 void
 ResourceOwnerForgetCatCacheRef(ResourceOwner owner, HeapTuple tuple)
 {
-	HeapTuple  *catrefs = owner->catrefs;
-	int			nc1 = owner->ncatrefs - 1;
-	int			i;
+	bool		res = ResourceArrayRemove(&(owner->catrefarr),
+										  (Datum) tuple);
 
-	for (i = nc1; i >= 0; i--)
-	{
-		if (catrefs[i] == tuple)
-		{
-			while (i < nc1)
-			{
-				catrefs[i] = catrefs[i + 1];
-				i++;
-			}
-			owner->ncatrefs = nc1;
-			return;
-		}
-	}
-	elog(ERROR, "catcache reference %p is not owned by resource owner %s",
-		 tuple, owner->name);
+	if (!res)
+		elog(ERROR, "catcache reference %p is not owned by resource owner %s",
+			 tuple, owner->name);
 }
 
 /*
@@ -785,25 +832,7 @@ ResourceOwnerForgetCatCacheRef(ResourceOwner owner, HeapTuple tuple)
 void
 ResourceOwnerEnlargeCatCacheListRefs(ResourceOwner owner)
 {
-	int			newmax;
-
-	if (owner->ncatlistrefs < owner->maxcatlistrefs)
-		return;					/* nothing to do */
-
-	if (owner->catlistrefs == NULL)
-	{
-		newmax = 16;
-		owner->catlistrefs = (CatCList **)
-			MemoryContextAlloc(TopMemoryContext, newmax * sizeof(CatCList *));
-		owner->maxcatlistrefs = newmax;
-	}
-	else
-	{
-		newmax = owner->maxcatlistrefs * 2;
-		owner->catlistrefs = (CatCList **)
-			repalloc(owner->catlistrefs, newmax * sizeof(CatCList *));
-		owner->maxcatlistrefs = newmax;
-	}
+	ResourceArrayEnlarge(&(owner->catlistrefarr));
 }
 
 /*
@@ -814,9 +843,7 @@ ResourceOwnerEnlargeCatCacheListRefs(ResourceOwner owner)
 void
 ResourceOwnerRememberCatCacheListRef(ResourceOwner owner, CatCList *list)
 {
-	Assert(owner->ncatlistrefs < owner->maxcatlistrefs);
-	owner->catlistrefs[owner->ncatlistrefs] = list;
-	owner->ncatlistrefs++;
+	ResourceArrayAdd(&(owner->catlistrefarr), (Datum) list);
 }
 
 /*
@@ -825,25 +852,12 @@ ResourceOwnerRememberCatCacheListRef(ResourceOwner owner, CatCList *list)
 void
 ResourceOwnerForgetCatCacheListRef(ResourceOwner owner, CatCList *list)
 {
-	CatCList  **catlistrefs = owner->catlistrefs;
-	int			nc1 = owner->ncatlistrefs - 1;
-	int			i;
+	bool		res = ResourceArrayRemove(&(owner->catlistrefarr),
+										  (Datum) list);
 
-	for (i = nc1; i >= 0; i--)
-	{
-		if (catlistrefs[i] == list)
-		{
-			while (i < nc1)
-			{
-				catlistrefs[i] = catlistrefs[i + 1];
-				i++;
-			}
-			owner->ncatlistrefs = nc1;
-			return;
-		}
-	}
-	elog(ERROR, "catcache list reference %p is not owned by resource owner %s",
-		 list, owner->name);
+	if (!res)
+		elog(ERROR, "catcache list reference %p is not owned by resource owner %s",
+			 list, owner->name);
 }
 
 /*
@@ -856,25 +870,7 @@ ResourceOwnerForgetCatCacheListRef(ResourceOwner owner, CatCList *list)
 void
 ResourceOwnerEnlargeRelationRefs(ResourceOwner owner)
 {
-	int			newmax;
-
-	if (owner->nrelrefs < owner->maxrelrefs)
-		return;					/* nothing to do */
-
-	if (owner->relrefs == NULL)
-	{
-		newmax = 16;
-		owner->relrefs = (Relation *)
-			MemoryContextAlloc(TopMemoryContext, newmax * sizeof(Relation));
-		owner->maxrelrefs = newmax;
-	}
-	else
-	{
-		newmax = owner->maxrelrefs * 2;
-		owner->relrefs = (Relation *)
-			repalloc(owner->relrefs, newmax * sizeof(Relation));
-		owner->maxrelrefs = newmax;
-	}
+	ResourceArrayEnlarge(&(owner->relrefarr));
 }
 
 /*
@@ -885,9 +881,7 @@ ResourceOwnerEnlargeRelationRefs(ResourceOwner owner)
 void
 ResourceOwnerRememberRelationRef(ResourceOwner owner, Relation rel)
 {
-	Assert(owner->nrelrefs < owner->maxrelrefs);
-	owner->relrefs[owner->nrelrefs] = rel;
-	owner->nrelrefs++;
+	ResourceArrayAdd(&(owner->relrefarr), (Datum) rel);
 }
 
 /*
@@ -896,25 +890,12 @@ ResourceOwnerRememberRelationRef(ResourceOwner owner, Relation rel)
 void
 ResourceOwnerForgetRelationRef(ResourceOwner owner, Relation rel)
 {
-	Relation   *relrefs = owner->relrefs;
-	int			nr1 = owner->nrelrefs - 1;
-	int			i;
+	bool		res = ResourceArrayRemove(&(owner->relrefarr),
+										  (Datum) rel);
 
-	for (i = nr1; i >= 0; i--)
-	{
-		if (relrefs[i] == rel)
-		{
-			while (i < nr1)
-			{
-				relrefs[i] = relrefs[i + 1];
-				i++;
-			}
-			owner->nrelrefs = nr1;
-			return;
-		}
-	}
-	elog(ERROR, "relcache reference %s is not owned by resource owner %s",
-		 RelationGetRelationName(rel), owner->name);
+	if (!res)
+		elog(ERROR, "relcache reference %s is not owned by resource owner %s",
+			 RelationGetRelationName(rel), owner->name);
 }
 
 /*
@@ -937,25 +918,7 @@ PrintRelCacheLeakWarning(Relation rel)
 void
 ResourceOwnerEnlargePlanCacheRefs(ResourceOwner owner)
 {
-	int			newmax;
-
-	if (owner->nplanrefs < owner->maxplanrefs)
-		return;					/* nothing to do */
-
-	if (owner->planrefs == NULL)
-	{
-		newmax = 16;
-		owner->planrefs = (CachedPlan **)
-			MemoryContextAlloc(TopMemoryContext, newmax * sizeof(CachedPlan *));
-		owner->maxplanrefs = newmax;
-	}
-	else
-	{
-		newmax = owner->maxplanrefs * 2;
-		owner->planrefs = (CachedPlan **)
-			repalloc(owner->planrefs, newmax * sizeof(CachedPlan *));
-		owner->maxplanrefs = newmax;
-	}
+	ResourceArrayEnlarge(&(owner->planrefarr));
 }
 
 /*
@@ -966,9 +929,7 @@ ResourceOwnerEnlargePlanCacheRefs(ResourceOwner owner)
 void
 ResourceOwnerRememberPlanCacheRef(ResourceOwner owner, CachedPlan *plan)
 {
-	Assert(owner->nplanrefs < owner->maxplanrefs);
-	owner->planrefs[owner->nplanrefs] = plan;
-	owner->nplanrefs++;
+	ResourceArrayAdd(&(owner->planrefarr), (Datum) plan);
 }
 
 /*
@@ -977,25 +938,12 @@ ResourceOwnerRememberPlanCacheRef(ResourceOwner owner, CachedPlan *plan)
 void
 ResourceOwnerForgetPlanCacheRef(ResourceOwner owner, CachedPlan *plan)
 {
-	CachedPlan **planrefs = owner->planrefs;
-	int			np1 = owner->nplanrefs - 1;
-	int			i;
+	bool		res = ResourceArrayRemove(&(owner->planrefarr),
+										  (Datum) plan);
 
-	for (i = np1; i >= 0; i--)
-	{
-		if (planrefs[i] == plan)
-		{
-			while (i < np1)
-			{
-				planrefs[i] = planrefs[i + 1];
-				i++;
-			}
-			owner->nplanrefs = np1;
-			return;
-		}
-	}
-	elog(ERROR, "plancache reference %p is not owned by resource owner %s",
-		 plan, owner->name);
+	if (!res)
+		elog(ERROR, "plancache reference %p is not owned by resource owner %s",
+			 plan, owner->name);
 }
 
 /*
@@ -1017,25 +965,7 @@ PrintPlanCacheLeakWarning(CachedPlan *plan)
 void
 ResourceOwnerEnlargeTupleDescs(ResourceOwner owner)
 {
-	int			newmax;
-
-	if (owner->ntupdescs < owner->maxtupdescs)
-		return;					/* nothing to do */
-
-	if (owner->tupdescs == NULL)
-	{
-		newmax = 16;
-		owner->tupdescs = (TupleDesc *)
-			MemoryContextAlloc(TopMemoryContext, newmax * sizeof(TupleDesc));
-		owner->maxtupdescs = newmax;
-	}
-	else
-	{
-		newmax = owner->maxtupdescs * 2;
-		owner->tupdescs = (TupleDesc *)
-			repalloc(owner->tupdescs, newmax * sizeof(TupleDesc));
-		owner->maxtupdescs = newmax;
-	}
+	ResourceArrayEnlarge(&(owner->tupdescarr));
 }
 
 /*
@@ -1046,9 +976,7 @@ ResourceOwnerEnlargeTupleDescs(ResourceOwner owner)
 void
 ResourceOwnerRememberTupleDesc(ResourceOwner owner, TupleDesc tupdesc)
 {
-	Assert(owner->ntupdescs < owner->maxtupdescs);
-	owner->tupdescs[owner->ntupdescs] = tupdesc;
-	owner->ntupdescs++;
+	ResourceArrayAdd(&(owner->tupdescarr), (Datum) tupdesc);
 }
 
 /*
@@ -1057,25 +985,12 @@ ResourceOwnerRememberTupleDesc(ResourceOwner owner, TupleDesc tupdesc)
 void
 ResourceOwnerForgetTupleDesc(ResourceOwner owner, TupleDesc tupdesc)
 {
-	TupleDesc  *tupdescs = owner->tupdescs;
-	int			nt1 = owner->ntupdescs - 1;
-	int			i;
+	bool		res = ResourceArrayRemove(&(owner->tupdescarr),
+										  (Datum) tupdesc);
 
-	for (i = nt1; i >= 0; i--)
-	{
-		if (tupdescs[i] == tupdesc)
-		{
-			while (i < nt1)
-			{
-				tupdescs[i] = tupdescs[i + 1];
-				i++;
-			}
-			owner->ntupdescs = nt1;
-			return;
-		}
-	}
-	elog(ERROR, "tupdesc reference %p is not owned by resource owner %s",
-		 tupdesc, owner->name);
+	if (!res)
+		elog(ERROR, "tupdesc reference %p is not owned by resource owner %s",
+			 tupdesc, owner->name);
 }
 
 /*
@@ -1099,25 +1014,7 @@ PrintTupleDescLeakWarning(TupleDesc tupdesc)
 void
 ResourceOwnerEnlargeSnapshots(ResourceOwner owner)
 {
-	int			newmax;
-
-	if (owner->nsnapshots < owner->maxsnapshots)
-		return;					/* nothing to do */
-
-	if (owner->snapshots == NULL)
-	{
-		newmax = 16;
-		owner->snapshots = (Snapshot *)
-			MemoryContextAlloc(TopMemoryContext, newmax * sizeof(Snapshot));
-		owner->maxsnapshots = newmax;
-	}
-	else
-	{
-		newmax = owner->maxsnapshots * 2;
-		owner->snapshots = (Snapshot *)
-			repalloc(owner->snapshots, newmax * sizeof(Snapshot));
-		owner->maxsnapshots = newmax;
-	}
+	ResourceArrayEnlarge(&(owner->snapshotarr));
 }
 
 /*
@@ -1128,9 +1025,7 @@ ResourceOwnerEnlargeSnapshots(ResourceOwner owner)
 void
 ResourceOwnerRememberSnapshot(ResourceOwner owner, Snapshot snapshot)
 {
-	Assert(owner->nsnapshots < owner->maxsnapshots);
-	owner->snapshots[owner->nsnapshots] = snapshot;
-	owner->nsnapshots++;
+	ResourceArrayAdd(&(owner->snapshotarr), (Datum) snapshot);
 }
 
 /*
@@ -1139,25 +1034,12 @@ ResourceOwnerRememberSnapshot(ResourceOwner owner, Snapshot snapshot)
 void
 ResourceOwnerForgetSnapshot(ResourceOwner owner, Snapshot snapshot)
 {
-	Snapshot   *snapshots = owner->snapshots;
-	int			ns1 = owner->nsnapshots - 1;
-	int			i;
+	bool		res = ResourceArrayRemove(&(owner->snapshotarr),
+										  (Datum) snapshot);
 
-	for (i = ns1; i >= 0; i--)
-	{
-		if (snapshots[i] == snapshot)
-		{
-			while (i < ns1)
-			{
-				snapshots[i] = snapshots[i + 1];
-				i++;
-			}
-			owner->nsnapshots = ns1;
-			return;
-		}
-	}
-	elog(ERROR, "snapshot reference %p is not owned by resource owner %s",
-		 snapshot, owner->name);
+	if (!res)
+		elog(ERROR, "snapshot reference %p is not owned by resource owner %s",
+			 snapshot, owner->name);
 }
 
 /*
@@ -1182,25 +1064,7 @@ PrintSnapshotLeakWarning(Snapshot snapshot)
 void
 ResourceOwnerEnlargeFiles(ResourceOwner owner)
 {
-	int			newmax;
-
-	if (owner->nfiles < owner->maxfiles)
-		return;					/* nothing to do */
-
-	if (owner->files == NULL)
-	{
-		newmax = 16;
-		owner->files = (File *)
-			MemoryContextAlloc(TopMemoryContext, newmax * sizeof(File));
-		owner->maxfiles = newmax;
-	}
-	else
-	{
-		newmax = owner->maxfiles * 2;
-		owner->files = (File *)
-			repalloc(owner->files, newmax * sizeof(File));
-		owner->maxfiles = newmax;
-	}
+	ResourceArrayEnlarge(&(owner->filearr));
 }
 
 /*
@@ -1211,9 +1075,7 @@ ResourceOwnerEnlargeFiles(ResourceOwner owner)
 void
 ResourceOwnerRememberFile(ResourceOwner owner, File file)
 {
-	Assert(owner->nfiles < owner->maxfiles);
-	owner->files[owner->nfiles] = file;
-	owner->nfiles++;
+	ResourceArrayAdd(&(owner->filearr), (Datum) file);
 }
 
 /*
@@ -1222,25 +1084,12 @@ ResourceOwnerRememberFile(ResourceOwner owner, File file)
 void
 ResourceOwnerForgetFile(ResourceOwner owner, File file)
 {
-	File	   *files = owner->files;
-	int			ns1 = owner->nfiles - 1;
-	int			i;
+	bool		res = ResourceArrayRemove(&(owner->filearr),
+										  (Datum) file);
 
-	for (i = ns1; i >= 0; i--)
-	{
-		if (files[i] == file)
-		{
-			while (i < ns1)
-			{
-				files[i] = files[i + 1];
-				i++;
-			}
-			owner->nfiles = ns1;
-			return;
-		}
-	}
-	elog(ERROR, "temporery file %d is not owned by resource owner %s",
-		 file, owner->name);
+	if (!res)
+		elog(ERROR, "temporary file %d is not owned by resource owner %s",
+			 file, owner->name);
 }
 
 
@@ -1265,26 +1114,7 @@ PrintFileLeakWarning(File file)
 void
 ResourceOwnerEnlargeDSMs(ResourceOwner owner)
 {
-	int			newmax;
-
-	if (owner->ndsms < owner->maxdsms)
-		return;					/* nothing to do */
-
-	if (owner->dsms == NULL)
-	{
-		newmax = 16;
-		owner->dsms = (dsm_segment **)
-			MemoryContextAlloc(TopMemoryContext,
-							   newmax * sizeof(dsm_segment *));
-		owner->maxdsms = newmax;
-	}
-	else
-	{
-		newmax = owner->maxdsms * 2;
-		owner->dsms = (dsm_segment **)
-			repalloc(owner->dsms, newmax * sizeof(dsm_segment *));
-		owner->maxdsms = newmax;
-	}
+	ResourceArrayEnlarge(&(owner->dsmarr));
 }
 
 /*
@@ -1295,9 +1125,7 @@ ResourceOwnerEnlargeDSMs(ResourceOwner owner)
 void
 ResourceOwnerRememberDSM(ResourceOwner owner, dsm_segment *seg)
 {
-	Assert(owner->ndsms < owner->maxdsms);
-	owner->dsms[owner->ndsms] = seg;
-	owner->ndsms++;
+	ResourceArrayAdd(&(owner->dsmarr), (Datum) seg);
 }
 
 /*
@@ -1306,26 +1134,12 @@ ResourceOwnerRememberDSM(ResourceOwner owner, dsm_segment *seg)
 void
 ResourceOwnerForgetDSM(ResourceOwner owner, dsm_segment *seg)
 {
-	dsm_segment **dsms = owner->dsms;
-	int			ns1 = owner->ndsms - 1;
-	int			i;
+	bool		res = ResourceArrayRemove(&(owner->dsmarr),
+										  (Datum) seg);
 
-	for (i = ns1; i >= 0; i--)
-	{
-		if (dsms[i] == seg)
-		{
-			while (i < ns1)
-			{
-				dsms[i] = dsms[i + 1];
-				i++;
-			}
-			owner->ndsms = ns1;
-			return;
-		}
-	}
-	elog(ERROR,
-		 "dynamic shared memory segment %u is not owned by resource owner %s",
-		 dsm_segment_handle(seg), owner->name);
+	if (!res)
+		elog(ERROR, "dynamic shared memory segment %u is not owned by resource"
+			 " owner %s", dsm_segment_handle(seg), owner->name);
 }
 
 
resource-owner-optimization-v4-step2.patchtext/x-patchDownload
diff --git a/src/backend/access/hash/hashfunc.c b/src/backend/access/hash/hashfunc.c
index 9ee654e..5a00a03 100644
--- a/src/backend/access/hash/hashfunc.c
+++ b/src/backend/access/hash/hashfunc.c
@@ -297,6 +297,9 @@ hashvarlena(PG_FUNCTION_ARGS)
  * of 2.  There is no need to do mod a prime (mod is sooo slow!).
  * If you need less than 32 bits, use a bitmask.
  *
+ * This procedure never fails. Its important. Some code notably ResourceOwner
+ * relies on this.
+ *
  * Note: we could easily change this function to return a 64-bit hash value
  * by using the final values of both b and c.  b is perhaps a little less
  * well mixed than c, however.
diff --git a/src/backend/utils/resowner/resowner.c b/src/backend/utils/resowner/resowner.c
index 3330c8d..abed36c 100644
--- a/src/backend/utils/resowner/resowner.c
+++ b/src/backend/utils/resowner/resowner.c
@@ -37,29 +37,60 @@
  * which should be called before corresponding ResourceOwnerRemember* calls
  * (see below). Internally each type of resource is stored in separate
  * ResourceArray.
+ *
+ * There are two major reasons for using ResourceArray instead of, say,
+ * regular C arrays.
+ *
+ * Firstly we would like to prevent code duplication. For instance
+ * ResourceArray provides generic Remember/Forget/Enlarge procedures, so
+ * corresponding ResourceOwner* procedures are just a typesafe wrappers for
+ * these procedures.
+ *
+ * Secondly ResourceArray must be more efficient than regular C array.
+ * Current implementation in general could be considered a hash table. It has
+ * O(1) complexity of both Remember and Forget procedures.
  */
 typedef struct ResourceArray
 {
 	Datum	   *itemsarr;		/* buffer for storing values */
+	Datum		invalidval;		/* value that is considered invalid */
 	uint32		capacity;		/* capacity of array */
 	uint32		nitems;			/* how many items is stored in items array */
+	uint32		maxitems;		/* precalculated RESARRAY_MAX_ITEMS(capacity) */
+	uint32		lastidx;		/* index of last item returned by GetAny */
 }	ResourceArray;
 
 /*
  * This number is used as initial size of resource array. If given number of
  * items is not enough, we double array size and reallocate memory.
+ *
+ * Should be power of two since we use (arrsize - 1) as mask for hash value.
+ *
  */
 #define RESARRAY_INIT_SIZE 16
 
 /*
+ * How many items could be stored in a resource array of given capacity. If
+ * this number is reached we need to resize an array to prevent hash collisions.
+ *
+ * This computation actually costs only two additions and one binary shift.
+ */
+#define RESARRAY_MAX_ITEMS(capacity) ((capacity)*3/4)
+
+/*
  * Initialize ResourceArray
  */
 static void
-ResourceArrayInit(ResourceArray * resarr)
+ResourceArrayInit(ResourceArray * resarr, Datum invalidval)
 {
 	Assert(resarr->itemsarr == NULL);
 	Assert(resarr->capacity == 0);
 	Assert(resarr->nitems == 0);
+	Assert(resarr->maxitems == 0);
+	Assert(resarr->invalidval == 0);
+	Assert(resarr->lastidx == 0);
+
+	resarr->invalidval = invalidval;
 }
 
 /*
@@ -70,11 +101,24 @@ ResourceArrayInit(ResourceArray * resarr)
 static void
 ResourceArrayAdd(ResourceArray * resarr, Datum data)
 {
+	Datum		idx;
+	Datum		mask = resarr->capacity - 1;
+
+	Assert(resarr->maxitems > resarr->nitems);
 	Assert(resarr->capacity > 0);
 	Assert(resarr->itemsarr != NULL);
-	Assert(resarr->nitems < resarr->capacity);
+	Assert(data != resarr->invalidval);
+
+	idx = hash_any((void *) &data, sizeof(data)) & mask;
+
+	while (true)
+	{
+		if (resarr->itemsarr[idx] == resarr->invalidval)
+			break;
+		idx = (idx + 1) & mask;
+	}
 
-	resarr->itemsarr[resarr->nitems] = data;
+	resarr->itemsarr[idx] = data;
 	resarr->nitems++;
 }
 
@@ -86,24 +130,24 @@ ResourceArrayAdd(ResourceArray * resarr, Datum data)
 static bool
 ResourceArrayRemove(ResourceArray * resarr, Datum data)
 {
-	int			i,
-				j,
-				lastidx;
+	uint32		i;
+	Datum		idx;
+	Datum		mask = resarr->capacity - 1;
 
 	Assert(resarr->capacity > 0);
 	Assert(resarr->itemsarr != NULL);
+	Assert(data != resarr->invalidval);
 
-	lastidx = ((int) resarr->nitems) - 1;
-
-	for (i = lastidx; i >= 0; i--)
+	idx = hash_any((void *) &data, sizeof(data)) & mask;
+	for (i = 0; i < resarr->capacity; i++)
 	{
-		if (resarr->itemsarr[i] == data)
+		if (resarr->itemsarr[idx] == data)
 		{
-			for (j = i; j < lastidx; j++)
-				resarr->itemsarr[j] = resarr->itemsarr[j + 1];
+			resarr->itemsarr[idx] = resarr->invalidval;
 			resarr->nitems--;
 			return true;
 		}
+		idx = (idx + 1) & mask;
 	}
 
 	return false;
@@ -119,27 +163,33 @@ static void
 ResourceArrayEnlarge(ResourceArray * resarr)
 {
 	uint32		i,
-				oldcap,
-				oldnitems;
+				oldcap;
 	Datum	   *olditemsarr;
 
-	if (resarr->nitems < resarr->capacity)
+	if (resarr->nitems < resarr->maxitems)
 		return;					/* nothing to do */
 
 	olditemsarr = resarr->itemsarr;
 	oldcap = resarr->capacity;
-	oldnitems = resarr->nitems;
 
 	resarr->capacity = oldcap > 0 ? oldcap * 2 : RESARRAY_INIT_SIZE;
 	resarr->itemsarr = (Datum *)
 		MemoryContextAlloc(TopMemoryContext,
 						   resarr->capacity * sizeof(Datum));
+	resarr->maxitems = RESARRAY_MAX_ITEMS(resarr->capacity);
 	resarr->nitems = 0;
 
+	for (i = 0; i < resarr->capacity; i++)
+		resarr->itemsarr[i] = resarr->invalidval;
+
 	if (olditemsarr != NULL)
 	{
-		for (i = 0; i < oldnitems; i++)
-			ResourceArrayAdd(resarr, olditemsarr[i]);
+		while (oldcap > 0)
+		{
+			oldcap--;
+			if (olditemsarr[oldcap] != resarr->invalidval)
+				ResourceArrayAdd(resarr, olditemsarr[oldcap]);
+		}
 		pfree(olditemsarr);
 	}
 }
@@ -152,12 +202,24 @@ ResourceArrayEnlarge(ResourceArray * resarr)
 static bool
 ResourceArrayGetAny(ResourceArray * resarr, Datum *out)
 {
+	uint32		mask;
+
 	if (resarr->nitems == 0)
 		return false;
 
 	Assert(resarr->capacity > 0);
+	mask = resarr->capacity - 1;
+
+	for (;;)
+	{
+		resarr->lastidx = resarr->lastidx & mask;
+		if (resarr->itemsarr[resarr->lastidx] != resarr->invalidval)
+			break;
+
+		resarr->lastidx++;
+	}
 
-	*out = resarr->itemsarr[resarr->nitems - 1];
+	*out = resarr->itemsarr[resarr->lastidx];
 	return true;
 }
 
@@ -170,6 +232,7 @@ ResourceArrayFree(ResourceArray * resarr)
 	Assert(resarr->nitems == 0);
 
 	resarr->capacity = 0;
+	resarr->maxitems = 0;
 
 	if (!resarr->itemsarr)
 		return;
@@ -287,15 +350,15 @@ ResourceOwnerCreate(ResourceOwner parent, const char *name)
 		parent->firstchild = owner;
 	}
 
-	ResourceArrayInit(&(owner->catrefarr));
-	ResourceArrayInit(&(owner->catlistrefarr));
-	ResourceArrayInit(&(owner->relrefarr));
-	ResourceArrayInit(&(owner->planrefarr));
-	ResourceArrayInit(&(owner->tupdescarr));
-	ResourceArrayInit(&(owner->snapshotarr));
-	ResourceArrayInit(&(owner->dsmarr));
-	ResourceArrayInit(&(owner->bufferarr));
-	ResourceArrayInit(&(owner->filearr));
+	ResourceArrayInit(&(owner->catrefarr), (Datum) (NULL));
+	ResourceArrayInit(&(owner->catlistrefarr), (Datum) (NULL));
+	ResourceArrayInit(&(owner->relrefarr), (Datum) (NULL));
+	ResourceArrayInit(&(owner->planrefarr), (Datum) (NULL));
+	ResourceArrayInit(&(owner->tupdescarr), (Datum) (NULL));
+	ResourceArrayInit(&(owner->snapshotarr), (Datum) (NULL));
+	ResourceArrayInit(&(owner->dsmarr), (Datum) (NULL));
+	ResourceArrayInit(&(owner->bufferarr), (Datum) (InvalidBuffer));
+	ResourceArrayInit(&(owner->filearr), (Datum) (-1));
 
 	return owner;
 }
#14Stas Kelvich
s.kelvich@postgrespro.ru
In reply to: Aleksander Alekseev (#13)
2 attachment(s)
Re: Patch: ResourceOwner optimization for tables with many partitions

Hello.

I have applied this patch and can confirm ~10% speedup by this patch in presence of big amount of inherited tables.

Test case was as suggested by Aleksander: create 1000 inherited tables, no constraints, insert a row in each one, and issue single row queries over this table.

Xeon-based server 12C/24T, 50 connections, 30-min average:

TPS, no patch: 393 tps
TPS, with patch: 441 tps

The same setup but with single table with 1000 rows give performance about 188_000 tps, so overall speed of requests over a inherited table in this scenario is quite pathological (probably this is expected because database just execute 1000 selects to each inherited table). I've also tried to set range constraints for all inherited tables, so only one table was affected by query, but planning time increased a lot and total tps was again about 500 tps.

Also attaching two flame graphs measured during tests. It’s clearly visible that PortalDrop takes x4 less time after patch.

Stas.

Attachments:

inher-patched.svg.gzapplication/x-gzip; name=inher-patched.svg.gzDownload
�p5�Vinher-patched-2.svg���{������W�27����>P(X���c����r4��&����^��������=��Bd
PGR�v���D�����������z�m�����_^�e|���b�.��}����p��~��������������E��v�������}������o�����\���?-���y��?^-������7o�by�n���*�w�U�^�_oz��r��������`W��������1��]���k��J2y�8���b���f�i^����W�������/��E���,�+�������W�x�K~�����]/dY��1�����?_|�-v���m������b������_.���?��O��4w�����{s]�`��mU���1b���9T�m��uc
-�X�e�V�Y7�vs�����?�|,v�mY/���.o���?��wp�eQ����r�,6`�k����p���O���`k0G��#7������9���W���������������K�V������k���k3��������g\���\4OGX�M���Y�5�����i��o{wK-�7\�:�������|����Y7���������o����)�/�K���k����^�\���������?~��W����E�X�M������;^�]7�%��fuW���&���������X�+n���{��b}X�v������z[���������%W��7��n��m��/���������5��'���zG���-��������cq������[^�5���Mm���~��O?������p������r��9�R���%��|m}wx�O�������!�o�}�g�-��TlO%������~��Z|f.`�~�����^a��������<������cn�}�O?y�����e�^�����n�}Y���/�������+|$|���%��-\g���_.������x��R>-��X�_����wR.�U���cyS���+c�����:�����q����s{��7p��
���c��=|�p�4M�^W�y����li~��pg5^��
_��2�b���}�r[�o��/6�}��9>���_7[6����3~����*�S���X���8zJ��r�z���w�6�'8������{���v���
��>[�js�����U���>b�
.q���_}f����������_N������7���>!��\���������O�zVt��F���c~��?to�o{�q����l�<���?o���.�g��`��]5�f[���v������w�v\^;����3���?m�f�v���������,��y���������y:����x���3V��i?���a�����[���h^U�/�8����y��\�;e]����J8
�'������^��7�����{�ft����*���<���3s/��#����n�[4w����������a������L��<,~���a�7�����i���>����U���r�_�����o�B88�o���^w�7�k�������W���x���t�p��9_�~dw<��}x=�����������/���C����G��.���O�>�
.�O��O��y#�����Z.[�������v�����`=��yU�A5v|�o��)jhtP�jh�������.����S���F��*Z����8.��g�5x�����>�~�<F�
�{�'~y�p������m���7����`�}�U����?�^���uX,n?zs����dY|���c��h`{j��
W��r�;�<�0X
�
1:��G��{+����Xe��p��*���v!}h�����������5����o��!�X�?.����y1�b�o�������������4�����v�]��m�z`�������1�����/�m��W?:^Y<�_i��Y{��eO�����"p�|�k�o	|38o�K��������v�����05�i�����]����e��>����f��h��6P[����q��#��=�&/n_����
KS����X�6
<':<,}
>���'����V�����a�d���<��]�������H��g�d��o_���U/~��[��� �9Tv����b�����m��������f
�[�.io�����>����[���3���x�X��lH������������l�)�^9�y%�������}�a��n�C�����z���=0z}����7��������?�xsu��J`l���v��t��!��t��WgC�'��I��x�6���\i�:�vpX��Fkm�,��Y��>h��F�H�k~�,l�k��9)F�����~}��+��/:\gCW��C�i���au��y������`�o���o;�b�����v�V4������i�����,���\� ������f��^�~��>�����?�e��#��A����}���X����}i��WUy[>����Z��]Azx(���������d���O����k�z�L�z����!86���w��J�Xy��|~��
�/}������*��r��C�z��o�}�sI_�'�Cy�������
�/oo��>������j��G6��g~��5���qW����������p�2W�$o�m��E)�+3�3>k��c.��h��`s6we��������*��r�����w�#�W�a����wUy��W��.��������V��fx��j|��S�\1�=�����s\� M�����������D������_P,����
����y��5���F~�0�.�������/�,K����_��;���M������<�����?�lQ�������n����W\�Q�?__�w�7X�~�]���������>>/�v.{�w4�'����+�7�����PmX��g@uv}x6�}p���!{��
���8ms�O�M����?�	��'g�������=����V�u�$����������n6�J������M+�p{0�/�8ip�|���y���|�����*�^�}�=����������������:�M6O��_��?ly<�w�C_�s�t���*8$���\�p�v����'�'���.Q����������_��\-����V����}X��|��D�������/�������P����]��]S�� �����{���)�����[l�_�����t�^�iw�{8v��y�1��Z��r�#��r���<�����,1�0&o��pW����i������o�W�������C�e�f4��I=;[������������x�g������v����.����h������~����x���_;���t6����h<
���o���?��������"�,��\�������-�s���<��W���^a������c���W���s?lpG�
��9�������<R�;����_�h����
�-����c�� ����:���]�?�����Tm_��3P���a�3
���m���n�^o�+����k��%|������J�o��f���Z��^
�:����cc�1�{�s^�\��WL�t|I�I�XL�}���kv	�����/^�F|�_����p�/�x���y�x��g��=w`�g����bi6����NM�����,�s<.{�����<�/]&3\��"|i��g�h�c�F�~.u ��4_,n��?��
.�@�"�{���m^���4��i�e&i���%?of���r4���H&p+|��z2�ro���:b�T{!�.��m���{�����e�����d{����`F>�#g;f�NE�1���v�i�Ri��>E�b����4�|}��h�!^B�V7�,u�x�F��mV����&��vn$8��j�u(�L:��cC
�!9<_�>^*����<b"���<��I�>��nGHt��%�|_>������\���k�)����f"#�����N��2fmn�*���'\�l��d)2�h����i�j����t��zB�~�&�7����!��<�g1��w�\����Y�xI��+%��=�e�]�-(�X��S��m�>��I��I��oL�F<QJ����(�������S�_W��W2�C��s92�l��TIoC=iC	c&#Ir��q��]�+������ Z�]�XW�5�e�.����c]N��|x[\�l��Iu(+3���m�����*?�=,Q�����r4f��x�,���a��'4�K�n������.�����b���=/�UY�dQ��NM�`�Q�N���u�\�}�����IDZE�t�f���.��.�3=K2�G��Y�$�����L�I�3�j�K�fB^|��m����`G1kG����z�m��uI���+)����}�,��<V7u^C� ���a@�rK����p)���Y��K-BqK?���T����e��~�y���y�q�8S�a\.U������1!m�<���\fK��b�<o�������JpB,��x�n���3|�!��'.S,�����7=D�����!�_Al����z�j���N�&�ZTWg6�:!{�����hv��������,��Rw�-��\i�e�?�_g�/e8�RU<��q����������|��Y��(���H-Y�����45U�1f���y?��?�{<?�N��M\9})}�"�L��x�c�F�02z��~~�X�����v��m~|:��T����an4�F�;9�Bu�_���o�ti�G�T�x�tN>����9�Wy�z�3�p�"^��E�����&��|!�I��40W4���;5��>���2�T�s	�3�$Lf�8�|���|]��G�) 3J�������D��tb
H�T�(���������=���	�q�d����@�����A��,�?��r��fS�l���������T	�����CN�DF�T�T�\��������Te����2��<��Fi�t�ra;��RN&AL�b)E�0��P�J���o�l��U�t����.�����%y�&7:<�i'�R�_���'�(g�j{�!�_/^��	v8bWX$Q���GLS��l��	L���R�K�!�\�=#�S�$��H��JD�eI(�����������HS������<�Rp�$+��R�P�5�BO[/���{�?ZRp�7�R����@����J�Io�����W��13��&Wc��_Z����2�C	��Bx���1�^�R�����B<Ku!6�(�,bJG�t:�L/3J]��!�xA:��\i�T
�A��2����Q���|1#���.��`��rhP5�7�1������k);t{�E�
s��.�$Q]�3^�S0�P��zH��BGL���M�We��3��^��� �����G���u1�����.o�!�9�����V��UGIF�3�0S�N��M�u4��k[1L�03�*�r1�u52�Cm$��S�r���<�yc���ZTEv���7��I���6� ���F���n�v-*p/|��0l�(�Z�
|��L�')�%-�4��hDf��y�����5d��:�{���G����t����eu�b���P�e��kII|:�w�f8�7��PV�i��R'��
'���I!m�{��1a	%��F�l)y(��z[�����|��D�B���8eE��0X��N��#��"��t�L6[�I$�3���c����L�\
�:L�m������X�S��H	*T N!��C9������6wh&!k;�DT\I,YJ��h�!$3����9�i����,���$/��`�:$��
x�������L�7t��Y�7(�j&c���">.��Q��D�%B�����4�\��pA+��'���1�1��9�Hc�����N�C����@*##ID�b�C��i	l�=�f����M���W,��p��`�
,��I�x���s�1#��*��TJ�������%�6�����)cv���g�"��0�Sj��/����	W�c^���^C�R�v������,��$'��R;,@P�:�z�~�s:����4�X�`4Lt_�`��9r�5
{ l�b����<�NMo��e�mp��)�r����<.��n���p�`$�r�8�����<q*\������������77�h�{_�t�P�:��+KU�vvb	�,�L- #��i}���fs�����W�1U��,	�6e)q�>U	�����!|����;|�G�Q�4c�T_"����$�~��E�d��r�o�z��f����
��O�'d�iL���%[t�xs���>�nB"���q9��
�H��[����P�7�k;d���/:��7�\�1�O�L�r��G;{<�(c�K�(�4�l�t�p_������GG�p�]�������3�#�b*�E+���4����
���������V:�o&� J"I��_j����.�,DCp��O�B,�DJ���)�D��5-IX�67s5��K�9���C�V������7�};����hK14�#'�,J8u�	�
LY�Np4��������zlj���8��:�TB�	Le0�8�S �lU���#���M	����U��
%����t�F�l���b)N��V�*��"�g
�Y����:Vz)�P7�N77eU{�&�� �7�(fP�� �8��B������F\�-*�9��t�z	�3b�W���v���^"a�����i����'��<���6�L�����+A��B���C^�[�����)�+G/wfL9cd�j��4���E��������q1�u<�hW��X����|���~8+i�
���/\�_V���	��/v�����@����	Q�����>�{�Q�v6�/�b���ptG*#�H`��t(�{��1�`���<U�d^u�>����UU<Q��_~���fg�or��o����5����"J��l�������`��[��u�z��I1:=g5)�sF����b��3��X�p�c�*��ne�R
1>�'���pjxaX������[O�j���QI$c�]��u��&���
�t	&���(��������uQ=L���N��!i��p����S=�c��=udg����zD�=E���t)u(s���N�
�U�/���ciW�?���f$T�:���`��f������V�c���(&�R�����T^�������Yn�M��gyC[�y�c�J3*V=�K���u0�4���n�f��
g�'fv�t�G��i�5��82��R��J3�>�G�s��c�+���B���<� ��p�_�B���;�w��|��!Y�ak�W���G���2��b��u^jni�8�� �)F$��<�P�p�/�� ,Z��#�cJMV�B����{���#b�e�ww�f}x����r\n�N�dK1�?���IiX�4y(8��_�/��x��(��W����D��1��U*�
��iJK���Q�z��rl�KoG�2����!I�Q�%`����b��C#Cd%(������q'�`V����Hk#*`�������]N��L�i�>Ga��
��4J��FIp�MN�A��4^�TdKL����V(�����m�*m���Lb�v�,�i4Ve����f�gX�x�{�g)��t��Yz�m�>���fF��<#R�`=��*;���������d�C�"ZeSc2�����Q�������<���g��`����W8�/��DH8ur�!��LH
1�]P�S�Oy��.0f�YM2��x)"
hh�T������_�y~Sw���*�~�Q�d����Fv��p%i����{,G���{+s�V��Kj �`&z�.d�j6��v �c��C��8��P�1�*�2�����/�7\�6��cWj��g��B�H&�Z���G�O��a�R�J�k��b3����2p`N�[H����t�����GY6{B"7�p4%&AXH�1*-���P��w���[Cx�||��
)�,"������i�c���;U����D�|zh!�T(��2�r��M���qE��$��Vz��1)I6I���P�����*!�9�7x<�c��+4d���wz*��Qm'��������<s�M� ETT�z��^qdnx�:�t,W�q�d`q��|h�YA-�J?4����[�4{(��.Y�	�!�T��e��Qf4�ZwE�D�;�>�`��}76G��!�J�/E^~\>���Xn����y[F�*B��$���[����]��Q��/����G��9f�l���(�����,������S�Cr�>��9���9���\~@�N��A�14��\;Z�����Z����9MKazJ�h�AV%C9/1H��;�����dD7r��y�*F�#It=*��l�����{< ��O+�9t;	PUQ�������	��}����>�bZ��n���y1no��g��$1:W*����8��G�r�'��IRQ#M��
��8���j6�1��5�8�� Q����ag��p������6�<c%��.)c%��GV?��_�p���R=R�N�QMpi5^�9�,�<�v��#�+m}��u_J�1�8~G#���1>�x���7�V'�Ge�c4�yJ��N�C����[�N�z3U�Bs��2�v�������f�c5��3�&c��R��,Fx�v�i���}K��=`wG�"t����[��a�=�p��$�'�~<����g�������(�h�O�P]��G0��,�
([�IS�eN���F���,���,���f�n��|B�b�I |��`E���s+X�������z���QP���N�$5����n�5s�!���G�
x(o������������?��f�����>�,����I{Yk��G�$R7��l�P���H�y!��#��eF���(--m�N�����p:���n�9S�yt���c��$�WE��
S�)2���������&?T9��������}�3�ZM��������RHp:����@l�+�+����Vg��`��������|������a������s*,�-Q?�]��'�����L*"���>� wr�-�c^�7SN>�������*3���>�������a�u����l;Y�lh�\���?��r*���N{�lk���40�)h��U��� ������_��9UE2�"�i_��IS8���������%}����y�_���]���*%"u�ZN��p*&28��d�26q�x������1��L5�!�Ly�����S+�}�F��n
���<� ��g��ur��4#N�e\�a��L7��c���t�M��$F���j���P�L�=�U��Lh%l�>2�$*KF�J9��2���m�M�x��kp5��2�Q�>&J�jX�Y
�e�|3_�����(�.�yz����|3(���4N[��#JP���s��86L
���*�<v�B��at�Y�G��P	)G�8G�	
�f�SJ���~]T���-�{�,��W3����}q���;>���]7�5��'�����c�����#���-�����&��v9S�=A�=�\�G��6��HQY��eqW6 `V��S�i���c	�����B
K6����@8�KFh�	����O����&�t�B[ ���m*@7F�����PJ�31�B��eH��I��Jii���0����3U�q��M+&����%��H�p�HG�he>���2���H 
m��Q.?�rOKK�=�
N���(#��dj�B�M/���vfO63W}�h����L+c'c4,�����'���-QG|����w��!3�#�8#&�vL����H1"�(��R�)b.���:T��|�J��*%���*e��FNe.���q(gc7���y^�M,q�4��7��cF\���&����������t��U	�d��X0�h�E���G�85,g\>���SY=��C��0<(�A�*b�q�h~�TO#4t��%���f���=z����@�F^��D�����U�c���D��O�)�����������}���k�\f
a:����t0��#��|�(w�L%��4�B0v�$f����j���78����"bG�9mI���=�O=� ��lp,���[Hy�G��}��)��)���$�D�"�{���W����rX��O;B���8����8"��z��Q�
W������������-b2U9��Z�cs���c��c��@��zUm��L�F�[9Gr1�{Q�H'�kH�$��	�I������	���w������!�`�1Hu���#A�j`���_%F�(�Os���0�X@�}��,�:!�0��D-& �B�OI>K����9��
}h��X�`F�nK�D7��U��}��L�����	���*&#�F�����4�67�zQ���E��n
��Sf���2;.$��Y�e_*��<��_~s�y�{~_j� ���4�����`B��[��[||8%��W727�������dL ,�������rm��N�y%��
��U���[GL+j�����6T�����i)X���.��4[��$����J����c`�kir�Y����T�6k����a4@�PP�f����e����H�)�'D�
,������zb����O�5JY�NU���1�m��R�/���Oi�]@�k�m�����Cn��~(o���DjW'%B�%iu*�\&�q;z�2#G���D$h����,�������d�Sj�� [����[&�	��+�p�}Qm�k3���zS7�=��< �,L����K�39��?CB�`��C�����
���,���������,E	�R��\KG��p�-KlMcX"���:J�q:\HYR�JD�}cJ���,�v��0���jFf'��x
��Pl���6o�U�������xd��0���J:qAf=�H"U�-�P������:�~�W�}�T�f�����$�V�|��U�����oq"����M�����\.?a����^w�&���RJ=��r�L`A��uf�L"������g}V����bs�j�|s<LSIds3��]��@n��}����y�\9����Q��=Rv�����r�&G1����THa�#D�� ��0	Y�zn���CI�g���o���b=�U�rg��$��rf��0�f�.��u��J��1���ZBJf����@;3}5hP��O�Y���y]����1U0J�(��)����_�@������[�7!����va��d�H��xI��T���`h��K;2�Yfp�,(4+���$�ynDLC��Q�uW��^z(�)g�mr�?��-`w<:��^�w��%����b���`��)u�����pT���~��g��!{<�jb�D	CiA�p(������]����������R�M;$�����`�������a8-W8y����Ki��o}P&�`uCb`��g��f�bK�c �)l�^���O��9�U%�,�~���U�����]+?|��>Y�&&��H�m{�Z��*zx`�������b)�6�a��:�O���O;��f�����{J����|7Y����"ZG���2���*������9���2�Y��&���q����]~T�C��v���]�o~�oQ�L�F�����B
��1b�!��m�T}%��!1y^7����u]�����O�^���gN���el�����g���gO�����!���g*y���/y0-��%yWax���t�gS�&���KA���&e:��M�?T����������B��T�J�S�4�Y��~W���-�"�&����GS�S�D��gT}C�;Z��w��5�D���xC�j��Ii}��9���4�HqQ������;��Q�s��cj7���$�C��$��K���L/�`�qZ�jJH��
���bp���9R����&%Cm�6�,�������Z��U�M�U�K��"�1��$���tJ�Ouf��uv�n\�8Pu4`��������H"��iew��+�B���f_5
���k�u[����f��&mZ%�H��2������OG��Q�	���2V&+�)�d�Z�`�j��l���e��m��F/�4-��8������pL���H�v��������TY�F���d2
��&'��J�)���3�mq<�{-���vR/�.������L����9�.{7��]����x��k���
�C�2���Xm���Oe�3��9�e
rOi
4���B�j��G8k���WL?R�|M��s0�����112�J.E@���������������.�q�G����d)|1��.���E���:�`}z�w$b��I%Mp71V��%�CI�����|u���lqdO������R���Gq�ib��1��������A%C�,�h�.NKr�J��N�`=�(�l�pV��T����}��E�b�zh����9 �lk��jI��b��P0�g��I�,��,�zDLt=
el����)���%��gn�������h<Yj
��q����+Ob����f5�	>�$*+%I(�;��mq�.}�����98��&Tv�V��4p_��d\���88���z�f�e65U��k���_~M
��o
au^�N~�;N�#_�AU����
�q��7���)�������7�3�E'$s��%� �Y���L���!����I��j2��bB4%���	 �����G�w[R�y��Jz�L���O0Yd�)�~I0u�����6yU���*=kZ�kBE�"�%(:�0�Q�N{��\�d���fD���D1��Zb7��y�1�����r��	��,���R�9]�7��?��_����A�KH�z�TO^S�<���c���f-&������Xn�@�������=�?GI#������&�B�i�o��3����
1�4������f��6�����7��C�z�S�I�p�_�]EiIR
��D�RQ����r��=A���=FWM�,bH�A��).(��-�b{�5��o���)�3l�*��e���vd���Nb}y$2�?�2����nF���[�n���D��X�@H1�$�G��;##���6����[9�g�?��IdK�C��o�es��9&���:	o�	e$�h�$�����\�������"R/f���)#�,B�QZ�3����P7�9Z��=�R�
���[4l6����t�~A�������|��I���?l�����}��=�L����f�4�&)�
��<����2��dg��8�2W���b�<�
5�SO5��2gi�?��.p��1�k5��s_�I���a01���$v�����.�Y�$��
 ����Pn��.��y�����HI&�:i���]��w�z�$7�HjRq��-�*�,9�JL�� �����S?P/��rd��M=�q��KK���3]W�z����v��N��0k��r��t��v@�S�:Z�b��i�r!m��[mB�����D�&[3`��z����x���[X�r6FG����B3�F)��@�, r���mn3�lB"K?�x5&%��f�}6)����p2e@X(�����>>6yS���M��|� �?Q����&f��02�T<����G�� 6�n���c|�^��?�5)��RA,c��b��h�7G��C���h
?E3�&���@P��>�W�
`u�Z�����"N�0i�]fQ'&i�[�Q�B��WU����m�������E1�r�'u�Jhe=�p�QO7����������y�K�7�������D��h�M��@��Q��H��D(-���y�7H���j�1c�y��~a�~Q�L��"R�Vk���������
�^L�X�N`��M� ��f"'���@��C'V��0t�o*c:*��pz��i�i���Fu��|�4^���NV�IjPe��F@�qUPw{^6���0j�Q6�J���}J<���C�M�&����8!�i����a��'��8�G���G��y�Dd)����"��\�C�!�M<���^�^��{)��$�U�*f�pR7(�P@.~x�S��������(�Q�h�&'O��6FW�Q���E��pb���f
�q��d��P�Y��pjFr����+�e�C���4�?��,fc��5���� 7r!�]�u��*��V���kO�%���#��8.���z��/fxA7��������J6>9R& ��"A�^�p�L�W-�%�
*���'���#�N�WF�4/��`r�o�e��	d��;g����e��p�/���s~8����8�/������� ��:	��QFS���SU��(|���U�j��6/�[�)�^0�tx�Q�������t��e�p,kx�a����0���h��3+��������F�_��B���N
��H�x���X[����3����zh�;��s����&=�A�:5�i�*9��P���t�>�k������(�%V]��($[�sR�GBt�P*���x������msnv�#�	ht����`jg#��wM����T��~���e�&cH�����J}&���UiT������������1�J 1������}����������)1��i2Ll�M/,��/j"�����nL,�IdEf�J����GIn����P<x�Yw�]��e�V,h�ma������v�?=��1[Z�<qX2g�[�ni�L�$�E*<-)&B�����^��I�����|_�����n^�k�t��&��q>�Z��+�!�P ���E3����Y
n$bRe��q0L�:7��S�����d�3
����� 2L��F�y������W-R`�����^o4p;��N���
�r��;�Zh�_��8
��h���T�Gs,�];g<U63��M4��$�;L����y��SeJR�>v���xZ"'�(��=n-�����/dh��z��SSu.d\~G�
���_�W��r�z��!-��$�"�>�	���������S�
Zu�;5Es@�=:-��VtN�Gt9��%�(���z�Iy8X���A^K�us,�m��1����9�.rJ>-o�
�%�(W����U�q�&����}�eoX�G���%b'�ejz�e�QN�lb��Pi[9��������
rK8!���&]6����-�FM�8�C�K�d*��YS���P�P	����w~��C��w�/�A�H�>J&�N��|)w2/b$�C�d�(�Bix,����*=��uY����
������Js�%����b�X��E�`��b����n��(%�����c���$|L�Ci���6=Q�>_��������<K�+��
������[A��GN�km�wF*����#��D���m@!��8A�����yx4�aU��pVv4����q�Fl���,��G��f���e�Y���E�b=�F�a'nU�%�!�������fV�9.`�}���7�]���������0F��>9MR��p~�`������u�lvFh��Sn��}��P����%����O.~G������u)s�%p�i'��d�##M5#��Z@����me��'Cg=O���e] L�M�f����L{�����L��G������n�
M3�@6��,bM]/�4��X�N�b_�=�����3������U�>B!5�Q�t'���^ �A�,P~�"��{���2{`���e�9l�P�F��q)��i��U����B%�%#W�����2�����]Q	N��MD$Q���zHl�@�!w1����{��gN���p\��	�=Y_\8�a���v
%��8�I�&��5�A���b�Y:+
1�8Y�~��"��?�G0��l�����Oo��������:���9q BB�7ZA(���@�e���N���^J(����I<L�i�"�����i��-�����Y�����A,��!�A&���J��uJ����8?��s5Nz��6Q�VJ��X�������>������%=�4e�UR���������K���B�d�#["�*L�=�r}�%0Nm&�8��Cy�������b� �)��t�d
���x��>�0�Lii'Q��3�2���$v���������X*
���r��8~<�F�M�������''���4�q���&�.w0
y4���j��*��!��s��T�X �����bOZ�7������n��CVG#����I��)���Q����]T6]�v�_r��-��)�T��]�<K�����J�f?��:N��D���������e����������>���ui����G�R27bY�v����g'�4?�(bm�����,B�Y�5��?.�����T�PD�}^�_��8�2>x�8����h &z��OqPF�.w�#x�q��U�I��(G�lj���	" "2���y���ln�0?� �I�c8���1.�Z���L�B�U�c4��5�=V�UY�9�������1�~���	K�@�b$��:�v���X���bi�s����H�6��R��2._��������K�#;�W��bf����z�F�N��b���^~��4�|#a'���h�-4���`��P"�IE6����U�z��dH�2I
�����I�KmI(U���}� )�y����b�<�5����1����� -��F�b��/��Uf���������B^G�d�,�P �)�X7c���H�Q�79;!���XE6��c��@��&'�u����eV�>����I���T�\��|ef����4N������D9(}��Y�����s�tJFH:�\��	V(���I������%\}����zj{Ac$��X8��q�.\H-�8,�3J�q��nd���O�3f�A��g�~���97�G#�'m�Af��\�����C���rxK�3��,��Io�*gQ��D:Va+�*Y9o09��0G���8���^~���2AW�[�W��>��y�kIe��g����{X�m�e(��z;xf�������)�����G,i6_���	2�q��R�����T<i(b_-�Y_;��%��;eGi�����<�2����I(!e��l7����II/%{i�q�}���X8vF+�K�e(e�{��@6�N*��kHI2s��I��hj1��;�F�gf�4�����.\������Z�8w��H�e������"��p\�����|y6�����7���R6]r�#��
�-f�����r�)b���L���R��^/�� ��5�/����*��`��}y����C��i�q~f��_� r���PeO���
�p���	���@��|5���g>����1�D�
�z&B��[��^���X��H�Ej+�#?3���%(h5J�N���w�$f��K0��v�EMR&�-�5����F�0{����Z�����7r��S;1���o���u4���pL�D�:�(����8"3��`����Q���#�3]�T��� �{���������i_v�^��z���Z�S�dk��\��DX��:��y���lNG��T!��q�l0o�k���ys8S�4��nD�jDyQ�)�:'�g*X"��>
���H#�m�1���<��5�4U��D(yj��/������|](���y��g�B����PC��BA�kB\��EH�f�\|�u��%�)4��6{��S����r��A�Q�v
�b*k�����^���|w��
����Q�))���
�p��k"J��
�{����x4?�����>�P>�'r*kT?����h�]a��W���w�h��*
�
j?�������;���@����	s��p��LS��l	J�����H\�Vd2k�������������0������:�Z�f��KT��v�<�m�1���et��]6p���M�p�~�Cq_��1��m���c�:.\�WC`Tbr���!�������=�4RN�1�-#FS�tU�P�H�o7{U�N�����	#D�&R�+��Ba�x�I��.� �Ec2Y'�m��V��D5���0��+�6�r�t��#����T�X8�������p^{0�i�&��Nm#��O�$"��Yl�WH'���8����S�I�/�p7L�l&b��P��\�582W�������s36�L��v'4$��0�8x��l�b��r����)���1����
 ��,������Y;b���p�Uo����0�O@�H���$=3�YD
�$]�`���p�Q��R��
�d�oM���`�������y��j[� ��#�Q��0	����������# n���:,A�h��������u}p�a���Wp�ig���\Q�(Ii�&��H�1�}	��!�H�'J��%i���I=�7��(QP���N�%	'����q�N�y�A�\�d&	��L�D���%3���I5|r������w��+���	xr&������s���Mci]�����!��
'���8]�_G������8r���v�~<�4#���HR��D���i����Ae�P�w�a�*�5�s�^��c���L�2
A��7F�^e���]c��'�
�$��~�421�j#&B���j{�=��bf��o��qJ-��pwN(���w2���'7�i(��[��&��n�!�9�~��8��-����N)_YJ��y�h���=>H��+���Sa�&��uU?�LD}8U����*��Z��� !��$�45�W�<tJ������&s�OfV�z���$r]��8F�c���������
�>����3���;�EW-��J����=���h�jWT���kj9@_J��S�T!�T���$\��tl��x������f�RrM��e�CI�_�=�T�2q���������� 7����0y�|�2�������4��Y� �
%`!\����������kI91��)`M�I}U{�U��+�2#3^�DK�B��>guW�%��8rU���P%��~3��UD�+-��
�?�����&0�V���D%�um[��,�l����M���V%D����.#qi�pnc���L���X�m�C`o��X������_�H�>"���7K�=<'����TF�ae����o/�/'Sf����`F$-�����1L���aqr���LM$[r�_*B�����(���l��:�F���8��2�	G/cF�����~��C2�]�dG�������/%e�?y�����)g2��7�Z�ZgK�BY��`UT��i�)3�:v&]��R"��a�6���!8��M�������u�:D&<1.���� PY�K�/~c?�"|(�������'�R�2���3R��T��RX�I(���m��[���a$9V��R,^�lWy�K7&�����9����%�P�68?~�g�j�{p�e�����
���q*����������#��n��L�[.�����#�#7h���#�f���->�X
�\n�@m���	��M�E��H�$���S�
n94�'��L1��N]L��H_�*72*�
Q�Y O.~�[���x}�C�-P@�+!�����f��	N��B��@��Y)�,'F]���jp<�&/���20��U�oQ�6dg�z��z�1�FUa�Hh��	���*mt��X�DXY�������'�r(�����������V(ltXA[&K�TbOzt�@�Z�����<��������/T������c��M�2�g>u�1�_�i~�*
��SOB){��H\�]o��d4P�\����KF
����6����a[�cz	�����w�-�����^��Ui��C(�9�U��*�cS�@����?t�O���Xf&�d���d^�g��N���!��?f���V��%�wu$c��D���� ��xb
�]�����0v�sq_Zm����? C���������kH��i�7�+4
Q�m�@~�\�}�f��F�I�$�`s�B&�R�����T���%=[
�,�i�w�dKL-iD�7�*�N���)?�2R4�0K�{��li�"^O����q��":8BE�c����lN&���49Q��	Z�\*f7t��[��wL��nm��?,#�T������`D�U]on��V��/���}��9xQ��^Y|XC��+�E����������G�k���$�>'^�Ms��$R�X����4�Ih;9U����Sy*!��ss�T&�aS�K`��������Q}��Y(������v�UM�I�DXm��x���%���8�q��l6X4BO���z!�m��XM&��$#A[�pL��2!0�X�K�B��6nLN"�t�P���i��@����{�u�����V���7H�y�x(}�mu�W(C����cFi�*FT��ua[����
�*���mW:��R�����C���� l�
&���*_>������Z�`���H���&b�2IB���l����To���|�[����n&t�D2���Tz_���R��8+'�A�"���s$����}�����tT�p6�u����H�c�[+����2�>@=mkIW�������3�`�p���x<f� *�"&��A����I��91���"���6���HS�Y�.E�Yr�����b����w�����p�&�BA&4��+!����75��]cR��-����/����b!-43*�z/}����u���
8k�e�|36�.����N���0�c���	����>���5sw�������c�G8�C��F�����P{\ ���������S�������Y
[{#U��T��&��������u�o�uY5(o����A]�$�y��$��N��T��A	��PJ@�*i��q����a���!��<���%���������AT��cn8�6�q1� ��]�XO�Z����e�j6���4�z~�%W&p��-9#�dF�2���ju����u~��Ue��b�dL�n�y�1`�(��Lc���P� 0*������#`��P����=ce�!h�&�6�	 @���k3c�!�l;8��=I!�"lo�dF�N{�����O[�������fQF������P�@��ES�
�$MW��`dG5�QT��+hXs��8(K�(�q�z:���^�G�ix>"4:������kV������{��"
��P�����h���K��h�9���~�a�,�/MS'��$�S)I�D�:�:��x���~:������)�� �A�v�}�vL&���4����o���_'�|��k(���M~�����F���p����/W9�\��:��c��-o�P�7�c��d��guh���d��1<s�2*=dT��:���,��pW��<��?5��J��$��K�<%�i�/Yv�9u'&Q��Y�����/�e�~��Cu�}�]�ef�8dgD�t�����'���f�?����,��<�P�xqk(�t%�������F%��������<)/M�^VvVg���mF�hi�m��]>BpW<��c	�$�F�<q33r��TL�;��-M����8@\�2t�V�R� 8qc-�e�dElzd�����?`������/"$����C�m������r�`=�E2������%��g������J���*P0p���������V�6��%�Q>W=��`�Yr��O�SU��&�/�}�5�d>@����LeS��(M�0��G	�Ip�^���~�����%<��:��M^��uY��������13U��JLTlO�:	%"���!���}�Cq9�<B|"�������
^"`�^��>{�r����Q�=�)x��DWl`���t���dN9�9�=����$�3�%y<_�n����;�FKp��#��r�l|D:���{�l��"�Jt��,�</N�M�Cn�o����W�<�,��`�SQ)#�o���c]~dn�`��������6����$9��z3����er�;������E�����ES��MA�b���/�eK���3d����J]���R�"�uR������{�"j�b_���DX���
�_���H(�"/�!�L�QE�1�������wQOF��br}� ������yZ�*D�~�E��N�����"��a]����|�:br/�f�LM�+K�������g��y+bL+i�)*z%-����7gz+����>����Y�j2"�Eg������ma_UU����f{WCQ�~��[�}5�|V�b�l=�t�l�X��������d���&�F*r����C���%_�}���X���3E�A`g�$[D�}x�R����O�|E�4��JM@V���4���|LyF������DE���A�t�)��Pz���g��PF*��(5������<C��)/_��qZ�f�u���E`�dQ&i��J���8&Q��o<}
c\r�J����%�?�(���P���b�Pz�W���!��}B���[�3G��{
�)��`�����'��I?F@5�'���e�dJ����b�F�em�M�{P�6���1��R����&��3�����2i1
)j�>�0�3rk�����l�I�d!)��W�]Q�����7��������W��
q'��!�I���i��f�BXc.!�NjK�c���1<#5���^�$t��l��1/V��������$��d22�$��F[���"�e]���7L�&������Prr�!m��H�d�2[L �|�p�j(�3aQ@#>���#Me�F,a0��u�J��La��B�g�a�F��J3b�����7���C;\d#|�rM(+�>Ez��E|>���z�i1������d���9)�1#R2��������:� �lN���K��m���:/%���.�4����4��~[l���)�9��?�� 	�,�"I�0b�C1��|���=�|��y��!����
bQ�`���V� R�=�)#biV$)�� ��t(-�O�@`�F��������F��i#�)e�����G"y����1��a5��*D�������\���[�%��)����:|������W#Bk���M�(u����v���lXn�%��8t��(fO��w�# 5��M+	�e(K���*�?M���i����-�+f���e��bz�F	QmU�t�&������Z������<[*gL	�H�������2�TJ�m�����Rm�3��4nd5jX����35r
�UU"Ow]�T��}���0=��	���Y�6��Nd����d*ek���e�j��iS����]#�<���a�3���"I��6��4�g�X���n~����k
t.m$�w��j83���F���n��/(�y�kq���2e@(/&�����d�d����).�S7�|7�s]���M-��?�RH��$���i��x�p���T�VM���C��T+�d� �5�Pf�^���K�\��a�O�k�C�`�jNu�����oM`�c	��c1��e��������el+�#5��3+�������D!��*��=��
��9B�c� }���JBq�U��7�a��P�����J�	���(�GFk(e�Z��'�}^��p��-��03�:#���^���/���b�G��R�L�K�&�����VY)��������C�r���S������HkZ��*����p��(�u:>C�"v�N�(��N����c0	d���Z�d��t2���4���wn���3���2������|W���l�����e;;�b��K�v������4g��}\��Y���q������{���,[A�����u��A���f��`���v��L��r�t_CM�"I�f����}��zf]~�~�7�"S;�3@-H�#A2+�1�`�z������9�.���S&LX�����"����G+T�A��X??1f`���bD%!�+A��B����V��m���X��N��W�ig��o�3!S7�d�����;��Vq�CUG�.�k�L����4&�L&v�9-��z��C��+����~^��!\H*��
�b8J���2�n��D���t�tz��Z�`:JN'�I�q5�E(�Q�����Iv5	�N��5
����������-�e�Y�7�qm���<T��k3n�s�Y3f�%�w'}d~���g��#c��!�a��-���� ��:�}�E�zl�V���N�v��`r��-3
.����E��K�4����V��=*���i�u!#�h@�1��!r����z	�*��;���s�B� �k��k<�f�����tD"����}=7nj�b�P�H��aU�.���?[�][#r����n����6����i�C/jy��s��-�U=:���(�I/�#����<]�,���������������G~��@���i(
���,�����1A\�&V|diN{��I�?��V�Oh��.{������}1T�U(.�f,��k $������ �t��"�%[�.�|�����B�t��3�*J����2���XTuY},�?��w`���[�i�U �T�L�e= ��-�|���{��rK������\7dd�;�4P�lo�b��������x%#=��K����g����Y��oS����/ms��4�����Hj��a�&�dQCd_$T��z�u�A�C,&��R~���i�&d#����|d���m~���q�2Rr`P����<�U���T�<��q�l�li"$}�udC�?��H��m����Jl�����a'#�!%\WI��ha�2n:�	�F)����L�L������d��q�2�����Uu:z�U+��S�S�JP����R�P�q��3�_��T��^�$�N&�7!j����+�|u]n7��c��m�ou	1��A�1�����#�A$8+`I�3��2���������o�(�r���ya!Kg4C�����f��p�����������G�932.���:NzB��/�utMS���wF�^��E]Y{^"stv'uy��3R��K�C	���x�ki����Gc������7B�eE�!���2��������+:������=,�uQ��G�P��u���rlY��=��|�#M�sw���5g�&iOUr�A�����-�A���_�jW/^��@�+)��RA�KES�6�XLS
�,��<T�^�����H"dv�Y�(0C|I'Ne(���V�qw2]�M�7���+�g��F��e�8�d/0c2��A3/�%���1�*��u;�'q�,"��R�Y�3[�D��]�����B����^��E�l>J�=���&����*����(f�.`��[�*p9���3(TT`v�_�����(F�����������S�h�m�������3{�`��?}��(��Z�<�tSW�	�(AS)L�X�`�^���!�����I8�i�#�z��b�s2y�������*��P��S0m�lc��I��6B�����N^^|N�RQ��nP��|��w�D<�T������K�}����������)|��b�w��1�2�I$%�evn<����z������fZ�6�&8��6��O�x���z/%
g����z���l�L���o6��!F�!R������8�������6�&����K��R�4��qul�������K�N��5���%��)a}�����#��b������M�}Z�F�Pk������n�
�$��q�:�sU~�,�#���$����@��Z���\n�{C������p>��w�1��r��"9M��y�Q�b88�Y��������p��#�x2���n!X�����djZ��&����������)?�>�V��}sW����f6��
������n��~�Q?F��Z����)��+�#k�����
ZRM��H'�����w��,I^�v�����t9�}��Q��>�/>`�+�c~[6�S]4
"��Yc����NP����eo�0-<��u������3C�YDc���e���Pe�w*���J�`�PS�A���J98F:���{N�}#�O���:�1�;Y^���Y4$��=�#���a�bX�;��-�����;����3M��3DBg�X����oQ�}��b��wT2�	K�����#��@d!�T�6�M���Z�OF�t��d*����2�����K����~d���j��cG�m��(�����h��~����k����������,;�f���j�|C���������Qw�5X��~��\>���"b2�M?�	K}@#wu������|�x�8�I�|�'	�b%�C��/�I(�L���w]�r>��=[#A�U�2�_"]c0���I��^#S����F(���d���6�D��q���7�m��e���8a](k���o9�����$�f=z����?�2��E���k��V�2$1���]�x�` C�p"�q���Q���x|V�����X�;��dL����i�:2�q$4GM���k6�Sk���{�#�|��6G�������;������_��WG6�<�!%�)�Z�`��:�L���1��d7H�1�V07�?M4:�� ����������0�����b����+5J
�������L3�")�&{l�y0�U���k3��C��8�f�c�\M�i�T�
o�L�> 5e�����������W(Tnyn:l���-������9�X��hq
>'�#RC������O����]P�^y��lf?����Z��������Z�8�|j�EU�x��r{��r�1��:)���2�u��]�1H�T��4�}d��v��f`���V�V���

����E�c���aq��D3��cR9��B�%���7U��o�������G/c"��p�����g�_e"�B��4��z�8����wk����D��������ys�]������+����������dJ���Hp�;!Kv(��)�����^�n��>���F�y�'���gn����D�'�����Q���N������X�+��
n8�[�S����9�%H[2�mJX�����zg��?8������s�~�0UWm�GMc��P^rdy�l�od.}��5�H���EWY����pK1�&���SIn����K���q��~�z37�I�8��9
$�g6�T'���Ir$��
|;{����m���xG���%2���9v���NecJx�)�8����YL�
��n"-.,�c_p�b����N�V����4_����Yfi���F��z�j����rg�q@��g�$d#��c���T�,7b1�
�z��O6�S}�5�C�7&�A��DW 5`K���=�*��CU��IEW�`��T�qJ�s^A�R����4��>����I��SJ����T�-�GG�g�Q���DG�v����c����h��*��i)����g=�����0[{����}�������l?�6���0`:h8C����0G�=0���j���8�R}9T���V���F�����;?�h���� [�=�o�2����~B_M�T�$-�-Gn��aS9�}��j��T8�����To�����h�
H
�\��}���7M����FeN$��V�����1{{tZ�:��tiT��b�=�
�,�����d���e��3x
/�UC1�u��<b�7h(f���������>���zm�%{)��/m�t�pe��&�&�����S{��4VO����&�����Gi����,�o����o����i�u�)���RL6����^���^�?;�D�\��P,�$�3�%�f�{qp?W�\"	t��v���K���P���9n�DV���g��!7�"��~}����=��9c�5������LUU�a,)R6Mh*A�������3�hR$
/G��i�R�;�z��fPY-�J�����i��k��"�
�w*���{�z�/y:UOu����#	���<�"���	�����&���s���X�JM���Km�
�qx��&�����YB	��{�/!�f�N�k�K�=v�o)'�/�����tf9�19����R�7_��u8��xZh�����S�Og;Z�!&�#5�h�0gm��������h���/�����}n�wu�4��7OI��q�����T�~��)w4K������^&� ~ �9j���L��d >�|9�
�v9�s)#�� �'^e��e#"������/����Nuq�E���De�&�)%Xv-���\��K�e0��j(���D�`n�fw��b]s�7�	�����l�'��W�k_�6%�L�����d��Af$�E�[�f��x��M���v/�	��,�9:��,�`���Ug�bw�<��pXKK�`>RZI�Q����va�uT��N�\�����vLR�tNI�u�s�F�R���X������PuB[���<hy�i@�\D��-9�Fz�2-<�1��-��_���>�?d)��iFi�6��D�oR���P����;^/oTg�e�����e{EkPg)�d�/��Aw����|+[u)�\���$uJE;����R��d�I�g0��q��nQ���
\��m_������b{r��u��;���:��y[�R����������pj~�SV��x�-����
	��T|wz>tKbIM��}U��*B%c;�����|Qy�:�I"���g�-I��;r��.�I� F��<&�s�@�GN�s�W���<v/]kk���<�)��^�������T��AE�$
�+y.o�{����$"d�m�.�G-�l�����?��l��@n��zhn��H	&����RE��.���T�x<���q����c���d�����������������$�M�O�Ac��i�����`�
�~��R$o����{SY��N�@��Re�����&?!�N,����X�e���~%�
Z]?����K�	x�A��3�;GZ�hn���P��E�f��
(�<�Y��,
��z��V>69�����oI�WJh?�M��3R����e���*�%���������]��)����X���&ea�JM��`�����X���$����]����(�A�>��luk���mj��l��b��X�-m�&��H�
���\&���������4a:��dUj�h�fH�Ic�B�}������Q�*K�<�jW�;TJQ��?������u������%��AR��z+����m>eW�_���t�����k��6���L�j��r�`"��$��-m��^���e�����K&����my<�k�dA��G��@�6��o����~?�4-�A��b�j�@��
�"��-��7�F�^�9�L�TPX�P�}If��R��e��"��(�SP���].�m�Mit<~?}|w���iJ�g��/�_>�%]n5v���`0A#�efW�\T���3`��QM:M����t��&������!$���c_��G�K{0�
�:~��@��~������xN�S�e%��0�
������`������3y�������l�F
��w7�)����mJ�	�,u����&��,��Jf@?��Y�K���r$_����a�i������f=�L���f!�"����,��E�(��BA�m���C���V�q��.L$#�t!�E$�;Es�|zx����N��9|�G���@
	��/q�?���h�b?n��L��e�GS��TJc}U�/3(}l��O��|�8��b2���;R�I�"���M8}}�T�����O�����j�����������/�mj���!�~����V1���������)P��UD�
i�����a�
������
�P�O����W����������c)M�������b�~i���@�h�)�$����e4����2$��L��j�`��0���Er���8R�S��d�\}����q��z��tkZn��A�Z�;��b��Z���� �OK���([T���:
�l��_��)^���V��%P��!JK�S*��L�W�F��@�JR
�d�M��0���F?���+��/lGHOZI!r^T��V��)��1����B����)�����c�A�~8��C ����MD������q��gp����En5����T��S�A������@~>��Rr��iK�d��d�=��r������������*���M��n�dt�&�ntN<2�8�,C����=i�m+Ioo'���`%�9RH�V��������o��by"�>������������\��e{�^A�-��+k0YP����?��������
�Tp�:�����(�8�E�`W
��v���i���ss�+X*���VC��%a'��L��6�����!�!��b-ws��[�[L���������m]Y�P��"�+I6:U��	���k7	"&����UUs�SD"���g�f�X�������$������t�N�>���������=���O�K9u��x?�)��NrO����-�6�����/�@���W�����4Xs����R���������	4��M���y��rA��_Y�Nvf�_���8����8wZb���TB����!�4��_������r����`�X�%�+�q�#����O:���i-U�{����FS��-)Q�_�d"
&��Q����o�U�8,%PB.���C���F�,�.8@`���\4<W�^�)B�t�z[qR�<���",��Q����r����m�v������*���,?0D�d���7l��0��O3��9�-9>�{���g��t��\�U�������N*%�T��g������o�����#,w��d��t��p�����`v:y<���h������M[�wM1����������,����*��#����%�"^�;��Gnwt��u�)	z`���J����Bv�@�%H2��@`��r���f�������T�D�d^�aN�0q�))
����i.�B��]��@��Kmz7���b���:�����7v� �PU����`v���1�����m9`�GJx������)yH,��8���@���������������A�L���s'��M���c��7(?9�hV��P���C���'������;�����K�v[_������
F��,p������|�8v�r=4z�c���v�n����\F���v��i���C#.��
�y��e4���:��P$/=�'�j�������5�5	�~"���"Z���R{�%��H�� �?{sS�o��1���
/�B2(��7��K��\�R����\l0�%m�a&�I������$:�sW��+SLbox������)�����qA!,I0��[�3����Ma(:�l�:� q;���x�9K9��L�b:�L�A��(j��L����z�=�f���T�%A�������7����]��y�Y����D�d�&LpD�y���aoX��x�9���x�c
�
�:�������_n�6`)WAY��"8#��1Y�|n��Ri6qG�O�y�Q���4����g�A����vV.���>^������`��6�>$35<�J�i�b��[? ���g=f�����!�$	��V��Q�

�kq���nq��r�[��XA2��� ��fZ��������C���^�_P^H��
���>��xj~������G7�y
$�]m�
����vF��YTE)�t�R���?�������B�Je��!&��Hv��n.NQ	�wDe�g�����)�$	Z�j
�[�-y�`0
�����>������vk.��9U�O�U������=�K:n���J�[>��+[�x�8R��w���m_��w�[�'��9�20������D��R��D�������2v�����%�PlgH.�/�d��O����&p���>�;��9��#q�����Bm���;���^f.:R.V6A'<�U������-�,�����k�e0j�w@�~�&����I��f����Mb�f���9K�
k��|7�7���d5Z������i4����9�p������\���S�6�}��z����H��2g2����Y���'HAUMyF��0d�S{to'�A�����m������Fw"�o4�� ���8������	���-�`��~n�����`��fO����,�A:u�&u��?	��i��
��Q��8��-3)��b��Q����+�d-e��$
���,J���}�2�T��r�Dg0��j�x�Il�g����m��c���`��{����v�C�i�q��w:g�-<�����"��T����G�s�t���Ixtc<k��T|M������;:��������zi*�C������y�].��+�v*��vf�N�\��o�N��<nh�t��0HA`[�+�Y��d�q���c������6oS_u�d�m�C��5�\Q$r�����\�k_�N�,S4��B3�~���g�+p
/>�}�2�5��rJ�N�k������U�MD��:&0�e�-�B,#'���f��� �M�F`���I�������(��&�Q�ne=�]s�l��m������o�g*��O�1j�kG��Wkl�AN*�����L���3�/���cD!��D���+��^md.�b����i`	����Z�)�-���.��0��
������10�ge���p.o��gf���r���!z�� ���gH-�6�.��{��z�.����z�aL��1��@��u
����5"�/�2�\����9~n{(TZ�"�����F"e��i(���W�����M,������Tb��x{�U����o���O/����{����3��hHjY?J_N~c�Ny���d����w.�<$��
~��.��X.�6�4Xa�W�hm��8a�������6�,M\�b:j/�(�*���l��f.z��J�9���uA))J$��~��1��������}S����T����DA�G�'����5��G�?�����n�}$��#Yz���^'z���1AK���u�#s4X�����
���88n�����4��������}
��Z�jYhdi/��y�8P��9ic����������9A�~�r�zt( �D�GQ��2r��I1���r��6�t_�< ��W	�������U.�L��N�:�����S8�(�$r��4�j��hB'~�0�Q2B]�\�B"!�JO�O�G��5����u�����t��h����J:�"[��p�
��a�&�� 7[�����c�$)�o!����0J'���[9�t�������qq�����H<��!Yh�P�Q�l�j����#A.�K6����h���^�.��^0'v���2;����p�?�����c������\k���b�6f�3�|��tM����M�h`�\N�WJ���c*�'tU	00�x��N�R�%�\�E�'�,�kz8wb����`d'x.q|#��2��%z�K�OZ��|N����Hb9*��R�5������5^PB���I�rAg��_�:ruH	�v�Io,�@�#�C����`V����
%�O��vOk��$)�S��q���Q�j'�Q����}�4�%�Qs�%�x�W���/�Z���X��')Y.����Hu����h,�h\���R�?B��sW��8�;^�OM�� ���.��(�IUZ��K�}�A=��;	S�s����d^eU�u���
�y�P��^	e�q��jJ���_d�*��9@��&1��[de
��S��5�-���]�A�D���\�Q�
	�����r�y���JQ���J�x6����(q���w�S�{G��H�"�v$�>�]�����;��zw=��}��u�����g$�=���_n���d��8]o�������l�������}Jjz��T&�<0LG�hM ����x�����z���K}�>^��%d��-L�"RSQ\,
���a9A�j(9e3�x]�IQ2�v�'d.�|����M{��t|�4���d}�eP[9�  SP"����;�M�3��y�Fum+�������A%����gp���?���8�7����L�]�����g2���=E����Bpd6��"���i�����/bh���$A��&�t�xK"r	���a���
.?��I;��:���h0��z��������.����y.���5�Ox��
)�}�$S��L�pG]s%���g��l�������&(-+T^QZ��u�����A[9��]�/��|���eFZ�����������y����lHQC8����ez������p���N��'�P�"��~�;{L���9�&Q��	��� ���9��2�\��^l������X�	.��$��mx����r�����_�'K1
R(b#�k��
(:�����U�9�����&\!��h8�eFc��le9j�e�h~��K3�U��9W��_QR���xH�TD��)h�H��rB�m?�����6a�E��5z��6qJP~��vmi�� ����`�i�ti���9���|��vm{��
��E�)�R�~7�?"��r�9�tR�,�4g���0Q�,�����%�o��Q����:��������p�C}�>�����E0Bx��t5������aQ.s��3������p��B�i.�FC���d\��[I-��r	�kLD%�;���Vr�q����4��pP�O���`Ho#�����b5KJ���PP1�����zHbW0��5`�����d��|6j���zo��Om[��t�X����}��b�pF�*bX^����K����)�R��I(��
n
�H8<c3@
W��lO�����U7�M�?$��`c�VD�7��t���Z�8��z�U_w������>�)�"E�'��K9N������M�4�k�������s�I�vk��Wd(��O�����
�;:�����I�J�Tj�>�Y����Z����k}�>�'�#$M����
���,����s��Kz����OtM��?V_�������� W-�	���v��`U���Z�lX��1���GX�V�H)�&�	���.8r�f��q!�����v}���>U�=�6�fLvI*�������ZGG�'Se3���~����iA-ID'��"b��+�JE�������:e��9Z|���wx�l0���Xc�voN�}������)��WS�Wp���hn�)�%����s���U�Gq9���,�[��P�@�����!�?�\���m)�����7��-�����N��Z�������v�\O�[��v��)����w
�/������O�S9ad}����f��;X��\��0�8j��>�!����&s��bM���Wq�0���2�����`�j�U���{; �.N'U������C:l+d�O$l������<����z�t�����
��H�>g�PU`�D�I�c��a���j�\T�V-��/K?��
��<#��I�u��t�#���n>4�Z
���f���!9 �h���I��p�#+P0��+�e���SI�[�(�\:}Ld��6�)��8���lm	&�I��W�u���0)��c��_�a��8{��2]i�>\��0��2�2�k(�/g,(|�b_*�-gr�H.��73�e�m�
���� ar�����W?�!�������5nW�(?L��{���)����p��H*�#&�'��^�M�U�����9i�W� �l�Q��=����spk�����C��b�moc�#�,����f~?����������i���� ��r���U�ez1�9�<JcF�0\�.�82�3,���y|��3#=>8Y-fdU`�6��;�c}�~>�����&�_�����jK���7!`�������E~r�rP*s(k�l.eF�����������4����#�1����}��7���>�����/)HP�}�UC��Xw����w�� ��Pj�y�&sYPS"1�v���x[��?�AK�X�#@��,8����r�T��z:U��������%�4
�tn|7�%��tR��������H2�E����"1�� H��lFo�u���S���*'�M5��DM}���+��������Iz%�z��6���,�J�����Y��7��mV�^��k���;�=�?����I�-r�x����A�#�GNKoX�A���������������I�	�'/X^��*��S��q���A��onh>�����m���&y9�g-�nQ�]_���$�����Q�@�����I����=��%�{Fa��B��8j��;�����l+�S����"�<�2Y)����XA7�f�l�����������9~n����bB%#-mI���|1�}��|'����&h��@�=����Afw�c,�Z$`����I���Z"Uvfw�����r����8�rNs��,�`-�������T��4�m�!���j��E%�0d�\�R�����,>@������qI�>y����2�#X;w�����/!�2�<*�<�l�����V��S�nQq{
�QY>8vcJ�H'S��G��0�jG*�S���Qf�*VUw�?�����Rz
���T��fGN�qt;�����v���^�c���������>WG�����2/��~F/��+3�S�����[o���y<H|b$�]��0tC�%p���v���#s�l��.i�3�bQ�*�f���K��x=z�.7H����	�9��d-A�N�W<�i��@���s"=�>�apU�������dD/$.�������4��[��-�i����n*���"��m�N�iw
(�|����N�%���7v�%+4E�u@~h$�����3=�p6�P����9��.'��G��NA�~Q�����k����el�	�����v�����`���-�����)�F5�����{e��+��{E��\��S ?���3�2�^��%Ru���*�-��R2/��A
���t��|u�Y����J#&�E���nx��\���X�G�cs�;�G��@���K���l����	�)��m�?�������3]FT�L�X� �L�[�K3�$����
�^
�"%����Bs�����3I.��
SG�����%`��K��ga�����~}�j�a�7���?���1�� GUjdq���A����d5�3�qVK�����������qO�}�-P���I����"d26�9��B�";��P����O��w�|w=�p6�i��
��bY����$�����0��H"����'��Fm��E��I�pz���������%GU���d0��+~��"��������P������/<��|+N��K3�c���
e���ss��o`4!*�D9�>"��x� ����Z�%��>��
������V�V&���I�����]y|�FiS"������������OgJ�f��G%(_�+L�@P�������mS��s������Lw<�E�@K��GD�\�x����0lk�86��RL��d�#�$��(�N�\�����r�v�[�\z����
��qK���\�R��"C�<�������f�}{'�����ou��P�Y���e����"s%���&�������Rd��l����XEj�������%��x;��4(���%����.hf ��=�$!����}s�fJ�ZPj�Q=xV���Ggu�2�	��p�>��L(��(���(UP�/2���/nd���2D{����/���d�Q�+��e��-���j"*^I�9Z`����n�Q�*a��HP��N��bu@��.3�N�OM�]\���1�bd��1��}	�.F�Rb�J�+G���	�qLX��G�P+��TI$������x�t~���R�"�L�����8~��n� ���K��*�[�_��<����E�d�g2����"��c5���o�����R/�c��;{�@��VB�Q��~�����"Q����
�p�Cs�S�I1U��q�#V}�J�y�Do�� u9a���q@Z�����
��~7>����	�8��P������P,�K�8n�F��$	GG����6�D�1��Gfp�cs����1!��]K8�y����]����I�
�����E��DF\c�A���_�h|�A����ox5��95����*VC0�f�5Ao*��.�i?��������Q��]}:�wW0:1���ge�hJ�S���
.��K)&
��n����������)I��qE�[N��	��>)<�����UgH��|`>�t����\�B�A/7a�&�v���g�<�h�.�G���B6�\���F��v������zhn�_���-��'�K���>|��S�>�����W/�� j����#�������S�S�����f�,�-�������9T�\8)j;r��7�o��)f�����f���N��/�5��'���>^��}�OR20���t�c	'�t�X�����$��e�)S��8I�iE
�l�2����X6H�A�>�r��g)Zr��!d�m���v��>�PDy�F�#�kUz)�����s�~�o���T�c��$����Q��-'�V��C�!����z�U���mS�X�N~H^��f��KH������:��Z��-�E����HX�}A��R�kl���RM<�����F��)�Ae36?�>+��|�Z *G�R�W@##��*�q�M�)���?=5���������p%��2,�q�N�p�Q�I�{�3��$����$d%,Xx`���K��l<X�1����9ZU���;������I��(������`)eD	���*+����&�Q�4�K�/�B1iop4�M~@oTe�QVU����]47,��x=("Y\�7������,�������������mv���x4���~H�4��23��1�6�G�[�f�E^��,����-"R�L�X.��h����4�7"&R��$�]mj�I�u%�3������C��om �z	�l�Vn�(��afR�� �����i�����$za�Z:����7��;NsK��<E���m��*��2 ���R=N�R�����|�����?���D�\A��A�S�R>��n��M���r���L/&"�(a�@@r����sdPd�=�v��:�zkl����"�&S��E��mb�	8��`4�� ��e��st��XE�(�*,'�$��L�$��C�IW�[U�M�r�+�@~Vi$��R��\z��u�t�����k�^���)���n���Nm�>�H������U�����#�h�|���A��YP)&��|%��x�	r�[J����W^%����W#�������Rjf�s����<{���������z.�sP��C�����#���,c��TP/^������o��B��0�S�?���?�����zy��Z�����P�F��RB��l7^���\�"���`�X�L�P�Q�d���78�5�8��m�c4T�e�������eA��7b;3^��k�A)����:("��~��>���x��)ll�y�>�F�R?�������Us�?������R��@����oV
���m>�|EJ������5J�.`T��������4Z��K�W�����z���PAd����3���N��� q~F�F_�By�cMYC��7���.�	w�	�����	#��S
]�4��97��}����e�����V���L��3�����^Y�]�fr��%�n���R�9�N��to����[����U�]���O��mJZ��gC�,{��_!�h��G��	����@OX���x�JI/���TJ�������A8ref�I��Dq����\�l���q�����1K�dE�B`&����L��a���"��A�*�
"��L,�
t�4A�����R��TU�����s\��aX�@slDi�D�l����������;�t�r��[����Tu�*x��D\�t���s��{P�;�h��`���7� Kx%���r��G�Wu����!����%L�t�Q�H 6�l�p��`&�7���vK\Q��{�Q�d@n��������Z=��y^��;��Rj���D��a�v����O�\�)e�fW"@<�nw�����x����
�<C#DM���F��Z�p������$+s��:[�_��C��@
J����%h2�^T���Ul�ofx^\��}�_[��,�|���
� ��+��8T&R7]�3<�g�M6J�5�$�l{
Rb�
�:���s�xn��[��X���=�����J`B��e��(7mTQ� Z,���������]����\`3U���\x�W�\U2Xr
��w��NI��?���F���T�����\�U�U")�wT�R!9K��	8!=�g���b<B��>��O���3�g	���e�+�"���a��vJ���z`���q�9��Hf�<����l�������������������)��4 ���`�����[)�l]�Olt{�����w�su��������~�>�A�_xC�����a��0���#��"1A<+mqJcS$�(��%ng�s�A��Czr��Cu�$�bJ���4�|4�����A���h��,��������d�r��L69	��(�>������C�9��l8�_����z	�nL �G�/4���~�m�U.i��[���Uhr��9�{�2^8��%rX� ����>������mN(H���se{�C7_�2��f����1�V�W��GP�v���Q�"f8��+K>l�q+K��N�����A�v��A�a9 ��`M�!Z���}z#C��V��������%��f���Z��>���I
$�!��8w�]+���K�����2����9A��XHAS�HUQN�i��a�����}yg�H��~��L��r�R:��A��!����0�{cgP��-\usd��q���&�R����%�c�v��0���<e�	\� /��lV����<����7NL�j�$�0�d�+���}2��89y�o��y���P�3����J�Dir&�M���o�|���Y�k���x�C����GqL�	�6�|�k�>�h�94��c��r��s}rD�%������<�%�n�@
��\s�%�2xI�\����L@.!��"��B
�`�f��2����MA��	U������L���pd(P����@��"<��^�0��s�J��3���RT������Ng����
��P��
��f���+�L��v��qg������e�/�������l�V�`�.X�u����
)e����,s��K��8������iE��i��c�Z�7y\��tG�/fYl�(��r�
���`����������f���Q�b����r7LG��GL���G�M��:�������t���z{���(��GMa4�jW��v=�D����	)����L�p�=����2���Gd��]�� ���e���N&:W���]k�.%rDl�ErQ���!���F��B�6*Q�D4'i	�}�=��l������T���������`�}���_i���++m��B�FO�4�\��\iA����O��O5 �{��Zl�#P��B��}�r�����C���-��S�^�Sk48���F�9�B7�5G*�;=T?Tfq$����Ikx	��&mQpl-N!�2��n��6:�M��$�g)I`0�2��g���c��uy6p��rLB0�
���0��}�K��*{";�&�fKR�'C,H�5��f�10"~�K'�������r*���x���5����<�C�2�%X���V;�_�������}���� =*�;i�	NBt{\(m�����5G�I0��yb[����HG:i��*��
M�~;���m���$+A�o%E�n�n�����i$�y�s�m��:�[a����zq��W���/������u�����1����"J���!��<0�`kR�Z#]��m�I6��Im�c���Jv6P�'8�C[������J���E���>{rQ��t8$�J� �(Z�S�D'q������A�tXU��8~��re�QA��q�����|��)��(7=���{�o,.42�k�fS�_�������G[!������4��lztE\�
E��(��������w�7!�(Y�E��P��9������.���$��gp8��c{���kx�����*S�I�-��Ip�%0�F1�R�s�_j7_7��2H}��0�=C��(kJ����-l�#�A���s��:��-����"���ru���l�����gU�}>w�iA�����(���X@�"��/� �"^��d�"Z�����>��||na>t>v�S�o�4#A�Dq�U��c?'��jn���+�?o�m��\�������s�����n7n`=2>��/~`�������m��TxX;+�J�����Tl'Uf���
���?
��$:�i,
��$C�A�o*3�C}����m����\��A�������|-�F`}�K�$������������/]�x��j�*����������D�9b���@�P�`���`���B6�]��*�W���E�����
�&�q_���n�������e��8�M��5�{%�d�5����l05	�m��7c;��<��;x=)A�DPBr�x��q�����Le�P�U.	��h�)
P7\�Cm������MU7�m�>���\=�u�r�,7��$P��\�����W�p,�s=,qlh���Q��m�[a���rr���I�{%���� 4@
-P�L�\((�ko���li���z�R�����i�rw3
�dD��_Q���_nlRg�w@,�2nZc-���j��a�a�X�oc�)I���23d�q�*Q�^a��O:�t���>]���g��mq���Qb|=4���YR��7HAFm�r�V���5���^��I����X�p6Rh��P�v����xu���6�b5�6���2=�L�Db3\�0^���V�As������� �G��c;��^�/Cw��D�r?qC`������)�l�XG�8�v�w����*^x��)Q��l����t.�����R4Z���X�?����]I����Igp����	U��g�S��my4��P����U�KWn��Z���������@!D�%�w�:F�@1.+��	S7���������=��%����T�@����"�P��s��R��*���"��0fG���R~h�������RU�z���U�z9L�G�
�n�E6��[�~������@�X�><�1,zx���e�G��|j{�������A}����fy��H����s'��\g?���/hyx����(�F3I��^�$�"��B����s �4���:�d,��A�y>��%�l{2�!���/'����0��G�$@�n������F�t�w0�[���l��RI�um<�$��Kb� E�v�*�	����U�����i��yQ��`]�d��$����L���d�a����?�{�������#97m�3�8�3&pa
u{���D���������D�6��F���X�r��t=�h��A/��?�vo���v�/�j�%���m������R���!!���	�R�x��p�����c�~�|�]/�'����\g ���c�[�����0���O��;���N(��<�J-���4�/<�1t)�Bb�PSp�����z���+$\�e������f����*��c����m(.I���62Y�V�b�K��Ff�k����	��I�jJ�Wa]f����-+K���:Yf'T.�~���G����c�d
6g����h�	�%�73�����,8����S�s���d������6�oC����B�!
�?�O)��H�7n_���r��s����~��������������a�>�����H_m&���9���%z2,�����wGo��H������Q�Z(�<�|����o<^���#HD,7k����Et��mQ�����e.���m�%�����-��5RfPQ_`f0�l~���Cu�|l��<T}����{��}v>����R�q��|�1�lh��Ru]��o������]�@x���$�d����#]��%d ��|�OO�z5]P6]�0���Z��4>������zTk=Y�n�%�^����}�6O�SR�D��(,�p�����'��0N2;�/��S�V�V�� �pE��j�Efs��� �U����H��bi�^�$���������D�i)i�a.p��o�?9�����|g�h�c��Yn�Z�SXf*�4nK#������wm��i=4���{S5���BRd��r���O��[2c_��I.�nFL��zC <�������Rf��*�<Q�M?\���1��Q�|+u.����\���K���PA����f6������6��^����YdaU(9M5gU��lT"�n������
�}���
�a�,+���F����!�p�#��������V�r�~d�fV	�!����t�RlL���H+r!�����������=b
�u������'e$Ay��b%!���x��j,�3�V�T.6�����j�y�|.�M&N\��Fg���:VbQ�� s�?���J�v*E����C�28���0��?��])Swk0��O(����#
#����f������zhn�H��*CBz���	mq$�P�G$��/��S�?���C-��a�4�"��*)*��l������I�d����>�������&��u�
������f
>����K����I�r�u����hF`��
��g��xJ#���^2L���u������;�g[;v/���u�XN4B���������0�Z�@�Ae�7���w���T?w�S;I�I�EZ[�(����D8M���g�o)/d)�J�����a��R���%�����mi�&Q�D� �5����� 	}���s�3�����\g�A����L�#e��F���p���.Pd����d	���cl��I.q�{s��/�����6��������2�V,�M��P�I�-X�KX�b��b���?<�ZA�R`��z2������y��_�����&`���&q"�s4V���e���Y���>����	��eI�E���\��(2�>�� )S������|������wA��^��ii�d�Q�9:��sY�W���5���\�'�=�j��Y�$���6����2s���)�t��1����B�U	puWx)l����pO�`����f_��{u��T=]�4�9��\N�� m���)�����`�����R�94����-2��r�-�_����u�H��`��X� �_a����a(���X��mh���\0�_��s������l�7O������@��Q��K��������L���,��82���1�e��Bc�/`�0B	2pY��`�����T���g�M���Ij)�_�7����,3N�Hi�p�\H=��H�����j]d�K?�HF���1�9�� ������%�����������c2�� ��?��=GA��8e*����n��+} 3�1��#A��0p���B��d�c27��f1���4�"\�����Y
�b�	�����x��*
��&�������A��������^�`h�����������]*�iP
�i-���������/�p~���9i7Q�M,�����^�:�6a�G�����[�ep>�Y�������o���>�W}�7 ?�:%R�z$�b�+��\3����f0^E_��W�wLw��0�!�7���2]��m��$,��="	t��H�`�E��:�}t`x���������������������\���=%��
n|l@��(����ta�����w�M���|���m���������Y�A���o�c�H!&�e�����������h���@t��x���d?%�6$���(g��+@Dl��qu_���7]��X$s��dK>W��Neg{nV���
,p����98�\���.�x�d��,�n)�\d#����e{q���6�#y�-7*������XO�w�\���6������z�TP+�}�p�n.@�+h7p�A������|�]xU_^����C��h������jB�m?����im����S����%�9L�!�H`�h�N��M qe���$��6����Z�TAl2�
"����d'�zs���|��gB��;�r�/��1?>�M
nf��N�l���'��_�3�����v�~9����
5v�a[�sioC���������X�����
_���������&'�&���B����X�E
�*����e}�l�H`�X_C��1�Vd��X.h����-��T����s���IN������0�N�����=�����
���o-��U��Id����%�N���5W8{f1���E.�S����g���g8�������q�3S)}��&�ML%��B���X���J�/�}PZ�{��E��L������������~�Y��k������H��*��T����
A��/_�/�s��!�\�f�0�1
����!e����
���{��vnRs�1��M��W�D_H�����2e<����H?��]���I�ZM7�x�e�������F�Jm�_�d�m�[g%��=��|	kQ$�����,~�UQ�������KT.D��}9Z.3���'�=v��m|R0��i����0�aV�Z�W~Eo16(����r��Z�����6��	%�s�t##�p�@�q2����=�aW	��Q|�Y=���8��lb�lW���H����W����Hd��BN�*o���&��**�rS�J_6=n��^�A�_���=���|�#�&B����j91Y����Lx��J�W��A��|�k����{9
:���R<�Cq�B)��fr���my6��������OP���~��ls������yo4�44�������������3��Oha�3{D�m�C�\�q��,���e��Pid���������2�?];�%��'�2l��(0I����>=2O2 F�i����=�9��H������v&�����?���)(��_E�W�L��CgN�E�"b��������V��N{~]���sy$Mq���`�.�	���l���>
���P��s[�A��S�F�W8�l�%�4*������s������i(A���I�2�����mu��;Br�S8���;k����Cb}
��CQt��m��d�:+:��[aU9q��Hn���~Lb�:y���A��S](���xNE|��\�a;�B���;Y�D_r<��"e�f]�|�9{�,2H��� �� UCm�eX.e���b������g�.��4��
�A$����(p,R`���X@���hl�y<j��������
eE��fQ�
2[���i���ss���K�)�f�};��w��.(j`D)�;.r���A�>�:��������X�`}�������;���~����z:=����0-�������[ e��q�������ss��oU�5��IF	�K���jSh�\X��g��|����x�taK0�+��rc�wF����(�����D������x�Z(M�54��qP�}�t�"u�p����b�h�PQ��%�A����Qf����<��Zj�
��/w��2R;4��?~�Q�v����$�(=�9P��h����8&���	s��A��L�����n����
$Q��8X���
"s���$d�	r�&��+���o������ ���Xt���7�����FCp&���
��CF� Lb�SJ ��::��6u�Q����e���Q����g3�P�����?t9]����P��MlU��!��c��U����O��Rs��T�~�_������9���;2�`�2�%2��d�W=��W���o:��Y�
����������p��a;-s��-���������)�	x�����r@?���������BT�����@d�-�OP����.7<QEe,
�
��gn�Z�q������D^F�;t@^�`�\e�i�����sg���Z���.���`�a,���(��H�&r����T�A�D.�L����``��T�`���f��0�3�J��Ek��q���\��Tk���b��,�Lt�F��q��6s{Y���?�ml)Tu���)�����y���.�R�W�~�XNf0�84�0:tK��?�����5������n_�DF�Te.����������&�S�xN����]��R��Fw��L�/3�b���g
�����
����������S���}�D�=Z}��V����qM���e.��
�"AdY���6��Q�(@�L��{ik[��&�%<�W�u@�?����>IE��[r������XI[M<�hU=9%V5�s�
����cDg�	���4K�#E�Ie3�h�b��9�k$�rz�	����{�-RN�gU��U������G���p>{���G�R	=�y���������Y��B-A@�����^��z)'��r�Y2FGEP�R�2�8h&1��D��z����!�G05�2��G:��j3�:Aw��G]��������l���>��ql�L�A�;Q�	i|�f�%%��%0�E.
:U��/�����S2� ���<���F��B�zAq|8�)o�+@#Y����w�%A���;Uu:~���s�����$�G+ �~0h��9�\X�@��rG�o��t��h.�J�d���1�fN��*2�� EZJO�gd�!���?>��{a����o�2#!|.� ���-��BJ)k��5�3P~8\���\�����()��1��QEI���O���������V#t(�hb�}x�	��:/���W��o�������~�D����C
T��T��7d'��$|#���.f���Jb��lG�����������wn���eP�[��I9�71�*���Q�ie���~����R^�@��O�u�(4�6\�z��J�\U�ri�$��G� ��hv)��UI�T%<�l���������o��/�S���m�#��dn �w*�h��h�������$���N��?������_�g+����h���,4Z��9D��;~����=y2�p�e��HefO���d�<��R��WF}�(���,$2y?�������CXJ�=H*��8
����mHK�#b���E+���d:(fE��X�Zi� a�d��s}���K�W�����2�\t�A��SN�w��g�l��T�(��)�/��0m��je	��g������U(�"��w[�3E3� S�������F�)&��V�����2z(����0���(��{00r������+��/���$�h,�x����q�)�M�x9����9����>�'(�(=�E^�v����|��_�;�U�!�lT��F��@<��R���)���*��r@�����7P��\�8�E_�/E�c���,� ��\�P�XL)/C��\h.H��!F���!A)�X������jz8G�\����;��HZ��WDe�:U����Q�9:jp�������0V_��G��X}��u9�I��D_1u��$h55rN<�H"8xu���x���>}<��|�B7�<�eR��`Xg�*���K�4O�	��J������r�I�H)52�b�m�\�������@I�R`r��o$2H��������}0����{���b�����`�#c/���H7{��/�~#YU��i>����)���)��f4��B���L������tV����:Y�������\�d����Pr���A���!f'�9�{g�r���97(�_�F�Zz	�:Y�@��h��<[������\w)�\HO�OF�))���t=���������S��U�s#`��7��/*�?):���}�P��<�i7���!���G����)��`����Z��2�}���q�v���;�dQP�������|���HEy�h4�jC���x.w�����Hz���C�<~�i�U���K=���&o7�Pd�7�@���2�����"�^O3��ep0�	�E$B�$��#)W���@��D9������hk��d���p4r�#Ta@�p�K8&�N����WX��L��E��t\>�w��5�J�@i��@3�������%�A�9�<�dlYt�c�}#������>��6��Ku�5�4��<B
����kC	#�(&�v�� ��
�$&(����������3e[u�)Nx$�������l[*%���>�M�C��U���3�5���QsQ��J��2���:���-�^�	�!���K�����J���Q{x��U��'�)R�iy�,��"�0��^�*�,����K����f���qf��{�n3.�&"����B���a3�"���
�����[d=D����P�$�������aN�V^iq��������:=��C��c�6�_.����������|���%T���9+��w^!�`7�
��l[��k���M�b��ZM�Bz<���F��*N/PH�l���z�t������M����n4z�+@����~����[BQ���7���6����HfB'���g���N�WY&��T�/�U�[��8(����_���u6����5�S�0�%v��(@�	5����D6���8B|�b?�?^/�lYD�]Oi6S��/f���8T�n4����b�oN5���_����X.j�246L��L��3 �#N�gy[��������:�!��(��Hh����F�>�����!�����������X��,,�H�b,Z`Uh����J�(���/���'��R�)�$��)�	A�e�S�7,�|a��m��>�����s}��:Ai��z��H�b!�Ua�M.[H������1�'FTxb�2�+e�Q��u�x�F�{��Rh���{���;-Ao�}�;��X� ��|;&[���
�� ���b&�
�w~J��Sb��e����qw���rw�a[t9>p�p�~�3e.����G6�� D=�����	�"w,���������+�}�~���da�ODs9�3�p*���l�Wq[`���(�<�U!�+I�c�����r kh(�!�z0�t���0B����S�w<<��@&�����dT�GZ7:	M��l��������$�Mo�������|��2a
	i�<�����-�8ri���l������m�����-�+�m�^�D���o��������o���{R�.��!)K������PJ-=������l���P�u8({���a4HU'��*����j����iGu{4af�9��2T[R?y�o'���]�=��dl'����|�}T��Iz��ih�N���s9���DYh����/�ge.����rJ�I��c�4.E�v�SDxg�*�rn�������Jq� ���[g3Z������r�'hB4]�[�r����u*���\J���c������U���?��_OW8�K�0
�0p��q��s1�(,�k���4�|�^����I����0|(���>�������m�j�Xf�F�p�W�5x!
��"tF\��9��dCN��t"�������$~��2��ae�[�8^�-��#�����~EA�����5J{$E!P�ei�1���|@Adlk8^�����a�;��K�*G��9-������X�R�v�'s��	5���c�C���I��d�@2�4�"� ���H�������	����R|�F#�)�K�� ��@��,���>�������8�����3�S
��'����J��
E��a�����9�#�(�aZ<�m�K�������/0����BI���{
�A�'7�@��9;#rYT���IY�Mt��Pr{$��8���Y��_��:M�(*j�u��(`��K$h��TF�����b���Z+�������d4��P
	���x~J��Q�[.G�!�fA��
���h�n����nh������?.LM���P'sf���������P}�?�M��Ho_L����
Fo9�����b2o��r�f��I�p�(=�h���Js0�w����� �?h����������M!����W����m����������<�i`�����F�&��Nd.�l��f�$�P�m���h]�	n�#�d�i�����f��K�h�!���X���"���G�����ym��������
.l>=��$���O��k�T\����Wy������.��������s��$�m�e���b^@����iV ���c���/!�ZlYF� C�fnZ�L���w�x���2�eU9u�ad��Cs��OI(���Q����H�
�&��pCoPC	%�M�{d`I�%�J&��f�Sw��:THx���d2Jz������~F�2�<�����\�C�[�"��I��$��	�L��D��1|\��3���~}	�Id6;��P�����;P����^�i��$N�l J9%�K�F�2�tq���$i^'�O��X�����8}B�o3��0�Yd����c�ak �M<��5���6b"I�`�R,�b���>
C�M�;f�{n����n�:����Z)�H���h(��l�����p��Cr�:�p���#r��;�(���c���
�B��"�~�(����nt����g[�s:���+%W�=�m���I��*�H����$q�~�����?���#���=�:�'rj�v�>U�	KX��(�
N+��2G[��&�Y(�^��`��L�*��N�/2�����Xw������%�����)����}~6�w�[��E����fF2��������t.�}�l�x94�W�[N�e�|���8C����c�2��_��gr�X�	�sS	�ZZ�V� �3�����S���8�}�W�}=^z������l�Y��E����Q	��	Q�w=�b}(/���)��8=%	�sx��qSH�,��8\�c�9C�'��_}G�����B����<�.f������'��;vm��6���y6f�;���RHNB���Sy�9����&����3���U4��"����&�EI��+�K��� ��w�3FI2L ��c��f����6�
(c?�h�(C�[i������p�����Q��mYE�O:�cg��oc��z�,R���h�c�q����U�(��������r��O/`��\�n9%��Q"���G�)����e�%��>���T?C�S.B��&��K�
��m�pG�����k+����	f<�$>��������s���wv2���k��	��6ae�c���a�q��Tm
���g�I�'��d>���,�����n�e?_L������O���Wh�	#���U�g}������������[�u��h�����/a/Q�U�dp�U��OB�^���eQ�O��z{���K��i�����f�6��D��:����d�������f�8�����h��OZ�m���xI����u��w/]usn���N9���	�FccjS9�!5�����4ZU��k��EG�����1�� *�y���:���
W��_m���p,B-��C
$����&�Uu:~�;�u���M~���=�In�G�[H�d3�72�=~l��m��)Q���ll'��
��L���0%V)]�?�^����^�$4@6�L���v2���Ck��������rD����F�qj���.���Y��b�&�3������|p������`B_���i����
qBoi��wKH��r��o�Mo6�Q��_��������:/���fr+0.���w^�L6�HM�6�����+�X���S��� ��AO���5�a������<U @�)�c<�(9l�N���&����e�t�C_���#���D:i0GP�l���KU�x>�*_�Y��I�=���
��a"������pcp'�&��������:w��0F����ot��~�����uW��������K��
S!�i�LC�\��]s:^��WO`���������u�`��`Cj��B��n��.�|���
����
����md'���dP�*�'\e���R
�VnV������x�i,�;����D��lB�o:, @�&���=�/)��,�����nAun�%n$\�'S�l�P��=a%i�J��e��6�B����oN�Q�\��S=N����*�(�R�hY��T�Q1,�=�<����������$�P)?���<|�rCy�j�
�e.�q�,���t?9���������D��Zl�qX��4.�+�h����;Y?�M[u_����( a����Ir�8�+�q?0J�_�m=Z�![zgd9^�8�`W�{�S�eU�L��$�`�]��1&N���%��6"3�|.1u~r��*�g
Z��r�>6uO%�#�f���9��3���%��m)�����7��n�_�Rx�	<L����j�ouo���|f�O�u{��rxnv��_H����n��>��51����u��b1�����m������c��w�Yh0��i��4=M*�b��g�6��z�����,~����>�8ge�\��Y]��+�A,J�L��1�5���y���\Z�
Lq�-9x��w���9��|�T��������]QV
�m&�%�zL����&D�>~���C P����hW��������JN���;����
2//_0M�~��-�U�ql������|wT�T�B}� �����1v��'?�{��k������z��|�r�|�r�T�QJ������{�/�W��t������I
��u�A�C��9Q�Q�.���y�=����Y*sXo�P����(��!HI�0�:�9�����^��rE�������<�*�s����Sw�/=K�2s���$�x	khqA����>�8o��
���-�Nb�
U�}���{�n1V�k{@y�.*@0y$�Q
1����CC9@�`D�!�Pgbh(��=�=����k)������s���>�������v����O���tD�q[5�N(>]�P�!2�V�oV�����U~Xt��|0A`�D3_'�������7��lA�;A��j%�{=5�2_I1���[����42I4"���t_��9p8��|G��I���r2S��r�����D��n`�]j
��G	�R1���Qr�a[SI���R!i�F�>^/�����^#�-�oV��}�b�+�aa�O�r�F�
j@�.����s}>m��A���Z$�-�W��p�����/�`s��1��m��G�F����0�q�G�`�[D��3���y���"J��0N

�H��u�����n��[��n������D�I��pqc��T�������h#�Pr � ]�c�V�T�����I3�Y��`���n��#�~���QD��������3�"�
3��r,vA���t),�W=�����=�5��m�U:����0�.�)n���~�e<���-���q
�<�k��.�ho�@��vj&����[��554��r��@pHm)�^o����oz����A���K1uK�B����������z�6�k�������=$T)�l���M��%�3U�Z7�7��1@o�4&��u)��-���v�Me��BN�C=����4�z_&U>������H�?��"H�fV�.
����>��� 1�l�?L|���&tE��d�������@^��!0,i��|!?���&�N2K������LV�!������e���������;���u����
I�>D����EZX�W���Ct����/�;H�U�RWm,r��p���nQ�jpn�����od~��|H������Z@�^��W���)k��x��Oe2��U�	�������������������~>������+=fr�"	�'#V�L���uE�V�'���*�Rp���mw�i�}:��yJ��N�����(����L��b�I)0&�]!�@�������Ym<�'���&H���/���f����n7���H7�'����k,
A�DRj�p
���d&���)UA-:�L�[L���vt�>���
�����A^��.��R}�J�R�����OC�T�
�v�W�Y��X2�&�Jb��e��������+rWdfbPt�����:kJ�3u������������>l��'Y��V+�A�a�U�%���6+2�Sdrf���+B���|\��5����{`T����y�b��I�yYi�\���3�M�jG,s�mXU�IEq"G��V�����~O:`�Yh(HC����u����XC�~�������<���e�v��x��+Ee��`jE)[�?n�����D��z��:}�3�2���H#����
��H��k�����X�z�e���s?6�S[���=8����7^��^�3��O���.V����g��
E�#J�A�� LBLa��`�$�����s�����T�@����K�*��'`��as{���^����B��\��K������X��F��S�����z�7�����@r��rN���!c~9i#V�Ql��)�/���a�w�Cz����_���.*j�q"������{Y������}|�db��8L2�\��pk��������Gp=��
o.�.<�LDIDI&+��$-�+jK������Z�KQ�`�_��E��M����R�5\;t�B�{�S��4x?d����r����}��Tck��H�@�N�IsKUY����`�P���`i���d��G�"4�b�F����� ��]�wG�����DY�&X)Oa�Y��LL���S^7��Y������Nc{x��f>������HOn.���"�����!�?_�7G`�5�������}�t�C�PN"$��U.��B�}�������O]�����d�zh�W��Hc;M-�J���j��O���g�J��E!,�kb�5(��24|n��OY�2tr�7��R�QH���K�M�b�?�U�y�n�����\.�=w��`.�YK[Ql"�X	^
�����]%��7fQ�����6S��W��c�w]@��W�Q�����@AA�(o|A^
�����NZK'�����i���92��	2�k.�Vb��,nb9����G]��)e?6|�|����-�8�D�cI�@�M�q�9�� ��~��1�+�u�w?��O,x�2�s3�����fx%R����?�5��~�@�����V�:l$&���u���n31�� |���n���h4$:g���m#�J��e)������Y�/�S/����v�*w
�H�6��u��;[�M��$�~������xW�v�U�!���7��C�\I1���y�\U���I.�
��.�@G��7���s�Y����F�}5������9�%~�������q�+bG��+k�E,��*�5s�yC�,�!��}[k9�E�o����:��d� ?��Ux�uU�+������a����������W��E�D��|W���p� ��&sv�����=y��yE����^�44V��z�O7=J�b�g��H��<@]Q��6x��*E)�&�������v�O��'bY����;L-�<k�J�R��>�T���\�9�Q���v���s��o�����
 ]������|
������2w��,E�=m �_��~]���<��)�a(��OR�
X��k.����M�����l��9�Gc6�[�,<i.g�r�\UX������rF���0(��6��H1l�������c�����L��v�e���,�H.[��/^�3=wS9��K�x��C����/G�8��v�A7�3C![�X#�9$@r�}=���o��y�=�6���N���T&h���IHo
����o������
���s{	����d���X��!6$k���&=��R���
e��r�� \���+�9�7%���lwVb+�e�*������ ���\��U�����3#��'���@�eP^(��}�%��R��mI$�$��)k���i�!L��������A!��9�4�����sQ������j7y������73�-Ri b��rw?e1Z�8r����'�N�������Z�Ssca�����=��>�;��$����1��3����>�Om�j��������q0����H�)�t��^.���[���|n.�j}<�/��xr7!g!��a��$c��CR:@�S�R�5C(p�Y
r�tj����2�uUDid�=f8��f�iw|�4y�����FI���]����/���F�OG�X���$����r�up��Z�%�RF����_��Ol}'���=f�z��So�;p��p��|M/��f��]�������v9eGn������S�[�J`��R���8�}�wX^�GH��zD��zPB��6�r`q�*��b�58��	$�*�t�d�c^>;�����c�U�Ym�^��n+j��EI�����=A}������3�$w���P�7�d .n������=��w��o�����s2+��
�d�;�(I�1��h�����h�,x(�eO�=�|V�����t������]�_�7���y.��y�8���t�� $h�8m�R
9�+�!=���[9�=��D�9�b�����\c-u�0+iK)�����5�Z��M�t�I�m����Zfq�v�ZWr�k�{i������j�G��F����Q3��J�
w���#��_5l�r����Y4���3pOq�7AI��)���!��K'��"B*d�|�Z���`$
��%���s8�+�71�#�����m��e��M����o=����Zw- �7��IO3r�
��C�@�I;k�29��������e�|�����
>���Au@1Z�2�_~��T���1����[>a=$�����#�7HB��y�Y
lO�\R�F`C��rM:+�dP����7fQ��\�Q��j2dAw'��y*���Y�a>��[������?R7
1���4(ZW��-t���0�x����;�@d��(����k�>����]�~������H��s��F����}�\�.����]����n���C�"����Kw=�WZY$7�u0�
����e�;
��w9��>���~��S����}$�~��C�F��h]J�y/J��Q��j"Fa��(�[�O��t����o����Wwzo����;���yEdRp~I�X"'u�p*S�����EN���==UR_�*���GS����M�����_��3�iK��@��]�^M#	2�v-�9Q�R6���c�����~��$4�LB���@��>���M�t����3� �7]�+���~������s�J� ����"�rWg���L��u\7�gu���,�.r�����!���w��!��.������f{�4�u��K��eL�x'��������I�Z��'���o=��{t��)��o�
�*SadH7,���5}d��b�&��-�z,��
,��.����Z�tImT
�"Qr,�S���������3=q�huwu�����CW<&���W@�Qf�L�F+e����'���k��v��V���������G�)(q���/�����
@�Y	V��,n���%�z��>�Hs�G��b�FDS84O8��o��?�(����md�I&����F�	���?��Z�f����]��2(�?b<(c�rS�=��S0pL���/�M�I��-����%L���fE8�s�V���	���R�_N�`�,���q�uh���t��+���+�i�rp�Y~k��9lvm�����)R3��5|�R"�=��Z,�Yo���Z#������>Z�Yx��0���L����)Dn}�t��(�����aP����V��.*��F��D[��������6dAOI&����/���Y�i(���
8���wL5�(�1�"��������pe7RB�C1�7��:�ze�
H�YOb��GW�V�@�.�W���0��Z;Q���1!�7D�AP^��os����a�F��3�2�];20m,������g�E�R`#!��O��d��R^m�s��EF�y�z��#��c[�o������&��0>��f�y�e,�
{~zs
�@�����{;w����8<�� ����`h������4�}��������6�$��~�\�`AJ ���'�B���������K�6�c�qp#��R2L���c�;��D)�D��L6������m�^����N�x���������r:�����F���
w���3"+��8�j������2n��p�����B|�i�7+�/;��.����a�!�"���=����T�mx���N��:��������n�Y6}�G}�[r���+#�j6rF:����a����3w����s}<��=/��J�s5������j�t�C{p�F�KnK$f�)�2�|��rZ���9D��{�;U�.i)����]���6:Y<Kb�3?!�P�C�Q����t�?zA.�����W*���&b������DN�Z��1���~��"w|k�7�ZR�B��:OA�*�)�C�{����������Hc9���:����3,��������$��O!���H���p[1Wtz �H��R��������2�6x���HT�L]J�������9&���iN�&�^U�!�����������b���q��3B�^�0��R���������s_8}��2�t��;�����o���B�.��0��`��i]�����ly���a��/�j����H#W��(e�[��#?�/y
\f���E��Y�������/�+���5������Z�m���mt�:���������`b��6p�^&�X���m#V��^������F��:?��S��(TP%|������Y������3#9z����T�w����������c������Q`�5�EV��.����_
��b����|��G����x���3G������������s�md�1�{�kF��J�z��g���������Z(c���n��;39��������{��D.��������+s�������r�4�>�k	�H�!��(��t��7����7R��ZV`r�#|Gz�����/OnPl6�����s#!�����g�������L)X����> �*����yEE.t�1+Z�`�G�5��<���Hb��d')+�Y�t��F��������uF�v�$�w}�(��!Xe�����U��u}h/�Gp�k��m}���������zCo���I| ]��|�\�������;�O�K�o�C��M�,�'���"���W�4m�Jo�!��%�u���;����JMs���j�f��j!^�
��FC����k��_~9��;>��>KLFIh+�;t�*�>xd
�C[���<�C=���C�^~.)����C�8���RV��������'"�|2��Q�
�1{w�<IW��aU����j�G7�����W:�*F�����J�Vg��I�!K�����w����q��� ��\E8����G=J�Y�_�����Qf;�l����H����d�����W�=�^��S��w'�{>`�������{�����k>����=�]MC����t��)rA3�������#��F�Vg(/�r��`
���^�N���m�y������x�b�\0Y�S�N��~��j�3eb���Sw\��zwsR��;Jm�;��ABt����TD�2y0r����9u�oNY��'D�P��{$r%	��^u�u���d��Q��$6U�&!�@��s�W���(e�g������i��+eh�q�V
�����s���5���S�U�������u�v�O���,a������>|��<�����3�A��:�u��;(���[���
��`��
��b�
�R�����))�������t"*�������*�|;����X���^)��N�"��3��H�����a��@�s{q��6KeL@��HP��*Z��Bw�}�[H�b���|��������C�W����q+� :�IgDA���B=��2��c}������I;u��u�������%�Dy�~�
���_N�a��$�tO�}{���8K���)�${�"��q����]�DxB�tF"���V���/�x{������;�������,����6������1p'����:P��}e�N��\�f
]Q[Jc����{!I�^G������{Wr����� ��[��q2;mk��$O��a�G�"����Q:$.~_{���gU��nNtS�f{x��f�����,!�o���3V:�
l�,2��R����|�����=�,��!%s:{'Vyst��e`��D~-U+�i>������k���
d�b�V�U+���3�����n�}�\�n�(56��'�����x��n���H�j��<	\5����;M�8���/��q�����c�m/S�?nw��70�]R���B�$6x�z�dr@g�
�X������S��������U?jf���$*_I^_Y��0���_��E�9
b�����E�P*OS��(t�������	!���ET��j�`���IB�z��/�n�������?��O�?��?��*3�eF�MO�7�����!�����6��L/L��C|R����l�y(-`���|��a���o[W���;�i�<��y�����H;�@j6�������.�a����"��,��5�g%l�^1^�0�9]����"�Ti��R�[��Q^�q�7�(@h��?h]	w�����J�dNb��Z�0��?��H	6Z��#����I(��O7�$�Dt�����T��b�K�,�>k�b�b���+�Ja&�~�>�D?IVY�����.E�3U|l:��G-�gwl����3��0�*�G���������X������N:LJ��6!���������p��K��p�Y��������z��
���AB
;F��(Ed�kN�#�����r�Eg���������7)�J�Af�+����q������m���Y4|8��rO$s�
����D(*����l�&�Di���{��
�T[c�qj�}���������q��sc����\�|����������f� oF�q�R�}��o�C�r.�Jq%[J"���X���]�?i�?������u�&���oX��������h������e����p}G�!�v��#]1� ��CT�l�N�p�����fW_��4��Y���\�)�f.��a1a����R0��{ �`I"�g��`*�����+����i{ZC�B.Q���:"J��^�N������]w����c{^�o{x=���]Juw�i3+�BZ�r`,�8���>��`���|/�
w�����no&�r���'"�����K}>�[^?4O�z���� R�`�z������Z�P8^��N�7�S�#�6',/f���L�:����U���$�^�0H1?]F1�������������u���0�Z��.���$jM��C�n���a�����m��_�{��s[�����n�tv�2��Ns����i+����|�z���c����JjN�4��P���&�+MK��F�"�����s�'����S�T��49l0�"�E��� �J�\�^L\m�S9���7�\?�7bN���� �Cf�}�[G�"&�
��/����������~��-D�O��X�����1#�q:#�u������5����[�^�L�]Mh/�/�������<
����
����`�7������.q*�F�K��������������]��f����N��v�����4g�5��d�������o��_���o��y&��l��$87g�����-=����a�;!%QGXH3������/HIT�h0�����u}n���?29m��?j�N����U�H�R0�@��z�]�b�J���-���C(Rf��
)����K��c��V���#[�(���h�s�V[���D�U���g����l��(Vs��z�}�@J
\{)��9_	[
ig�����(��~N�@1�+��R�R ����z�C����D���:z����D�B�M�&xn/���=����(���pM.EX]��xwIRJ����Mw���9wQ���5�+��f8�W��T�R���	J�rJW�d�(�[^92�G��6I?E�AL������S�n���|D'��qr*_��8�4r�>Q��'{D�y
Y���o�<�����`����3���w7�;4~][��g�� |��3V���4�_�����c����u:c{cB^�T�4$�Q��) �S�Rh�	��^<*<K������6atg�p�z������1�$<{x����
��(��B�X��cwG���5 ��,�j�L����6+@��~9A�)�	_ ��s��3��Rc���*��L-�9xAH$������R������������
HP1�Ew
�e�����u����<������}��M[3}�o*��M�J�b|���W�L)w�\ =����]��A�jg��J(9P0������"3w��!i��"���z���f�g�@I����\��Hk]��K�.�/g@���f�Y�Y`WMy�s.�T!i����B�?��������`b�C��L����SY��/>��T�lP��PY���O�5���:�8T�R�����h3xkP�����y��jTE�6���"rd����������V�h��U�w��#T0�*��������T�-������1Ge����0'�r*k"�H3���6�A�	-(������;622�����)
	�p�T������m�x��t���gp+8g��Ze����)R�C�8�a+��M��k�K[_����9���?2�r��gn���3�"��p$�a�:�KH��G��NH�bA`�E"8e��G�zgZ�X��l�O�0�Msi��f_���]����
qK���%
��K������	�nw�"��*�X��L�j`}�p���(�b�����<D�fG=���$��gS�&�d=?\��M�%�]��/���c3nf���D4(�q�[0����y��2X���2>eM�x8Q��:'�6-�7y~9_|d�O�=qn�`e���Jk�Y:C$_	`eL��a�A�����~!hn0�l)�x]�����YY.���a����jl���1t���:^
����c����@��f]�x����QV���o+@�
���m ��"FQ_�.b���\��jp��G^~7Y����7Pk��p�^��W���2�����m��A2pRh*/;7<�@���_W����$�3�"�k�E'i��*��9�J�Rsz�j�@�	��!��#�u����F�h1T��|��l�Y��g^Y;�T���q+5��l�f_�]5%Ho�t �~+�>�����F��~>�����S{	�����,����H�0X%���"p� m\��&.��'���4A4�WUx�.������@���1
�t�z)(�����I�{��J��1��g���L�^Lf�yr�}&�!�"�����������Y�	�����}*���^o{�{��u�<�
���1g��y(�%�g��AdK�K��n$aG�>��>j�H�1bs{q%*T�������&����������sNo����}7`F)����������������pq��?
9��u��+�23;�������J�R4f^��^��Z<�R&�<�[����6���������l�]
��[L�0Y*J��8�zH�G����K�ZY[��ys8^��/7)����)�>H�7�b����|'��QR��'`����0�������4�[7~�/��rn.���H1O�!�l
������[��E*!C�D���M��2tP�R��c@�UX��V�7��o.��/�MS����~h�������ON�IA���`w����?E�a�BN��P�$�<
�����~�)`��w7$l����|F���zB>��10~@�.�Y�~����5co�����
5}#��!y��q}9�M��7������g����k��aNY��8J�(>����L���-t��a�X�B�]H������h�����B�4����"��9���S�n{��gZ�����#�]��/O��&H����7��bH����n������}nv�a��bbX��ef1.9HE3�Ve1�y]��mo������t�RGI���t��*$�_��xx)��������^���_&�H-����$k4���*-�1��z��F����;�.��<��T)�������,����Hb*C��\��T1������~/��qq�|������2$LH);���Z���z������������<9��p���opW@�[]���`�o����(�!�q:�'_QYS	d���X�^C�[��Z�������	.tw���=����c�_&+G�T_���=e!BD���(�AI�����j���vz�R+�Cl��Xh��?��������������)}w��M<���"f�Ha;��|���f<������x=�H<O�K��@$��-�,F�E�t��o���ON0�
����L��������n��r+#�JT�h~���A������c,J&��F=	��� :�P�����(�I0YO/�S{h����������ESu]��<�3���'E>����e����H�z�O���Dxl3�mX'7�hU
���G�O��~������s��X�}h�X�t8���>#�w=�����:i��TU*Bo��\U�`jV1j$Ip�-`�����S�)�������$�/��i$�([P� ��"���O�I	5a[��r�O�Dv����n��q���64c��dC�V`��s�erex)�����}�]��i���9~��'
��
�^�X�y*Ep�,�����z�"��% u��Iw��F�����B)��0�3�������3���,�����F��,�������A�����*�@~��V��Hi�[��CC�|Z$����[l��f�D!�%Rz�
8G�S���e��yvC����/����p�l�\o[�'U�tp	W�H0�.`3^w���|����Q�
��l3'p\��A;���.����|S>��r��E�l"LP�M���!F��;3�]��X�L3n�	���|q�9���`��[��G���7y)1o��4RiR"<����sPe[���ZB��l���������w��t����T%c+dH+������g��dB��a���<�	p������	2tjN��!��p�����a�2X���Are��>�����%)7��H?�t\ue���8dC)�����������z��<���ox�@�&�kX1�������]s�}��jz�IZ:�n�������|s))���J�p�,�5�I[�&]���q���j��<;�����\9����L`�K����}��y�thv9�pm�*�r���xd��]�bvc�u#`!wKL��h����tQ�w�� �6�,��>v�����st����Y���4q��d��
�3p�8��!�q���4�q��e��$}f�n�^���~�S���k����^MKcb�E���RH�ZKK���cs�	�0�?��V�|P�f��0�s���X@[������}�S�2:�����3�� 7����^A������������k��;���Q� ��l���]��8�n�i)K�;D#G�(�L���/R�bj��u=�������qL���<�8=�u���C-_��v.�a�R���K>��V>;u�����m�<�y	����&�N1b"G�*=x,.�����{��o�'��'G��{��D����5�)��"xg`����4����������\�����16(��[�F����g�([1~$��=��+:4�8c���sT���ZQ
�1hv�eOV�(�6�l�$Rab�9�kH����lw@p�q�8.���$|�
�/�����9���?�>t�������nF�H�+��B"K���g��`�0��pQK��y����H�����y P]���OY#���F��9j�j�-�:B-������f�a��������FL�-�"�d*�pm������Z���x��xKHP:�'���GJ,&I�
l���R���{���2>�!d.��[1$��	2�,.���9�;��H&���SA"��"��������gt)#��<�Df�m�+���q�D.�l�t��v�&�.a�k���r�$����*
�����*�G	x,��b�X�������"���h|�p�"�u
L��;M�9�5��"��O�	������&�w�� �@��@�(�N��q^7����o(y��h^t��)C��5�:���A2�N�C���>�y���K�m]������u���*��d������e���x�T�����������}�������`
�{xhJ���/�
�6�2�S������z��]/��v��%YL�<p�4�(�~�YiV��s����id�+�#�wH0MK�aKF�H0wO�-	�����s��7D���2-vt��Vhn�;�~�S�\9����~�9�93%���Me����#�����*����n�x�����Qca�8��	�b�\�b<n���W�@��Qh���1�b��<t[D�#IX)
��}hvPx��q��~>�Y��b�#G�hf�v�/�(����>up3{�j�m����g���0Gx(�cp�7V���r�O:���� �1^a�!��C3xM�����0$��n�p�vT�`�^�f|����or>oAU��'��R��;��!������^^�-dILZ��!�-���>)�����������n�C8'���	�P�:8��������-���9}�4�[��R�b,���(&��hxL���I�T�R���Y��U$+����b��p���~<v��&��%�u����V���aG.?��]siv�'o���?]/��5���hv~�����e�_�o�!��d*0�(���(E��1z�p� 
w���R�O�$XR$d�R���M=�	7�kd�Ie$�#�sT���@88:���0���'���^�0�����[^ac��k!y1���L��4U����b1��+�v�q�Z�>c�|�����;����cV��U�����*����&��
hw��9����x�����{�{px('],����m�+z��s����������]��;���W����G��~���_�i�Jw'y�Px��$pV��`��+c�k\�e�Mfm���P��#X��+�3��/��`XU��n�����������_N��H����5mP�k�#���<����K���rf9��o����Ks9�]L�fL�c%���1�-m-$<�����
_y	�O�3�\wm��e���n��@\�C:�v���UJ:�-�� �n���i����_�di��^~8&����e���}a;\����n,
0u����o)X�Ro)���X/tb�I@�y1������~���y������<�sB���	������|{�c8��*�:X��l����f,�E��m,��*����}����.�r�F0�)��kC���w��N�BB:��)��346�[�+�V�����
���W���(+=gG�U��;Y��k��B@8-�p��FN��=�,Z�Y�F���#dI�:��lwm^� �ii������V���`++JQ���������:|6��j�T�P�k�`Hf�7���>�,OFJ��v�I�]C�>25X�-���]��3�IV'���������U������3X��K�
�_�!�',tu�ux�7*�x�~+q�,^��`��������<DH�\-�o���:������@�w��9�Q����{j;�c��9������f�xT"�'��w���q��G�����o��k�n����}�w���b�
A�����A�T
��]���������D�UmB*&U��E�����kK�������d�XsH����e2���e2Ebj���b�,]������%�����"��:y�;G�C��k6L���-/��P?7��>>�����
�&��j	��%yD���b��uw��$��y����12��ka���P6c�_��pY���?����oa��]���I��2�����i���/.[T����A���7�
����Y���J���	��
9�h%C�^�>i��jZKr77;*,u��.�G���r�nT�IOy�I�M��lV�Z
�����0��x,����zw���-���[�;C���hc��	�l)�3Y��bs7l3[JS���!�)CI��w��`(�WF��
:�03��y�]'�#:�$�b1pF�8=���
�����"d<����QD�^)YJ�&����5��?�:��d�[D�X��"i��������w�9�_�/�x!
E_Mo��5�WFI����w^j���Z���$�P�U;��K*/�E]N�U�U-�j������C}>��>����C������C@�z���0o1�#��x����7"`�Rh���
+�� .o��D�<�'�4�C���[D&�8���k��r^;/i��l%�(`�(�(��������f�mO����;��q��6�r/Ep��������C%o	�����:"7�?]O7�Sc���7a2���=��i����$%
�����C���$A!y���	�����(
A'���o�odt��X)l�Sw\��s}<��glS��R���R�7�D0T��,Vhd�3��
:.N�����+1o�����+�j��x�n����B3��? �	��������6��Zx�%J���@�aSx`�����D��*�~�a�2�4!�7����}��`x�m�s�!��Az���3!�����1�?���W���N��5-����DAC��2"�Yec����:`E%����}N���?6go�\�>�$�z�h>m����������="����=��.��S�q�{$mXEL�����j#,��d��V�#�����x=���Mf#����3��
�&�y��	���mp���7���z�@:��o�>�$G�C����O9�nr��|��������4����[������z�c<�C(��������H�QH�kJW������{��~�E�w���,cr?y��d:��M��S�#��D�h��	}�����.���?^/���C��(���;����P�1��EEP?������tt������G���)�5�64����G���F����7J�,�PM�V��F�&G?��$���O��k����w���x���J2q	U$�c#2\�n@�{89����^*�w��b
"����\���l#���z-gvj��9%��w4	�W�.~}������}�\|BV��j2%}km�\�$��������Q��>�.���;@����Q2L���+%����V�k����k:YN�����"�&t���^QU����������3���F� *B �8�&g`
j��sC�N��H�s���_���(��eQ������"70���d�9-!cH�?�������j���B;1H��g��(�p�����WB����M =���/�)S�������
��o�����M��=������r��7�qv�w_~�}��0�J9�'G�AWZ���Z����i9�tD2�QK��JS�s ��H�Rju�Z��#4E�����O3��+�$�9�" Y[<�����:�5�VF�	�b��ZM�l5���
��U�LI���t����Z"7�^���bl.��a{�7'��i;��_��`&��o���7�
w�H	>��@�:B��?��|��`=kH�&���+ ����{VL1|��1���D	t��-�:0=�,��,�����m�fq�$��.f��`����`�Y0�(�������TD�^�G�JT����t���
��W#[����;�_+���G�[_
l�����?���������U�RL?��w�RT' w�5��|��]N!e�G�m�@-�@������G��>�T���`�W6��!]b���$-�����0�D!�����]���_�;��e}�?w[wM���Yv��`�����TZW�|W>�� �����
=�e	���F����.H)X2U�7�l�����[�	�=����m�����H
�L��E%5r�k������`���~���r�r�:$����2-�z�o(,��`�Zq^
������{���G�]��"s�1P9���;�st�v]C�fw���)�I6X��\�&��R�'9
�R������Kw���/gw�BLv�Q#Q��E|mgl��q����b��������.KZ.����
��$���,����h���rMg��l(=c�0M+OM�E�s:x�<���Q(���L��j��6=����	Y*A��D�����X�, ��R�n���k#[7����,7714�ye�4���n�jw�7���q%;+�G��F�_F!*�9��[�����:�{���A�3%�$�YY�TO(�BJ����M5C�}q��qk"p�����z������}�����F^O��x�������q45�-�P�?����=y�e�����HZ]��y�`m[Y�C������+�c��]���GF���7�^��!�}��=�J��e.���p��������H^H.!����i%n�q?�eCb�G��s��M9}�Q�N"���sY�s��"����o�,��?\�;X;^>�`��R�8�Nj���G�D�5����J�J�6g��!U|���y���H������x
�+@����(q�F "��W��34���\B^��yoh��Q�W>vbS����3��G�ez)����G���
���n}���C��I���������d�7G8����_�R��v�@���������bS�q����h��
�H�M=2������
0������xsK���1rpy�ls�R�)��WS�H7 �>9������j��\�H�g?}AM�Pd��yV���F��A�������_qt�Q��w0#l���;��C��:|���E���^�V�����b��wdk&g�YL��If
�SGE��������?�'�@_@y>����'�JA#Q��`
�h��B!V��"=������6	�	�
�0�L�[2
G�ot)�^��0�X��hq�'.�� ���$Q9q������������8���������f����Hl��]1���g}�b���}di��o�
w���Xg�L�@���z���n����7D)��Q������MU��S��l�3x���5n���p2^
7�^��f;w���U�����;��Q4�<���>�2�3�A��#72�e�;�K4}}�s�O�]�������{����/�u$�B����!��qF����U�_��s�z���`�g5���x�8-��\3���v����}4���D�����-�g Ms��X&������"�>� :��X�C$En#��7����R��(�����J�����G������������#M��������(�H���5 v�����F�+�%�F�q?�A$��U}��?������t���s�|.wx������R+�Ud�S.��jQ��~����e��x�w���p�	�J1�0�;��)�����=5�_�V�v�~����A�i����!Y4����,c���u����<��r��9�L!C��G0o���'R���T��*��-��
����4H�$��F�jSS����D����-�� �*6u�l��3�
U����R�8�6����"a|���1��o�kHzq���%�8���������j�1Y��|c�Qs���}nyS$��T�ffS���"X�����?!w���k^~���tF���>J��.�	s �������kH&�s�0��4��Ns$S=:��\M��w�(����'I���P9.���^&�$Q���*��O,��?�{
:�g�[�[P$q6W�]c��va+Q��"�M������:�
Q�ci9MkN�;N���hHz��(�C���.��E��l`��z"\j$�%�U��r ��^���(�}F�CnN�8q�)�+�6������mw������PM!��WS!������,�_���aM��I/�l��4��8O���y�nsF	��s/=��p����|9�z~+��p='E����9��c�8h�
9RLe��U���~����!����cQ
s!�)��z���x�M�>�/��S������k	��b��]����B���6�n��[���2Rh:�x�%���6�Q���rz�&u���d��~�z�7#I+�����B�b�����z�}X��s�~R"K�kHp��B���[�Hdv�!"X�e���x��������S*�p��S}�em���<Cs��Lp�Ff�(�����
�r���Hq<v�����<Y�q���z3����+^w(bn0�8C_�J���$��K�X�5��fF]�E=����7gW�����kC��#�v&��UC������&y���f�XD{H���=,��������m��c��^��^��G�m��]�t�)3���Z��W���G���NC�XL����M�!g�=,�$��O���. ��9e���62��R��d��,��=��eU��:Z�@]���WKp"Rp%�MD�
a`�A������9����NE.!���Z��3ve� ���i�n��
]61{���L�����x�Qfv^n@W�P���4�9�#!��ya��[����[N~��gl�4�����z����8�v�j� ����l�u��v�i��:�����D:�P&W��R�k��%�<�$H�+	������L1T��9����O�i�*�V����o]��W���%�eW�we=y\��p;[|�x#R�j������=�V�z�1>_pFM��F0\S����5�}��^���
Pf�l�r
����v��E~�CY�������z�.��x��J����9O�WK�;22�R���z�}�^�S��mN����O������������$��-Mz@����#p���R�qB��N~x&4�o���W�\�]�'bS;A1xl�c�d���E$�G7���1b�E)��UP���l��e���2�XB!U�f�R�\q���~<v������\D����q����/[|�=���l��n�C��14�w$�X~'���n�PR��:��[W� 
|8t)���` ��^~�
�98&���:GH��Xk@e���_>������>���K���Z3�%/$��H�[:�I��zSk��5��:�5I��B4�<L���,������%�p����dr�y��<S.�5�fw|zr�w���zi��9�G��B"������b�&"�$�Byi-��
Q[��o����~-������&2��!7s���n}=������<��^~j$���������Y�v�z�hD+"F�9]��K;�pt�	��HO!%������E�]\i?x����?�9�/�m�hd�Ia@����J�Ip�)e�	0ZWoO�<,m��Dv~����D����Q�+������Wwv2������]e����<i��7�aH|�/MJx���������&��,�Dj�N�%����d�S���2�� N����d�*:�y[&(���D�B2Ez��w-����;���N����f$�>r���������)c��l-;�0!0KE5���1��R����{���*&M����
88b�Rl�X�>v�i/�:nNO���7}������Q;�s�P�Z0���������?27:����s7:�VT����s
��pz/���q{��?�~��G;���Z8P�����h24�Z����vK�
���������f�jmO�o����(S���b���(�|��P����'b�Tw.n�y�r��v�������|����~h�����$#_�>7����|Z@gyh.���?������E�Id.�2?��T�=�����!#�;^�Qm���&�;���l�t��"���"�����i��\�������WF 1ufU��)��|e��){-�����diJ���!���q�z1k���.���/���|���o�F�3`��B�~�,��_��O �y<��/@�Z_��Yc���EOd>�	��pwS�������T���L���
�K\$�R�|��r�k��v� ��������_s�F5\�]G�vC}�r��q����s}>m�
z�Yd3������`���?�TYF8�+�Xt�*R8���������w����A1���%������J�|�Rn�f[����K�2����$fc���Rt�#8�{<F�s�J*+�p�xc�h������s����DFg� ;4�S8#���+j���5CTc`�$#"��Bt<���'4J���Z��
p��S��M�>^wY�����#>#�zM*�%��$VL�b3r����7��>1��T��H�+���B�#J)4�r�;�Qc��a4��������(�$
�`�37l����
m(B��p���n�-F�<��@���f�	��ZKx�#��8w�8���j�C�X�������!oz�3Q�T�J��!�����_���r���fXW�	�����������/�^6����K��^���K�y�hE�bp�W<��3*�?��������Y%RS����;�o4Y�������g����t����� �����:��A��!q�
u��~��z��w����5�z4����)��E`<I1�twsq\&��<	�G�J]��o���]�t���7K�b4�E�����h�7L/�M��q�|���y{�X����H�_}��'5v�R���M��S+K��zU)mE��W���w����v3���<�uW9��4N�+��r)��L)V:
��v
Da��}���
���!�# ��R$7��+�W}��6�]u4�tK���W�
R�TqwA�����{��G����Dew�3`����p.��YN��������������$����[�d��*�t�T)��_\�������k�ri����RO�y�V���-M� )*��
p�:�b����_�O���B1l�'���	�x���u1^��2{��
����yn��r��q�;��6]������dl�4
����r�M��7���x��\f�Um/��H
Gv�n��������.�/S���I�&I�`�b�'�z��=@e�k���t
C9.�����|h��-���>��S�#U,������B�"�z����\>f�Bh0�0���+����� �����De>�b���	5��LV�I8�U�Q@5����F=���p�l_\C�gJJ��(xg���������a�|�:�5��i<h8L��BC����4�z+�����q���\��-k�=�V
���fU�R� �@�`����/�&������
n���R�R����5�����;���;�$�<g7�X7}���|�oF��O\U�'�6P4R�#�����'w�R�6C���8P��=�mkB	�$������K�
��!o��>s��"�L���J�R�p��1"P�?oSQ�fE�laz�C��n����v3���/�KV���}����p
�����:��=.?k��`l~��2�g�I�p����D{�_��$�]8p�q�=
b���l����f�Fs,_I��%x��C�Y�x�m��F������"U@cq	��E���w��&��CR��u��q��\�~����y#��7�t����������t�#�A8b��T��g�K]��������d����0v�'c����co"N�r�y��?v IsW���S�U��r3�c��c�))�R*����;��|i�C�=l�����j �W����95
3������
�I��,�NJ���
�c|XD�����k���f���AOg1��
�����u�y�>�^~�m��j{�������8���k0�}B�J~�DWR!����b��i����1a4����@�^�(�. �R�$+�J��O�q��������}�Y�7���+����"/�[�Z v�U�A�:Map>y r��@n�����W���?�NMwYi��%aH�d��������{�U���o����XDj>���q.��+�D`��	L���4�,j��j8!8�"%�0����HM_�ezr�4�
�Q�P2�P�.N��B:hw.����8�o~�����������qn`N�YX�f{�*.p�dT��X���y�th2��"��Sus2l���-qL,1$��n���}?�����7�O9w�S>�lY����lT�����F�����y�d]M�;����f�@�3���V�$���[I�gTns���4h$+����|H1�w��a��J�>r�v��<����;^�=]���1O��[q#��RV�
�_�b4'wB���7��``�e+��4����C���l��h��vS?�d�DbH���58q��ZL~i�ZiQ
��Q���p�,gi(��CI����<�F�����~�����m���,��6�����a{t`I��U�3;���D3�1b�@�H^K�]iV�`�XA�Z�_��Z"�6*�����H�up�Elu.���lw-d�H9�����ML��O#���q���,e�x��"�qR\X�weh(#�,�xze�q�1��I1l�Wp7���9���(�c(��k��am)g��>u�2z�"��*��B��=�|W�t��:� �wSL���!XOg�l�pD#��9S!������Zu�����-�����9�����L�T1{����b�����(9��T����M/����������f����C���us�8��I�hv�s�t�T��b�	u�d/@.�c��x�����,���C�"��I��{����-P�b\/��W(8
8'���Z���lN+#�&���&������Q����bF�B�����pn���
��%���I�l9�-���R�V�����i��������������g�37����c&��-�r�K0�]>Y?\j8GW��l;��"b�63�P a�^N%e��+ro&���L�rQy���$6����B�����|��	;a���=|����T7D$0�qb'%�?@1$T������
���l�Q�*�Z��7�{7d��'
�9]>4~y�w�f�Z���,�?���9�I�����$�����v��+F�q����#���/4�
�]V	����=�:<n�t���h�V�_D@���A��t1���cR?v����d���01�a%�Q������I�xc�a���TQ)m�Zq[4��4�����R& ��q�?A�(�71���L&�N�Zp�B����F�B�L������M{�+#c���c����[R�I����kf��	;�o=���������� a�<	W��V'z�rEe)��}������2�@��*pR��T��R��$8����������GC
���`F�+F�� �+`�7���2]?)uZ�����-����12W��������[����d��*������Jr�p�3�����l�G���;uN�}�L�`E����j�&��H��(����b!�9_7>����!���g������7�����V������`������;+Ba���/�x�0��S� s(����K����5H��YV���J��C� ��<��������;���i#��R!����m%"C*����~v<��,;J�p���O[�J�m��tY�R���x���^7�<���xf&u[�.��"��c)���w�����PRZ�S9qI��8��qd�!��H�`k2[�������D�Ovs:�h�����5M�������'�0D%��saV���	�}E�`� P0c]&��Im6����Vrx1c^�Q����TY%�hk+V<^�c��2m�@:��2((�~�[�K~�D���0����]I�|������5U�yE��
���F��)��l�]	�6�E�]�/�m��{�����rXG*d�����Rx�_���j�UH-|��j&}�2��k��$���O]��k�A�9��,�[X���D�;�0�5\�v`�0����kw��#x����H
1]�����N�ZM)���|$x{�a�'�@����5C���)�'7�?u�k��
�����q�d��\����+��JOU �I�m�c�-������9m��Kqa�����"W�
�������	�1�FBp�����7$������|R��X�f�E����� ���'%��I��) ���v���������,������(n���2ly
�4��8��O��]�@+�sX��a�����eE)�t	�i�C�E�|^`�d���<�QpC� ��.{�<A�3YQ$V�����B��$T4��0����,jOs�#`
�b:]w�����3|��7`b_��Q���/���H�������LF���1g��*i�anV�H��"+��a]�@��<u6f��P�k�e�S��%S��K���dya�t��SkT�zr����`3[��=Z<����n�bp9w]ql{���3>l�\pn�L������X�>ma/��YY*�JR��W�'mW��A"?jF��P,��^���v��l�
;F�
��v�8?TED��-�*��6���$��p�H�,�M��!1�H����}6���m��,X������a���Y��je����e�v@�l��z����k>����T�)*�1 G���z5s`|�W�S���&)p����e�+Q���7�dr�����)�<9B��z�q�IC�S�.�K����~ 
�rYq�l��.��x~<���]>c
PJ���I���a�J-V��HP����T�J	;fUE�?L�`V7��%O%�]�q�Z��<��������U5��P9B�[�|��F��ame��J�#�F����Q2.�d��S-����@����?b���Oz��'�����F�P*�U�$\(9�0]�c�y�K�Kqu�Bv�g�����Xj�G��������#a7gJ�	[IZ
��K��DGH~��li�d)4��;n���i$����4O]��>Q!��������7;�d������V���O����;*������N�v������w���|����Si�NNO����H��h��R!�o��U����	��1\������$�
��p���>nh�?A���i��xt��6T�����*jv���z�e{���Q���o*+
Be�������e������
����I������c,@�&�����w���$�����;���>�������/"�	�7%��8j����^Z@'�Hd�'���~5me���(�Z����g~��SUE��LY��'��|3rm)���7n)���=5]�S\�|���i�
)�w?��(-e�s�w����,�O�/x�W�8y�b%�IR��J7+������2f�k�X4�V�B32��&�"�����-,5�N��Z!�{~$�c�>-:SS���_��Ozp��z>��e�����X�x�Y(��
w'e1i������
1���x�B���7����$x�04�����R`�����+\wab�L��#�����������G����|g$���"�
��
���3eA9x����I��#��j4�oh�P���u1�����c[�����;)�q�����-�����BD8[�b����W����c]�X�nx�TN�L:��Up��(e�{�������x���_?�������������'Sv�'��M�uS�+��T�X��������xJ[o��9_S��r��6��J$����&�@�"A�������tF��)��V��70�S����)a�\>�Q����zM����&O��,�=S��$XU^9���0�B�z�kw�������n�}��W��5I�i��~�Zq�R��6�Y�Tkgl����`f�*�&u��_8H0��.+#d�j�����p������
j�/��E�pK{�U���
������AV.���{1Y��
��+��35�?Y�}4�q��U$���S�d���[Ef �5�����������K6\����5���A�������&�����w������4��7�_�F�P�C1�����I6J�>���/�������J$K�
�U"H�5)��������tG�K��s��{@�5����=��A$�����v�5�S������d����7���R?y4%'��C�q�a������m�\,A$�\���������Q H{&���g�d��L�������*a],�!�uG��?	V��EW�����~��� p��i������\���,�u&"���x���,)6(�����6������&M�%�s��U���D�Tx"k�	_�o�u�t�?�ek.�2��q�;g`��~,�:u��#�p0}����	�������d'��/��~c�jY �+�������n>w�U�X!#�	�����X&7Lz�0�,��C:Pf|�N����$$�^)����������>��	����p0���^��QR���rn�}S�Cr�`O��0�mC���u=^�A5e���yRV����C�Fb��������2`�������,��$������k3���[�:��-y��&�W��Aj[�)��RX�LoE6%o#`|��2
3�c�$�����'G�;N�i�����������8<���$VG9�P�
�C7FL�%��2f<�~���(Lq+�!������B�h�zi{��k������?��%1�l�(=���#H��H�d=7f��~��7�K�/�������&<I�5�w���@�� _&�)�P5���������?���1W��tQ���/+GU�x��|��������e1��1�q��;��@&L|�C)���C�)��������$82F��^';2���<E�U���.���������98��bn&6�b��-$�y��G�C���\�g��+�=��S'H�h�wu�J(��g�|Y�����o���>��#Wk��'���� J�R�!��Q������x��`p���(���j�-:8[)�A�v$�e��>e=5/����`1�M@g�b�m
Q��� ��d����~:>U��4�t��$���Aj6\�Q;2�P��G�*03�������$O0N{�Wn��/�xF����KJVKP��L�b���� 0�e3=�m�atL�I�ILv
���#P��S�e[����!���~Hj&�>���ZQj�v.�X�Z{~�'d�R�����u���>��"�[�!��[B�z6�/������:��ol��-:��������ir����?�W�S�4��e��-��Vs���n�E������:a+�x���2V��p6�8*�JW;��U5�E��yv�x����*�%��R�`W�[�R#v��h�����1��-N1���H�(_Kc��x���j�\�����.�ZP.I�a��W+`2_�D)��J=s��>�k��=h��%
���Z��2�����w:��j�*{��I��EJ�}����)��8����xQX�������������t6��M&SFu�S�s%�� �K�u������ s���
��(��d���aGI��~$�|	���K}�����v�O����	Q�;�Q%6_��c��g�=_�u�����N�-����^�k�Ml��f"*�c�C�GP"��
��'8�3Z����#�'ipV�h��h����G�"d������'[�5� �p��e�>�~����S_/�o����W�)	��D
&��H{�Db����LW���@b��V[�~�Q,�xHE�����Q�,��dlG��k�4����P#;��)*��NDS>v�I��L���������V���@���]s�r��:~�Y�e�.H�L��}�����\�����f��$gJ��P�!r)D����3�>}�2n!Sb�m`���N ��%J;YQo��#�c�Ml���3I�n�W�D�k0���r����?5u;�}Z8��m.=c���3�G���g�\,O@[��������qo�:7�VP�	��N��=�\��3x>�M���}�<_����}U`�}�z�'u��o�����NU6����[,	D&����l���dm�����+����af�BV�7�u��4B[�5� 0`p�p��������)��z>�/]��^�����{�3��{�P�Q	�����<���t"��bQ~�.��f��q�Kwyp U�c")�����v����'�w�+�7��u�B��;9;g�
���sNl���Z<����H���4{s<\����C�2�&���>����VqJf&��/�������"2��o`�"�*�KM*�C����7���v����+gm?�w�����W�I�G�H����l�������U��X/�y�����<)��)B��k^h�4Z����F�xh���\p{@x���R�T��������W�/t��������^I6'���2S"����������95�K_5�T��f���!��/��$T����<\x�R��p�F`����������_�g(���S������p�%���k�,n�TzGF������}�����c��;���n1����uh gq�#d�R���z'�	�l>�\�Q�����K���>qU�o�K��X.q��p�+1�oNi��lV�o5��P��X�c���	����	B���NN���y��m�I��<r�
C3`��������;S�����zr���s���S����u={��hN?�(��zT6#c��������<�o2;5~�b�g��^��R���p�e��R
�{����8!_�iIe@����FAF�9-��e�m~��'�+���''tPRU0��C��;��K};Vv���[��M�����X�S.A�I�p�Y.��r�or�9�����
���9�����v4�n!4Fg��
����j��G`���[�-w��~�G�,J�}�Z����F��w~=]��4��=��(*%�g�������Hn��lJ2h.�^�	�0�$U�����,?n'�}e6?���q�t��������UP0+
����5(��H8
+N4���8� �//��������{L�(�����Y��2�6����v��j�Q��s������V, ~i��:�l�A��_��S=�
V?����`��F!���"u���S1$�p��_��m���r�x������R�'�O�Z��V3<��g�E��u�����jm��l�o�m|l����2B�l������>v���<�sJ����"�`�
!�,hY"w��hmr������v�Iv�`����_5+zf)m��p����>]_���O����7���C�5��|9J�������Ku�Z>.���������<j�2K1-nt��rer�$>l"-�����O���B ��%����`I��9w����yG!���02�38���H��� 33�����u��N
Q���zo�Mu����\���rENjo9�����i�~�^U��~r^i��v������E)9s��,�;�;�>Z������Mp�L��t����������)�0�dd����G�S�����}{����K����*�'oA�u&����'p
��{����,x*m�0�������qE�����lr '*�h����V,DX��2p���pZhl�	5jg����S���J����@^����P	�����@����aS�xG�?:v��q��NC�C�b�`����k���k��G�l��0���`�M�3h�O��}����J��z�/�)Cjz�ve�Y=����z����F�P���$��Z��;���^n���t��2�Of�P��}u;���2?X����n�����l?�k�I��4�E�Nl�.�R��������sW��9��/ 3Yo��)C�L>G`]a���N������Q����(�OWb_�����E��e%�|�	����f;�����}��CB�'�/Y��,w�������q3�a��'8D
cP])~Md���k���?��K�<�����H�	��J��^�~����`Hy	���v�~��Ksi��~���c)�7G_�;�2���_2�ms���d��9�L�C:���bT3��Y��������q:���H\�)�%�Y� i�FI�@����(?����MQFD.Fq��>S?hN�j��y�qL!�g*����z<T�����Xm�$�����2jBm
gD��+�]�����������>H�}�$_�}�/�Ba.E���
�yZ�S3����(<P��N�k4n�V�x�	����k��__�$eeR.}/T�������x(�6_�����sU��b��6��-f\�h��
��������HDA��������6*3����!���*�gi����B
��[�a�|���wY�|-�5�OIl	*�e�H)���kKP�Mo�{X-���t!�_�Z�T��AV�un�.�8������KS@
��1r���$�'�l��9W(%�yJDH�
�n]�
�f��gaf��c��N��:��T=_����'l���/7�rd��|��M�g[�>����	��u�Ui�P�����M���`{2�f�j�1�J��o��u���H���i6j�9zY?�'����$���x��t3,�`"��?��\������b��7:��a?��'
�:���}�5���.M�����MR�I���e*��P�����}2���5\��A.	�DQ�*�g ��[f��|c���<�Z-X�J�	�&�bQ{�}�����u�����k{���w<�a��"���`X�;�b*�xX��k_�����m��,���i��AL�� n���X������.�'�>��/|@m"�<�y
>$��������F��W
�_?���F$f���d���:�;D�ATe�N���~RfUU������q�X�=���.��j=E�������2 U�&�.�`g���b���k������\����X��N2�G�D���R0\��I q����I��$de����-%�HJZz�����7Y��K�K�1��G�6;8�~`v)��M~��HX���?$���Lzz�^���,s����r�������[�u
�H
T���c���:KZ���1�VeC>w3�m��O�,j�������s����/�l���P;�
�j��h�(���2��#�����X�.x��p��
��Ph���-7�k�'�U���)3�x�5hl_�B�q0�3aA���N�k�$��4q�2(:3�K�i�98#���3y/
�L�5���<��!`���6NQ�P����v��ly�E�C��\d6�R!Wb���&���������7�c&De����b�s�8�r�)��V�����v�����'�~*����%(j����Urr���u}}9��������4����2��@��k�Z(���G��&����,��C���������b�$��v�^Z�:�S���Q\���y����U��=���M�����<I"����
������Y�p�������&[R�.5
�������Z�N>H�����e*�v�� �' y>~���7P7$��[��J��,�/;���HX]){�G���o;��;{��~�%��Y3*������H������hJs!�k�r�z��������m��������
d5�x{�]�8���6���S/'�o��e)���!x$P��:R[�K�6���lv?�/���DO����'*�{Z��@ @I�3�@HgUNXh���0�p�$gp�s������T����.��4�����H�H�	r<���>8X���t��B�-�2�#�Q��Y���BT�]�2P6�������EJ@�����x���X������BjIs������S5�c��.w,@2Wb������c�lT�ku����t}vT��Ws�hU"a7%���2 Wm���n��H�X?HRjO�Z&iEOR=������� ����u�%�R*������C��p1������_�7���:H?Fb��� �jga�c������Ru�Z;C���s����"����cN����)���
�\�����u�FTP�*�.
����&�v����G:9�,n1NV	R<�#	�
����{(Da^u}m��������FB+�I������he&���iYj���]PLF�jpw���9�����$p4w�k�!QS��*V�Q�F	�<����Hxw�}���~8���q��F��1�h��	\���Gs����a��~:��".iGp�/w5��b�M_/G{�G������;����Oa��
�d������"TU��W�Su�i��h�]~�A>���~�p)��Y)Q����e�8���~�F�r}|4�<��JLa8���5x���/S/`��U����|lN�p�;��jh@,��x8�L4L)w$��������sn	&mI��z�Cxx|�XP�@I�)��\��C�{�)!�~ 
��(:(�RJ� 
�s��;KGK*>��@��~�7dd(���Gs�����.�&9�+��yl��zpg�E0�����O m��7��/	CA�8x�=�����THx��$����~9�W��n4�����|�CW-�lS�C+AJ=�I3X�T����>���z��d���p�Z���W����_�3���{�=�(�Pk���N�\��E
Y<�4�@������������Xi��}��1����z�O��H��!�d+4cS�U{t�*#��B9�)�8����o�%�=G��3������.�V$8�Ho8��I�j��H�<:�3h��*�W��%
��WV�� S[i�S"������-�x�2���4�8)a����w�@hzN ;��*6n"��QEf �MI�;6n�r���e��"���.w�Bk$�|nr�K�%���Ur-�����ci��\���MG��
�zO�f3��.�
 �#�r���d6P#�
�P���B8|�"`��U���1��(���}�
e�E���F�l�y�5|>�4�0#���
��������?>Ij���# 9����A������8�e.�Z�����h�����o����)
%f�$z�����[h�c��4���s�T���0�h�G�.�[��?V����Kb���>}�EbJ�.��K���\Z��KH�����������������X�}��W�&<�7�{���]{������BS����h�/�P��n�}Qq���B���?��~��a^f��������FW�v�=.���EH6<���g{��K>n���KYs0��)Ko�G>�����T9-���?�q$fD
o��,��
��H���Ez���I�w8����`�d���C��f���9hF�P��)c�5��-�� �u���56De�j����f��fc�&W��z��-�����y�#�Cvh�`{�'�h����9� |l��{o���/����]	�p���I0vlg��a��N�\j�2J��H��(h��/������E�����������9��P�Gc�U�1�I���\��W+�u��3a�����R�e��x�����?]_�}��{�/ �Zi��M{��y�I�	G�g���-B �����<����^�������%m�A'V�� c8����
k�k��������$�'2���;�CB�����?��q�������>^������,��K�5�x����ho��C��wrG�7y+����%�1gt��8
�S���t��w����������c���\^`ZG9(��{D�����K��2�|g<oq�#z���+L�l���P2����e'�DGd��?�����~���a{�9�h��I�]G+�_���s�����������I�$AVR���L������c}���K�W��)�kZ
]�S���T�@���2�pC��zG��}<H����\����!�u��N�l��K�i�����1�0^���'�0nK�fE
� �f�@p�=������R^�����s;��
T�k�a������"�������[��;[�%]�>c�� ]x-��d�Xi6�R>o�����m���O2.�t�:��4�,������4����]�O���su^vuk�����~db!������(F�pZ^;f�Us�����WU{���H�%��p�g���������z�B!��w,��9�����{]�o~�-��A��v9A��X�.�=E�,��IO�����K{�Ksj�c$�����	�>�7��9�n(Gs:(�|� ���)���}S������F���JH��r��.&��H$K�P�S4�n����������d��:�������qa4���TH��m�d>�\/�h�VOHF�m�D�\4�Q��?��,
�"���TV�m+�����=y����R
�-������x�W�'g�vu�>O7%RL�0��`7(9���������p�/��m�]~
fe��me/y�-��-���x�y2�_��0��k��������w�9_o2��.�(=&R���F$�=���uv�����@����#�u-��c���d2����S9�:4��~� ZKp����O�~]"�>�fJ�b�TU���0�C�c�I�?4��>�)�il
��<�
</M�?�� ��Cw*X�����k,�Q!�P�k���^{�|����u��h
l4G�7�-���d��:��#���=����|���
d�.��d"H}[9KbJ��#���Zf��p����
f��a�3y��eFE-�
d�Zq!?��p�t}�
dX.l���d@��i�:_.���U���Q�(����0��T]��u��R]N���������~�������h�c��kO��hp���|�rWz�|���g�1����09)	"��@�p�����g*n���S�88�'�������������I����D�*�������������ud��A�r���I�����%+�i?|�
�����+l�i���9��)so�J��)�������|e "v���n�K�s)��%Q�z�0�������4DAe<a+�V�U���
+�!.�}Q��
�-�KB?��`Q���v�9��@�L0)t��GQt�L�g	�%�H,�l9��d};��G��un��8&�[��F�
��7f�/��|���R���SSu��i��e����(��`c��c#,'�K�����:�R�U �"�����R�����W��`���@��M�k�6��������s�>S;>��2M,%�!�(��7��s�y�4�
C��'��~O��2�~�}���t�	���D��G9�@�t��<���U���6z�����xy�eiV������C�����'�}��7�������^�������s���2?%-���w�	�2���c��`toG�G�?]��(�lo��YfUEKq!���D������S�����~�aulk����i�w�=L�h�xA�,/m<������/-Xs����T��aM"tJM��=^�gH'X��d#��At�#�G�x�X�I��fbq:���k��Uui��3I��c�g�w����~,��8y�_����|��:w�|�Fj����9*�����L"�o��4�YG����E��EYa9sbGi."���C��}9_�l�l���K�����0�����c�d��8i����_����=\���z�N���T��������'�2�(r�U��~�:�B�j-
�#�8E��1��'	�lVbGG;��6�6��]�_�~����v�iC��_������
2-pT�r�r�DO�x�xWz�h����$�z�K�xh��Q{����	`M}��<[v�8���@��w������K��F�0��}<������F��e;z�D&����~���#_��P+M�j-L!T�Kg^U7��/�=���I����$������Q�(AKI9��Pz�y�F�k��N8���*����9����H�����\��j��%��T�O&�A��L6,�k�O�C���I1���8�k\�!�����Y�a��=������U���\���������.%�����YC�8����m�p����=�LdP���]A���?�d��t:��R��,��0'�Z�d6�F	���6���A�,�
�:If{�q����z���{o�"���&	nS���T��;��D�?J��k����@ZL�Y.E@A�<�,C�i�4��p�F���'6�'7����/]��0IT-c<y�/b|W(�8�4�(me�e.��C����v�/�h�@'��X���c�����EK�&ZaM��Z
���)��i�B��U*U�2[�)�V���Q��A��[������4~+�B�L����)	��.��lq��'�w��$C�.�K��H�r�\0`�����'��?F�>Ye!8����Z�����T.z������f��w�<dv��ww'�R�����`q������'ga;fV"��F�V�+��X��~�?=U����4��}p`����f������t�w�+F�������)����o�T�u�(�7�m���G�L�����f0���R������U��a���������T.:!0�WO��5_@
�E�un&!������I��?��E�yi��o���1��;�������i����^_1�x���R!;JiQ��o������>$����
�2-6��c�
)����LP��U���&i���������=iH�6��,�`��x��o��|��N�������W2
\���2i
��|��_�G�<P+��9���x�%����"�Z�\s`Lc?H�:{"���LG�d�`�����d��^1�#f9?���������-�,��u��/��������:�;	��vQ��2���:N��UBh9e�m�(�[_�$q�U06��V�a��O��z��dt���]�Rz�R�
L$K������j.�|��R���V�r��B�"%r���w�6�YD�
�R�)E`������vB����;�5No�/��|���������3����J��w|��#^�������v�5���:����O�[�{F�������u}�����-jV"���$�I���c�	��b����������l����V��N�O����U�r��
�v�R&��&(#��[������?��M ��%D�_����KS�({�A�\�H���x�a��f�)V�VR��|�&��&H�;������o��������R�3�f�V�����m;��W�-W��$-2	��~���;�c��V����E��z8b��1������S����A��������=X,��4l.�o~�Y�WH�x)�4f���!2��/M?�EAf����^�9�A���H
P��FN�O����UU��M��\����]�a������?M`U�L.��Z���s}K��N�;�X���#�d.��G�n�x��5a�c�WI�kvJ�u�H�4�:���*�I����H�xxh�"�)��B����|�o����k���%m{�$����m��nk9R;*��)��<,��1��%{�h	��8�*w&�[r(�:������	g�������m�m��-p)�G?�/����XJ�U���cr�a�x���Sb��c��*FI@���x_�32�r�����t�	����9���q

������R��������'#�UQ���)���-�8����b�'	&����Y���/K��������hw��|o��6&���oie���k���p����g����;���Z��?��#�^!'���	�����i*F�?��4J�d�(	�O����rQ�Fu�������_A"_Gbg�R`-��K����6O�	�����\/]�NGM�M�N�v�%�*�@���'�1���E��?j~<Zp$���>�c�[���@x{v�h����,���/u!rK�c)���|e����K�����f��/'��,��f��9�du������������`K���Gx<\���([�p�fb�d�o�Br����>��������F@�d��Y
�Y����<�(��~8�M��{xmi}e�S�@G����)r��p��������B�m����LP�Q�2�G���qv��D��I����]8�o�ljiN��'����&��s%���=����qv�[��{[������j�8�	�v��&�VEz��������|^�N]�|���b�	f�x1������������9�`g�?B�b����QZ�(��NQ��Y~�\z�Aq+@�BU%�k�Rp��9������)��L2P���6�I��.���y����d2����`t&�e����QO�t��>�t[�Qd+e��������p�)I��K�R|�����c4���N;�_�6/���:��C*K5T�������$���~n��y�!������X�F�P���Tb�"0�8�Ur���F����(�i�����`�2b��-hjQ�����o�����\�j�yo�/�����1�u����-	_n7>�ln�������fBvFl�/i��`k�_fT�$�t<��p�l(���C6�S�*$K��K����N�@;��CZ�|;�������_�j=
	���-I�$���T�iE��DD!�d���$���#�������{�mU�C�n�m��7j�(B�!n.}zU}��L�L��Q$�6��o���4���^�z������c�e�����oR��q���`����S�`>K�R�^H��A�O!Rz!�#�!M	��;A�	���wi�o�����i���c�H"�_�v%_n6�lHz���l�`�F�H�<2�B"�_�E�^G����u"���������V���Dc;�Cv�Soa'�A�7����o�
f..'���R�S�������	6�����G*��M�Yy���F�oF��.oiU��K�#�=c�z5���������vd�{c���_S�'��D��ES�u�!���d�������_����p�P���a.4a��`C����G�K��`9Y)n��I����E9ne�LV��1��g�H�-|��m�����_�@KG�~���SS����������3����d�d2,�������s����@6�s�e�N�	"�Q��.�8�p��}�N�ps:�������UB�{(e��u�gPr��4�\H�T��6h��������!�?'���"E�2Q��K�(z�
x������`i���2�'�0��\4�Q=$�j4����4�~�yrB��S��U/�N4c'������F��z�����7MB�xC����1Kx��l9�	�H�l/G�8
R:r���6���;�5��A�dtH������iKRF��!<������
�m���H�������\�% t�|9����6K���#��t��i�Kg��G���C+�����3E���"�K3��2�[P��
�2i/�+2~���%���mo�
�sb�l��������un�	o��k��#��|�����l��P{���T~�����G�;B��i�������b������d�i)�d\T�	���\�N���vJ��c��OUm}yi
Y'�"����BQ�_�����`���������h�9������2��a�
{��VC���2�+bv��2�q���t��d��aY�
��)=�����T�������c<|�������X��@r�d�������R`�yDM����4��}������S}�E�tA�5�xt�K�OU�AO�������slKl���R.���6GH�>��4o�`�+&�E<�CVEQ�4Vi��t����h���r��%Vs	��8��b�3p	��4L������\Y�P��Ef�L.���6r�q��R���4������}�
��i����������%G
inher.svg.gzapplication/x-gzip; name=inher.svg.gzDownload
�7�Vinher.svg���z���%�[z�l��i�e!�@X�{�%��sd�S������I�y�,������`���&��-�df����Z�����f����r����,��E���u����//v���������x������E��f����O��fq��������~���_/����dI^�������9��������=[��������m��_�_��M���Y���\?yp�bq_��[�w�M���m���P�b��m�����rW6�������}Y�������x/�-�Jx�]�����1�@��_?l�����D)�Z����>�|��&��|"���.^�M�����_.��?��O�)����r�����
����*nn^��VY\���&o��1��
,�2|���k|���zy��W�R<��������?�{]�����~���"��b���5�,w�}S�����������(�����@����?�M�������f_}y�_
��"�������X��x�U��Ze^�����[~�����y����K�)�������>�V���o�`����U����Wx�/��>�����R|�X�z_���/wMQ}��\B6^cU��fx�b�������Oo��������'��j�.�������;^�^5�%m�fu[���&���������X�+n���{��b�_�������fS�����w����%���eU7on���|o��w�/�x����mM��{U��|������c~�Xlp
�-���������-�_���O?y�z����s����,w���Q���~]�I>�����{�$|
|�M��3���|�d�%��������]|�}��������.����+�6p�/|�?,����WM���[}���}��}�[����z��{���e������+������%>>�5���������/�����?�[���W<.��]�_����wR,U���������R��tv���C�;x���j���D=��5�z���sy�������/����������?�1��-�o�wV�U��������E�������4�_��}��9>�����?/�v�������|R��c�M,���=�i�~��������������_��|���__�Y���g+`_�7Y��/.�g����hp��L���3}�g7������9�r������,N?�}B|_��#�+������Oa{Vt����w�1_������m�?nx�U�����#<���M_<�K��7X��hM�l���������iy��_Y��?
��?�����A��?�B�7�v�����������_���j�Osu|���<���O�:}��X5��i;X������v�������j�i'x�}5����c���x�_	�A�����O�q��_��W���q���.�V�����t/��{�T����f����w���^��_���~�
�d��c�~��n��Y�_�+p��;~��m��������[{���K|����p�N�������]��,~���^b|v	oy�'�v���W��w7���2��#��������}��/�^�9�b��[8d>�|p��t���(|����mpa}�e}����-`���r�fpG�4��F����������?fU���=}��NQ�@��
VC����m��qq��>9�O-0�qT�b���=�q��xrY��X�?��������C��0�w��3������Vo�ON������6{�U���?�Z���uX$n?����
�tw�,^����X����Z�}�X��,�%#��sC�N���3���
�e�1VYk4\e��>'�YH/-���/����������~
)�R�qy������%�E_��������*�N��,:�����v�
����z7��;^��������a[�l�~��2�R�������_����lO8~^���[����On�_��_���}u���������}������������%^^��6kh��
��x�1�>�p���g���E�*����%�?]���l�9��a�k���f>!�i?w���(Sl�	�^K��0��t�K�R^
���������������U�����������+�k����1���lhm�C[�z��O�c�[����	Y~o�����>����[��D��n�=0�+�k�����3�h������|
�W�oB�.0�d|a����6�~������]O���F����[��q����o�Yv{o��^	,��M����������Nw�]q�d(��7&����W����+-V����"�h�� ��>����{�6�G\��ga\c��&��c��������|>���|��
]����6��]k�����V��aP�1�jl����}������[QD��z`K��}�^�U�`�==��������.�������w���F��!��l�������`�����_/���x8`	����\@��/�������?�b<{u�'�mB��5��n�w��?��W��tP�{��J�/���K~z��
�/}�����OU���<|�K�z��7�>�SI_�'����������
�/oo��>�����������M�x������N]�i����u�G��}_�]������$���U��RRWf���}�D�G_�}�x]��ln���[l}-���}��}G��o;G���b��?o���)x��c]����~C���B��p��j|�O��S��{�(�zf��q��4��6R\.~�m�!���^��[|A����+�+x2c��e/��D�����l�{,������,����~5�:n�[��7��63w�,B�G����3E�g���s�����n�.)��8���h�o�����*������zx^��\���h�O�r�W�o�+c���*X�%��
��?�>��68A�����w�#�=N�����C����g�[<���l��U�Y�'�8�
c��������x���M	����������1������d����K��G<�����������N�8H���,o������=�d�xy��]��a�������Z��}��3�{�vW�!���-q9C��[�����4���Dr����v����#�<Z\�i�����^��0���
����+��_��s�g���!o�?������8��A^ysk	�~A{<S�����l�_��f���x�^�iw��;8v��~�6��Z�r�#��bS�g�w��MU[h�����-����4X��|s��7Z���h�vo�����_3�r����-��?�������1�����0�o��g�b3��]���;�������}�������������q����QH/~�����q�E������e���~���F�o���f�m�P�/��O�����/i;��1�-.���s?lpG�
��9c������<Ru;����[����?��-��M�c�� ��Q�:���]�?�����Xm.��P���a��
���m����\�7�^B���W�>������K%�������������.7WC��N-������h|�o^�����$�E��/I�/������������]��rb���W�����K�5�����7�:��w����P���M��5�7����8���tl���~x5x�g9��q����^��Q��00����Y��K�<3D[��`5z��R�t����"`e�m���K���i<W��O�����K�
��Z��I��c����D<mh9r��pC|��z��g�������d��d�?�����V|}��eJ����M������H-vd�Z���Q1�#��#�!iD�����D�Wb����l�2�����,�����XLI#����iM��5ID�y$��)�B|)�o���_�u��<�a��l[l(a�q�`,�)�H��'>&�I����9��x�����Y��R/.�hg�)��KubJ
/~��,��8�1���,���~��b�A��oS7��������Y�Ii��{�x�iwS<=�<�Q�cN
�i8l�s7g�a�x�=N�a���k�����A���L����'��\�)����$�����d�v�� �n�o!����PiD�FCl��Yb�=Tu����p�����#�����0��$�s��VE���=�<�m5��.I�jQN-G�D�*�@�;K�.|��
�l�o�.8���-b�Yc�)�+X�`M����\
Jl�/��O��lPK;�z���9�����m����!(;w��� �|1�m��S�����E�vMzY4��&�������{���j����{����MY���r:g���)���+R|II(��7��
��?�|��t���h:���r4$W�rxD�)�6��U�:wC��f�Sd��]�=���T�]j���M����'g��p~�''���.v�m}�d���x8��a�N\��J�����,e����?0���z]�+�u,u.�ay�G^�\�H������MY7���@D$g�s��%;q�3�9�H����lFB����A%�&�&��.C0aJ����e�](9�l�)�\�>G��!�e]�KX��l����*�#b �<��X'i
Ug��K,e����u��62�z�Tt���������D��/�4]RS�?����d{��lu�W��i!�����{k����Ax���a:!�_gM��r��3���x����l��Irr����	���Gp����"y��e�:Ve�����h��qh��6� SY��\�&p���L%B���r�0
������K�y�q��8������<��3��:3
�*�,_��
p����Q)xr�C���Z�x^�]���K��]��l2�f2�@�7��c����0J�rCqA������e�mqq�~�����.!���a)M�h\�����Hy���:��'(l���w�r	��!b�,vH�����+v��Q)i�U��q��I(�
�l�]���::yld�%".�����3O�\)55"��}e���M�����������`
�4��5f�K��_�Nx_l;��zo���&o+��I9#��{�/C.����~i0���M]w�N��J���l�-��i6DhLW��u���r9)�OQ�4~_l��c��
�/�^T��6�����2�D��V�`I������M�;�a��+(bI�T�"egoY��}���X�� b���������x&%O�Hxa� �2�P/���*t����c�78p�����V����A6pDSM�-I�2��}���ge>�2�<���j��y�^;$���C��*�PG	�T��:���B%�.�\�>L�G1;Q��<�nj���<��0SF��{A���p2�m~h�Y��J�|#�����a4���g��H����vp�t��]Ywp���Ff��8��L���CC����Hd\���H|;=I��dr�������88���d�w�%��n�\�q�'�`b�k��	���%8��eL	.�����!���g��* j!��NaUJ��.|h0���	>�U	!{W�pG	C����o{S8�E(�hMf�F�psH��9�!������P�Hl�u�C�K>���e����Q>�MN��H���u� `��T�v�}�#���,�C��f��q*�a31���BgL�8��'�����D�"�(�+�Q��i7��$$kaEy�@���E���T�����CbI,�L�!>�ID�2�Xr�~�c���]��.��D0o<0'�D���!�������p�	��-g�{br&�H"�24���M�KkMUj��>���j�9���)���:��,� �S�DLnx�C\E���� �OI(M��	���&1,Q�n�\�*��s����R�P�{�:b�*{�,u�����+��#��%\H� 7u�K���_����L����uE��<�o��.H��K�Af�]���l�����f���%��e��\���x�P�����P�R�-7����[\":r`Gx[�����6L}�t�C d�����1_&��$?����G0�}������y]��.�����A�W�$LD�����1�|)g�01!�7��?���xp���4��3/~,�[�4�zV�`�C��H������R�1�zxI""R��=V������*k�mQe���:'g�np�x&]9�C�
H�.�`���g�u�)�r8���<9��!*�^�,]�I(���J�3J�i��A�����!���jL��n���:Yf�K�b�T�H�5���EZ*�S;��b���x-F��J�mQ������~WT��68��&o�`��������'�q���G���D"�N�*����M��V���]-&[u���k�!Mz��\�S��;\)5h����}zw�uc<�L����X����+B-�����+n�8~�1��R�vy5�*>���� �L����I�B,��N���VRKF4���z�����Hj���4�G�����|�7�C�����pMK�Y���~�����a9rr�4��>�j�>�%�� ���7/�����@*CA!<�S:��B�tl�����)yR�d)�a'�<o���eU|���51y�����
������L��Iqm*H��b�X,	� ��y��R�,v?�#�H������)B� G�h~r�c=�N�#AG,�x~W4�uV=8Rrpj�F�kc
i2e�]���W8�fo�~����#�����l�HpcE,����D�������o�{�u���8��R�%����X�K�����B������������)��@��O#:�(��d����Q� Q�|M���I$w�5�����:b�X�QM���|������X��'�!%�2�KLx����ZD���P����B��{�B�7��������}%y��l���}�0G$Ea?fG% ����������z_�9"b��|w�uB��E��g� #%;4��[���uH�����8i>�D����C)�u����?�#Q=���8����F�	
�
��W������ZZR�c�F��#�%��$F�I;pq�������|8mA����b'���%[�F������B���BQ��C�����O"\�Q�M��f���A���JV�*�!��{8;�b�%��_�v�/�5>�G�w�������N�m�2g:1mYO4!�:��&M��:��-�F��aq��A�N���YDHO�7�~Z�M5"����G
Jd�B+�~I���F_�h��*�n<�N_�M�h�4�:��{�F��6{7���\T�V5��C�����^4~B
��lw���6o��O�+���~3��<���B���r(P �d�'Kf����2M��v��Bx�������r����<���_�f�xN���i��%�������ht�{���O���84{��5�P��&v(��|E��d'B���8,��G�)q���f�$���d����w|�9��G'vu]��6����8d���E�����=�N=y��R�f��O���z�Z��d��j=���� �$�}	��9��Rjh�C)5 c-dZ< :��P��lr��5u(�Z$�!�B����u*P,�0	�nl��'��L'�\D�z�T0����9�d�M��~:UYh}����1�!!��3�����c�k:�A��_y]��.� ��������	f�O�`��
�+�k���e�D��z����u�����J����}� ���9	��#����)
�0��
8J�4��z�o����zsD�)7.as�z���z��j�@�h���?���*�����2�d<�����)��V��3��,��s��$Y�N���]�����;
Xg�V'�,P/�$e��@�n'gFII1j^X�re�D���B��3�gJa5�5��!����"�#��	�����2�U��d(�}�� �u�;�{��5����dFi���"�����&�K1�Ih$eVq(E��s�Wa,����g �$"��$,c8�������9��:���Z#��$�?����78�$	������M����'*����p 2����n'��PyK�.>Q,'l>���i�{��`���m���R}��G9,��|_��MuD���z��CCJ���T^�z��8�/I�B�-D�(�v�h�����������K��JM��R*�x�*S*!)��b���j���J��4����8�1�W�GA���pb�g�)��=+����Jm0Y�8�'�R>R��=��tU(�]A$&����H��������W4��jS�U�;Q�?�����"MA��'f���w�!o)6�8.�
�a]�����8I�������.U����H��E�MO}]�Pbt��!��!��x�b��")<u6D�&c<�q�,;��e���T��Y}w�6��GQ������v�P6{�7X����KIg�k����~�!���e�<�Jp&��}�� �t�jLb3�7��gE ��d�Kb0a0���RMF+�����:b�p=���;�T��:��������#��G��y��;j�t�)_�.
���^G��8sf�������v�a0%��q�# �$�� �������������;
�C������r�B�)�st���~��`�5:	���"������:�������^�M�_7�����M�������7��� ��wZ��� (��`UT�������i!g�P�R�L�c���Z#��������|��[�����C�{�T�`���}2n�[��[���g�bP�/�q��8�p���U����E��{�e����"��+1Y&n�j�� rJC!�X�X�lr���8�8�|YN)���^����|��-
ZJ]n�f����m��>�������l��������i���	���*�4��~a����Q�[�>�:��C���1����w~�����yn�">��Jq���9������sfj�6�i~�pd'��6������zr<B���~d�113�-o��)�������A�uFRK.���������:�.���7E�uB<�����N���3����ITp#6y���3\�C/}0����5u&��]�d����;�}���l�M���L���NR&�������K1IgSI�������,"�h�<���l�q(�K_4�g��g��Z�0�YT�4$�h_�_���]����oTY��f%��)?�����E����d����S6��)s��qJ=�q~*�kHm��tVu�M�u�n&C�||�x{hk�����|�=�&�qo����"~w���O����&G�����+��Acx������T�L�#c�_L�,�I0����]�8��!f�{�ah#\�1��o����+��Uu-��1k�.g'K�UQv�)�Tx�Y����2�cT?Gm�������������uSO�X�,W�I%>�=�m����`�c
e+F�:��;*[`�&-��~z����90���Nz2���;M��tl&�hF�B/P�6|�&:�U]dp�G�g|��#��o���I:q��@����[;
����;��{�fw�{��$��k�Ot�,�,k���FO���y��|�P1�&���m�m�fu��o/Wnj^$��U�o�A\E��������sS���R�;�������g�L���J��~����yO����2�9x�u��n���2x:S���r��4�oO��s����Iz�������FW�]�9����!�n���2��n�c���:�_��0��-<�XG)�
ff��w����v�<F�A���r$/�rn����ng{����$��RI���}��j�f-��Se#�}�y��wa&�P��r���U�,�
�2T���WI��"��h]�D]��.���D%6��I���zBI�M�2��rh�>=��Ldvh�0��!��L:m�P�'tC�!X=8�O1f�D�� W������s`~����b 2��48���!�$���<as|��(O	�$��|���<�GAS���n���D�FKn���Dv����=��)2I�K��I�=��Q��P��v�����'����2��L���$��o���d�D(�������C;��N���?�R�u����KDl.�����dj�@�h$����)=,�uQ#RU���Pj��#�a�����zzT�o�{U�����l���(���*�?5����2����yF��������t�Z��s<���R��f�����m�J{��i�R�xg�qj4�R�a�+9JT��<���x8��u��O�t�7��0F.m�������Z�	$��]�1�E~����r�[%�!M!,
k��zi������1i�w�b���+������~���c'�Ipa7d��L����.�'�Rt���g�1��s����7�i���_o
M����@��������:	s�QLo�^�&������(�+	wi0|�zl��_7e�8���%�Z���2�p�H3�@I�����TrT]�fI�Z����|����k�����]s���if/fEsL��������4{��������$����h�P�&j���m'�*���6"=���x`��h�*��,PKCOS��,!��s�TK��q(���&���u����y)�$������neP��o�F���:��5�uz�v�Qux���<��3�46(���
g��r;�-.u���=�}$�;U� ��K��|�!��<3��V����=������r�/�+���B��q��|�hk�����Y2�2&&���HC=��5M���=�}2�0H����9��ZJ����Sd(E��>�w����;"Z�XF&9p�Oz��P�tA�Y����I/}��C������h���>���#����{R�����^C����j�f/x~W�P+��j|��y�o�0,�b6ZW\Yt��%Q,M0��H	�n�A�w�Yc�����NY�g��������?*?Lk��x��q�kS�d�&��u��1�,�����[(F:�ZD�2[1�~+"I#���H�L?�G����-"]w{�!��b@�+�v�X(-������R����L��	����8\�~w��
"�����ff����"�;���f�$�����R���k�Ta������9b�X��x��f��b�8�9JnB�Qm���OBG����h�vv���}�m����$����o,���� 0�(�8�iO���vKe$�!�b�wA����V(�P�!�Ww���Dv������.�_2��:�`q�����w��M	�gU��������bL=�v(�a�[�����
$��u����G|s�	5�����D�����.��v$�5	��<��.���!�q��� �y�#�� &����}�l|bZ���s�~O��u<�d������������bK6>��@��-������O����6vj�9H��1��<T����N�P:<G$� b��il@/#���!	O}��<W���+Ys��5����&4M���u2�N�@�/�H(��
`w_mk
�#r�%i�����<M���rTyV��`n���&����'8&�
%fF��UW|�a���gVe*=���B�o��������1�P��*�<�[9�'�I	OF=�0q��,�C��M�]p[b��l���d�c�Bq���G�da����<FI���5��'J��c��p�`��H����2��Db��m���q$|��bIY(������Q����L�r=�����tT�������EC����c�����`��������n\9?����n�{cHa���S�20���)+���_�G�@j0���n����'���RS��M7�!n��u���mPE�Ng�EO�<z�T|�|m����R_�����������<�u��.����c��W�W�L��P��������b��Zb�Z0j��4���'�/
��P����(��uj�Mmi�78i?6BRb�A�6�[��
�$��#&�s���|����2)5���.�h��r\��MbBQC8?,B���������G�U\J	�������D�p������Bg%���!�S	��S��HP��m�y����ac]���k����ta��>��.�h/��\Kij���[��;�h����L�����R}��);P�P��9t;�v���#g��k9����o��M�c~�)��5�����JT�I<�h�G*���(#�P�h�+�?V�{����osj,:�k`�������'�;v�3������!��$�A�7 �p;L�~���}'��X�`D����|�]���H�$-&�bcB�ZB=y�������m����a����S����������:DR�!F��X�`lY?���D�l��r�:
�%��;V4�DU�D�5dJ�<������q�>9,�Sx
����p@����r��,?�����i�:����#c\z�6��*1����N���H���x*��+&/�����1�s^yv������|"�
����`�]qC�:/�$�7O"���o����]����"1��	6��(�:��[�e,5~}(�<�la���G�������E5f��P��
m�����x��o�rI�BA�jQ����t�r�9c	��Az�G�v��%(w
/~r�I{4s��RO9$'#���������7pKp%���{Q����7����6��u&�0�)��L��� �Z�2%$����Ed�*v���>Ug�0l����M$1��(����:���nME]g����2��$]������k�=�:�;����.'����w���8%�,���l��A2��I����g�i589������q�(+A�M��c..fs���
�t���3��z���4�VEO��4�����E�F8������J��c[ ������rPo���
����-����Vr1�Vo��8SVD*��w7cc�3���g;4p�2R����:�Q�!lE$����M����A��7�����=�o�9��F�5�C�=�@�a{��)
V���k30��u_��2Y�"����������T��
^?��uPt'�"+�������Q?�LHG;�o<���Q��*{�~5�N�[g$�:d����Pr����U��f�9��uv���;U^��b��d#�^IW�Yf�����B/�4Y�/�^Vy����
}Q[Mw�@������+��+��	��4�N7���A�O����-���}��0-����A�Q����Ri���D�M�� :"�G��WH�� �d�#��ov���)�v�����v)
������r�%�tC)��&��~�P���T4��~]8��pF%�����)$���K�:B��p=���AcrU�HH�3T�{����F�5�!�%W���0�`���s{sH��`��q{�>��w�W���)a�
m����$�0'�I�0v#!������
C��fY��������sL������T�����T%��9$�����t,�EK�d��f�����M4�_����K�eS&�cU��U���Ww&r��@9o9�L�L���,LF>�������kt��mNG�baO�(_��xHNQ#FKx��A��	���\�_5��������{������!c�9�<M���u�@n�$U��}qwI	"q��x1�������1_0b�`��
�I�~����g�#
V-oQ�:or�������pcr�Y� "��!�/���^1��{j3iGR�l��i���C�o��^!3���(�l��X.yc�
�Z9O�����s����
%���D}��m���I��7��|�z�1���9�$�Ea�1��)���_�y;���������:Nc��u^��.����+��}����A�o�11�Ag<5}T&�:2!+G�
?.���P�D�B�D-W�������)��J�'c���u=	6���$V��E]Y�J| ���)az���}G�a5&���H��^����)����cH}��="
��cD{�{����mw����f)�{<��qF��I����yN�+�z(
����A��|�w-���f�5cR��'�h�YS�+��v[���x�j�e(#^T����%�����,�>��u�.U�������&}9������K"d�����a�$������m����_
z��Ao�N��>s8��L:p��r?.{��/�J��G����U�=>n��L\0b�������R�xE�6���-�n������j�
9��,O1�/�*2�m�)Z��u�� �{���zU��Z�rw|��_z0��C}n�Q����H������Ya�5����+q#�@��&������_>�5��~�8Sy��+�����BM�]/.y2�lKaa&�F�!�2���H��d<t��V�
B��.xF`;�fO���1{�8�F��=IO���}D�;����eU|���uK1�hj[����&�i���b��#�!-��R�l�s}�����z�-1gd4v��n��Q\I�
�1�6���i�?��#��
e��@p�"���cg\*�>
g��DB<_LH�M�x��q�"l����N��[*������k�'��+���l�#��|�����7����G��0�k���$���1�Q)=/�@�����3�#@�V�SeTG�|�B3D��x������������D[b"�������q�,��Tk`��\���$	Y���mQ�^���+m��a�MZ��2���9o���r����2
���G)�������~�54�x('�udR���$�:��}��(��@E��&�|OW>�tG�}�5k:2?H���n0��f�����������.*}�F�&����%(4 h(��,��wY�m��U���k}����0���4N;�>��iQq<K��P9����_
 �>�	�����+s�d������.[Tp����rw����;���Y�g��Z����K|���3��x(������-��7(,7�hbk1N,�hZX����6b���~����N�AS>W���L���3��3rJ�C���u�������E�K�����M�D���qd���!����`ZR�����"���>Go�<��"oJ0b��M�W-s��c��X&�&�\���d�T���@�����}�z���X+�;pm`b'=;��ED�JS��������c�Yg8)�5�Z�u�/����������><2��.���|W��;���2���b1N
!�`.�������4L�L�+I"�B�+�23L��o��!�K���&�y�QI�����dx��N+���RQ�x��r
��fdW���dJw^��f$<T��RR��cd�����*F�]��_�	���J��,
��`��(���1S;��u��h�U(�cZI��?K�!3�n���m�k��EU�V���E��(��c��d��e:U���x��q���������]Q�����{C����6J#b����$��������A �0#D�lM^#b>6��J�O�2O!��W������m���F�����W���!�T[�����I2�"[�<P[x��D��$�R����R��"��M����4�"�y�l���>���Oyb������	i�0��
	_{R=��Y�����`���6�8�:�5el�!�:C�;o.H�S�A6��.���U�a�n^��������^�����>B�?��6����i���LP����Sa+���}�����xq+��F3m���SP"")����[���~����Q#;���H�u�<�+9a�.�!��M��MQg��o?����^Rf�"��r���~��8}h��?�v��i{j�����U�$������gtQ������$
�3�B����<����.���Y8G��J��PJ��3tW��MN}�:�AX��-��.�q?�����g����p���!��Mv]���/��c[��)g���B���!�R��9�q p�~[n�w:d�p�
�&[r+�G,-$g|z��b������
�K[I������Q��[��d��~���
<�yS4���[M��]x������X�$����wP�gT���H%�W�f��4=��Q,��g�!�4���� �t,�����w;-�&�������^�aH6��i���5�,�� ����~���BQL��O'V�����Km��(^�8��J���G>!�j6%��Lv�O�������K,1x���������lH�@��$�{����������f�����'�uF���7��$��U(k�\o��*���,��8����J=��9����K	��l���������t�7��(����� BC����;\���G�����������\g��B	��b�f�C��1	��Na�z�i�|x����H
��x8T�����"��|s������R�����I��T������q0P���z��������~F�����<}��:��6n�`q��zt:9)�=��!����pr
?.9
�W�]H����F�G����%$����#i(qeO���f;����t�������9�����Ot��������"�V�N::��:�	E���<+1��J�Wjt�q������:Sy��������ay
������cL�:^�� �����#|E�����t��*���C������	"N��r��/��]?��P�����|�f� �]�s�9�@y2|0y��Cp��y+����c�mdFj��HB-L�t�]�=���[�����]�(�>�.�W�����8���qSx
WqN�!������
X�����BK���W�(�CC	��-��t�3V��9��'�2J��+�+0af
�6�����%8�bQI�5��B�0�'�#*3��y:�E��|\rI��o2���q�Rc�!2-��5������K�x/ps����PE����3���t���=���Q������o��������u��el �go�JXS

�Y�X�V����u������MYl$����w�>�X;��)i�{�.hj��n�g����
��<�� �h��Kq!��)����T������O#�cbj����r��>��� �����@���c��U�DJ�#[k��h�HUD���1�wJ"+��0�ax���[o������l)����Lu��F�3?��U������������><�������$T�8���_��x|������d�T)h$��T>W�	O���-m������ ������}�����
%����3sj�%=��x�g�?�-p�=q�=P5������v��/��j0	'��?`�������5��_�1����Z�o,w���>$�}��4/a�����������NK++6�ca���
�4-62�AP�
LM�@	c�ZO����R�������2���-\%��0�/���g(��:��)�|K5���P��F���=W�� F	�$�@h���"�Hb�S ��0�|0R�m���n���tN�3��JKp�����S�#�d��k�d��%��V`i2� n���1���?��&�|]!���5��B��Z1mF�fD�D���)l�TD~�[�$����)���~]����=��]�
�?�I'@[l�)'&��{��FC��on������,�|S��o��kxG���q�L���1>��@BL�9=���0������L4]`J����$h]�l^�H)�L�P�� T��&��-�5�{������63��	��,6�����:��t�"L�?�1
2�D�Y����Fj)@u��w�������[�N�4����NT��S�s��A���I1e8g�*�*�9j�2���l����I�?:c��YT;7$��+t�kM��
��$����Y�]eU������@8�X����F,Q�����g �`�������lnu;2{�o������]��yJ,:Sw�����c��O�?X�EJ��M�z��1�rF:��f�SK�B�_��yJ�H��Z��Ig��D�'�t�Fo��39O>^�6�?SF������S< ��ju����S�a�������4���I�O����!������i�j}�f���o�|����+7�/�x�/9�i�I����HB���a������y�����#_>�M��H �����P=�d`�r���ON6u�� �m�>�i������)�������r��}O��{��4
��/j$90����ar�8�����(��*��	�m�
�9�
��R��A���*b����
��b#X��������A\�[�t*P3���j�?�m�4��qW�7����*�k��3�^��I�
8�'�h����PH��e��M�����(��g�������/U8����X{�`J������Q��
~�r�W�k��0R`vC��uwO���@�.3������z�����EaB�����1��("����Yl@��}p��9���X�f�ns#I1
/����(�x~oo@�j���mM�!�j��0*B"kX�4�-�C�m���(�rO��*���:0�S+�d�AZ"!#�uT�Qvy�.������cq,����4
��0�k��������{J}�mQ������~WT��6�3hEou�>_b��y��pL���O��4t��L���V�+S}w8�7�s#{����86bHD���|��j����(��v����mH�%��Q��f��i�pn�fY8���u��D�[���\%H�<�0��"����4��	|���"v��$e�ohJ6��D1m�I���r)�)���W�a�=��n�V��%�N�NV�H��3��x������\cac�X\���KL��HQ����Iht�Rx��Lh(��N!W��a��)�S�	��[����>2J��y��Y��m�<�����[7U�j����no��9����}�Q�ldce�8e�l�����!������,%h#K��,J�A�;���$�f�g8�XxL��Y���Uifn��*~t��-�`Dgp��-���;G�p�;K
�\��qo�p�SA�~W?���D�L+v�%���0���.]h� ����������'��3}Z��{�/1<R����B��P*�Y�k��wuL7�ui�lL*
]S�+�!��4���8�V����ar�|�'���0���0E�WL���Xn�Y]��p���H�A7�8��g����b�F���'4+=Z��V��O�Q��l�m����!�V(��[hV��X��N=k5���l=&�@�}40/����)w�[M�(=�K������
���>��s<�7�_��Q�9��c8=I(�}�]]hd�f����S����X�X�4o~*������P9���s=��G�'r��&��� +�=�P����Q��K�C)c>���t6���~��k@#�=[=��^ %6�gD������Q�h`�sD�I�����������M4��M�� ���*C" ��Q�O����mN�69�����NC!t�"�K1������9�,�1���oP�s�D�v��B����'`L�nr�s�9SH���'s����x:R�	����g�(�x;�K��<Jj���\���������2�;����m�G����K��4Z1���u�}��$�4(�5M>�5�� !�I�}f��!
n~���XVn�26!�x�y�o�@�����H8)U0�%���`�.��^EQU������v�`jh��'�LAH��8I�+Es�������5�eGq:�	_Xe|���2�%�M
G%��^����0o�G���c���d�VrW}�w��Oa��g�@+p������Q���A,_���:���Mv�o��<7?a���_��z\�^��b2��<_�C�j�����af��F�s��O������L2)[R���3�;���� �g����x�'�	����~�JW?b)$Nf[�i?4lMw(��V� -���B0�m^g���f����vT0Wp:39��L#d��DSjT+D_l����%���p�{Q����7�b�dY� <��VW�'�s��@��,=��9T�5��q�r�i�G�
�/>�}(��%������N�V�PQ	L"��Wbf�F)R�'�$�,���2���$� >|�����k�&��1����8e���+NER�9���9����(���O8���w��(��#=A_D��	��&<�>�;��^�wxbN��
���))�PLT��W����6�$����Ze���L�O��K�a��g�S"jdC���%dG���J~�����)	V�,r�\I�m�j&�$G{������0D��5o�#d@���7��]�}�qW}���i��V��@�Pz�����U���U��LF�!��;z�S��"A�h��/$���h��B���pt�`�8���@�p������P�$:o��W��v��*� j�<�`;�d���Z���1u
%��u~y"#�m�j�g����s��@]�-��������~�#f����
�0Ik�5���]�`k� 6����#X�}^�����z"�����S�9��li0��!���lI�r��*�����y��?�@I�H�x�?ck]���43_���6�=#������!B4�T��{SLRK�l��-��`�
�wn��I�e��n�������b���E�j!��lB��i��!��vh
�N����b���7���=P��O��~�yL����x���vL��������;<����M����%F_��)��M�5:8w�>D���3�����9G%I��q���SuSS&���O�u81���#�����O��������V�!o�|N�M08��v-:��Q�=�+Q��{�Bb)�Z�OI7�J�G@��j/xo������b���P��L�3C��bcq��g�)axJ��-|VS�wo����~��H��S�:���s %�y@��+H�!�9l�jS���T
KX�h2a=s:�A����q�nIxl(1Q�����Mo�NM�;%�m��EOnz��|�������;
e����S�ZG5�P��Z�e*]����&�'��-K)C��n�&o �,���)�LE/T�aRIy%�)�W!f���z��YT,�
�8��s�E��z]��z��i������o��u4�`9G
Q{$�B#r���YVo��.w�e$��:+}���=�i�a`��YU���ZJ�����S�=U"%lk��s�D�W�:�����G�#��1��i]2��@\�'0����P��H�k���)����O�Z��3#�W%U�i0p��D���^R�U�'.,B8�W)PwF�R=�������T�]1�\E�AzmsB]���<��F!g�v����
���<��E�~N>F�P����V�=�1z�i_�u���-��p�+��^*��
�m3b�^����C{;����{�8 /�fz,'[��*���$�u��"-�'Y	M�4�������wq�DZ�&Cl'z�R�sRN�bW4�5�x��}��wW�j��vT9�,�Pk�crF���3U#j-�A��n��6m���$�FS%/P�Hn	}�4�Rh	C�9p�H�����>����K�Z�,5���p�DB(�|��.��p��w�F��i�8]��q��'��bI���_�����h��XF���3�1��sO��^G-�H�
�����������:E�,M��j�wL<��"�� �d[�Z�����ZJ�Bk9e�8�R�I��i
��E���9oM���<�=I�G'ay����	LM��ud=m"N�$qL��_��S��O�m�L�������6&��1N�&I1�iP=tkf8��������a�3W�!%C#p��J�����p�J�7�</r�'|�8IT�P��Epr*~(���]������y���UQ��JPz�.�������Ql8�-��3����3����	�l������`8�B)��i=?�HO��P�(����A��F/��ZG��PYO�Je�y��"Us0�{���)5=��>9eJ���qP*�R$�T'o������!���a�M<��Y%�{��8���);���>%��	!��`2��;���i&�����w��
�����Q����p��h�p26��nM[j6�C�?,Sv�����b���v��Z��iM{O����c�@���aT+����H#9��<9���,�&�p��|[��c�*~������V��o�k�5bp���Muh��tz���~��	��h����!�t}�����d���UXB*5nw9MO)�V��M�T�:�������B��7���RC6&m�*��)��ae�`2�l��:��XC-B3������XR
���������2��ED���i(^PA��~w�W��8'[���Pg��������T�i��=x-�������N����Y���������k�h�PG@p�-�QXk����;���r���R���4b�����b��J.bU���X���_Z~�Q����V���F�m��D{��&
���e=�Jz��r}������$rY\�qVi�)JL:4h����m%�������t�V��c/����~����*w7Ng��svO0I����
�Sx(	��Hi�.���"N��0�d2���Ql��[�Q�q�UUqb@�>ubH�<A�����@�x��^o�XIbch����RC�OF�����2J�'#etI����G�a�o5�z���H�������� �d��W���&%
~�Cy����� �$�}MEl���$t����OH	�	�S7�?	I]l���!�����[S����cCC���57�m�b��p�P�K��?b=�~�]�h��=��j�N�-�DQ�����uJ�de3N���o����b����i�q���~���;=�w�:�g#���5!mO�'#���Dz�)�&[}J��ff]����K<�Ip%�I4�
���e�hM�Kgk���g�Xw+cJ�b������9c��\0=oI������%���b"o�{�7y�m�}�P��� �H������[��"���R���(��m;]�������7���E]����H&i������@�l��W���
�\���������$.�������C���}��� ^�A���Pa���f�a��L������Pb(�����:��k������X%K-�{*�D�;����G(��i����"Q�����C�GN���Y"O���@����
��{��-&���(M"�r;I�ad`�����w����OJ	b���GV�l5f�mt:"�����p����QL�0�q	NZ2�R���-�������C����m�O@�Hz����u����y(�m���������U��#	i8J�{
u���4���^�N�3Y�^fc��P�$�FhkJ�m����YU��-���dX���>�e�)vCL;l�����$6X��o�|�O��������t��.�F��=Wd)������F����������I��i�+��|C%�O����/\�q��~{����t��\���y���NO#���d���j�Z�k
�������_
C#u����)�
���i�#�hC6�Wh�K��Z��o`�n����n+[��N'+tZ����'�?D��S+T���U.�HQjE�p�+�7<�d>Fa��1E=-IG�7j+U"��T�MS�0����	c)/e�u��#am3p7Rf���VI�R�/f�p�z,���LO�fObGu�=6y���t<�����{�>���>�������)�4�R��.���v�8U�(��Yx�&�D*J n�
�(��i
������gVw�����Yo�z6!9Qf�"_>���T���|��C��^��q�0��^��������1�0�;������ULbsl�����]l��)~� ��1��!�`�+�'�e�����j�a��O�������;���a���)!l����������X\���	��,%���x����	e���h�Xi������q�|��4��s4��1*wN�R-=�7���K��R��.w�@�f�c>u��k��4�����!I���eN��T0�������@g������aS@����mN��$J����~����w:���f'h���D��w�
!��4����l-�5�����d�c���f�A�2�2H,5�o��_�%u�g���:��h![���T.%��c�x_�]��T����Ei�z�kZ(��i(s��d�>�����4�f8>5�m��*��>@MT#U���>N�)$��"��UV�t��)R���C��O&�t;�E�/����,����~S��${�RCU���� ���@�J{��&��~��{������HE^�K��p������&#|�U����gC��9�*~E`d�Q��3�0Y��.hv���pF����[�+�h������S����	e]�����icqM�����Sq��(���D��������A�v�to {��!��l�H�O���PxOD}�7kY��Q����qi�*Hy��ad6�Lz��x�|*2�������"JX��@U���b1�a���� sF�I?a��f1��
OT���{��{�B��0��d�0��J���a.��nPr��n�4�5��Q�9��]i�Kg�
'?������������t���(�)�-=���c���&��/�E��3��9W����b����.���%gO'd2�m ����cCfA�������}���_�J��<vb�����j�K#����J�dfD����a�����n�������D���(��ACI=�i�{/	���v�����tP���b7m����D�Pj-]C���~}���d��H��z�w�[�*�i�K*��
�f��#j=������������r#<����*3�;���4Yc�����n�n�`��g�����y!8g����d����xJ*`l�H(�K|P2{R2�zK�S����AbC���
Vb�$�F���w#=�N0�Wq(�yY��8�����m/k�PrL��S30�/`%�K�a4�>����!�cez5���\�y�}����;���X}U����mQ�w���P|[��?~���b�g^���`��.�+�)�p�$4"�Cw<���-��F�
�6e?��%�O�q0�{��N/C~�D0D����O�P<�z��VB`L.'�z9�uR�~E�v�)��r?HU���B�Hb������
q�ts"q����!X[�8S>M�f
n��|sWT(�1�����!������� ������B��n������.m����8��a�'�x�v�������r�q�i����c+ZiU��}b�f8���l.���\��#�=[9H�--�$b ��-<gB��k��O�]�R���+�3��!��_�RFb���(77���&;�6��a�� P�z2rl*�R�S*
�����"����*���y[\U�[zr��a�,��������j��8^X5��L]��GCf���+M|W����l
��zb��O�T�	Q����l:�����K�����
����t��D	>�t����Sj�4)	e�d��D������"F�y,�1I>��!�yA�-��R~N�1_X"5�#	��o�(!�a�	�5*f��?;7~��Zg2t�� �+	f��\��j�t9nd��(�:=�������h��������y��tP2S����	>cX�����f�X�����<o�
|N2��'JK��134�����R���rO}�E�;��(�lF���d��*��M��X��s;8FF"�j
�d2�����R
m��)iG���d���������Ij�pl��
�6�2�"��CwR@��,8��)p�ZD�'(0��������=x�}u�(�c
���zr�RNR����qw�����z4��I��r���q@�(|U�5C{JC�Q��k���Yq���g�S�1I����PI��XU����~WhR��j��3tG)�9�)J�
��=5�����8J���=ys����)��}��Yng I��%���	���a���(CQ�]9N��Vcd�Ihf�Y����U�Y��I��>n�Y+������s���c���)MBh>����db����sQ�C��z2���U�O \-1h2��V�-�#NPq��/�,��e�p�^i&T�����hidC]�����v%�3Y3��|P7�u:ZT�sP�x�x�sitG0l���?�xo['�I��5j�M�JQ�������{�v�3�s���V&���r��6yD�U�����\J
O[�!<����3;��5)-r�\M(-���������iv�t�����u�Y��tu�D�e�����������!���g���������m���[��bE�g�X��|�-��"�_�{>f�@_+-H��2;�&��y��"!�FS���z_m�5���kU���'��x���hv�I�,3��Mm����M��+�k����������K������Mzw��q�bVP�{K�&�������N��������i>iM<)SOx��4���	&��	A1�"q�k}#?�W�$*�Y��!�W�Nk..E::,�-���q�A��L�b��!<���e�R�4������!�l+�U��<M<�iB�-����6 ��:~!y��c<�SfKj�8���z�l�N�A"��4�e�X.��B�'�(�����0y���a��
����x)��("������$�~5�85�1DDO�'�1��87��|g��#�S���L�+������~��6����T��E�����Ed\L�=<�a�g	�%c�����Q6=rs�>�+����Z\����=�A�����;JO��;R������M�`)�u�e��7$vbV������F��O��5��i�������Q���6�V�d���=v��Ds���ds�	�D���` �I�d��43Z5�+:���w�H��]���9_�������o��d�N+
�
���qM~�d�W3��e��e�]F��$Z$�0V�.��~��{��7b)B����9�c�wK�O��$�Id��p[J�>w��i�s�	�
�e�Bf ��C�bYI�U���w
�����)eH�
������y8����������S�������L�H��9�����48��z�%|�7�#��!l��[��~���(��*		(Q��o��n�<�ocBO���
Y�n�l�X%�/O���H1l��������r�56�r/$�m�ur���WR��������i ��������6<����qU�I��t��&��[�?d�~��_��-����5���)Q
����]���#��bQ�����S�y��T�J��n^D}����<+@��D�o"��u��z�L_W��`n���������]~,�G!�#q�3��2I)U����4R��W�V����5�n�{���pc�58F+�"���8��z�������c��g�����@��*w�C):���,�2���!��,�*Mp[I %�s����>�i����D���I��S�4�.�q������o��[,�	N;��~���x6f��n
�����]U82���*�s�]>)��<�O�Q�������
M��
�7���)MB!*@d�`Mf�|�~FM$$�d�M$8���yH)�������P?����x��:y�a�\���k]0��<���)�N�fA��^o�8��~����Y�nOu����������>X�-���.JdP�.<\�%/(�t�� =Y9���=��O��[��h���`�_+��K+�h�]���H���>��m���-�������B��Uc�������������?T/�(1S�I�?{,�������>t�9���x�r���<�$L~������t?Im�.�^���:�q9_O���a��c�_�����5w��?'����g������Z7�]�x�r��&���Y5+n&m�H�����
���=�+����*Z���}�r5[��qB*�T����a%��������|�y<�	�:#6���"��q7�}������	�i�m/�/�O�a�����}�{�C���0��I��TD�~��zr]R��O����s`�`����n9&v?��$�&���GP�����@h����Q:�e������{;�g_d��q�����h8o� ����y&�4���)��G8!��Y�w�����p����n�~��`��hN7����c@P9A�1 �T�rHt{{����c{���9���p��&���9��13��6�{{>~z��*��� X�����gnV<HB��+���?��/������O���p���3K��-�����p+!f�F�R�����v��[�}�7�%��Ic�� 1��f��)�$��rl�9ej��5r�t�q���1e����6����s�=���A�z:Y���3�@��0e�HT������_����1_����G�91����t��/�D�� �A��_�G|���M��ZRwt�����X����'�J����G���z�Go���R*�Z��2���	r*e7����l���J��F'��xE-��&T�Y�`�J����-��iI{{Ww�$�b������`B�������������S��$>����p7�69����3d���-���T��e��}�Gx8wP�3�\J�O~IQH���}�>{��*�]�b��^}���sPU~��r���R����r>����CQ�U���V�::!��
���H>t�_���d�1�V��m�^�L�������5�B��8D�
��� n%V�B*�0ZCNX�������O��x�w��u��;��sVCE����y�X\�k��@5��sB���;q������������V�kC�2,���@+f���}t��^;HN��tF���dH��$X,{����������?�/~��j1���$�b�Grr�J�k����)�xi�������q}�k
L)(/L8]]J
0/�|���MT�$�	y	�����u�`��PgH����I�"Q��J������|X)M�sn����k���?�K-1A��dB<o��P?�O}����5L��/SXr����sN�>�?�3�s���R �kt�<�V�	GPRj
����?A������}��T `��'%�7��Af��~e�Px|����eMH�����pAH����(���,��$Y�"�������8�S����{��*�D/<-Y�R��K��]u3j?�\F�QQ��D��]�:�0�}�`�>�@�E�8Rx�%�!��!�����;>���K7�7�{��5���6@:����O�P|�wm�v����Om���8n����w�c�U�Cy>+�]Xkp�!�F��8����\�?_�X1��@i���O�@�JX6���QB39f�+&w���i�����+�����-�1�^|��\�,�F�2Bx���Ty�X�BpB�>�?�>�x����YG`�3
���~�������_���U��M��b�[�K��,&�k{��~�~��mWi���3i����J�$hI���s���l�(�R��O� �k[�~���'�=���+���A�u
����Rp^��]_�R��I��X�K���rS�hUV�m���c{r����|��%���t�
��E���.)��r<
p����T_@������(20���z���/��\��cs\���t%�v�u�����Cu)E�����7.���I��J�#q�)U4(&
X5^��h���A�PQ���Z0��U$������2��V��g��^�������\F<S����`���
l_�{��p��HT��,�� y��Ww�����������~GYI�$����p��%����-W`�eT$��u�s���rp�56��]F�R�k�pX��4������D��L���E@c�0�5��\n��L�?����������'sk��(�%+B�|��j(�����lr-��I��&��Q���f�()���;J\�A#/��{������|N?��P�S���6*����/m�X����Y��%����X�e�����f�n���8��:������l�]=t���pu�h���tZ���(�m:�p
��f������GT%�h��m.���\2�]�Q��\��i	�8V��Y"��#�i��u���VO���
R[Q��I�R��!�_��������~<����tJhYXf�&We�����j#n��
H;}aV�.)#��
YPV�>�n���\�;wCsx?4��F������Z[�`�2������W�
�D�8����[G��o��6�����G3#m�@H��$�@X��+@�/]jW�J)?����cCT	=��l�c|�n�k?{��C��4C�
[�����d��&��]w�y��RR�r��������F����h�E�^�4������qqA�����+�:�_�X��z��U��\�r��R��5���a3.�:���X��z"r� mF7��:[72�GCAi������
�{���i�u���FTwi��7�50�n��j����u5FZ,I��dT���/#?������� �0
�u����SS�
@��Pj�����5%cpP��K�7��+wI1t
���[�O�~��Y ��F��Z�f���,����=[�����lD�5�$$4?�Bs���bu!DWg�v��Xg���!��a{>��T�2j(f�}�����q����#����\�w����C���U����X�
��H����C���>�a(�������a��@(������c��<���bVC6�-�X��#��U'k�*�������_�\�g�iK��$6���j@3��N$\�	E��gr\�
d����d�b#cq�����du@�p�ZNTAM��v��_N���� �,������B���G����c��0n��a����������;x8��i0�d*�
�u��Z���d'1xvCw�^���y�_���\\����Rb��sa����p���Xz���l1����p�?5��<c����b����CA6�1���R*�+t(�Z�������j7C���"ak4�l��^/��ft{�@��A{�Q[i��E0����uw�?��r�?�@�������!J7���,��������>�b��p{��}���<���3�_)��t�:����� 7
�b��n�K�U�~6�+��H����d����J"����j��j�]O������=�l�4X:�����I�F7W��b��wY���m����G0�k}s������E��A!s����V8diN�w�����=�Rth����m�N���4	��H���`h���c�w����$�&w�����������S�D��>�%�,�k9;�7J���cV�S���h����~�U��J ����5�6J�J-��$���#iQ���h4-�{�G����n���l�����@[��U`1d�h���Hi-�g]S�����]pP7$�9�.K���o����}��)��sLDoL�
�H��gN1����#%j��$~���7z��~��a��<�m/W��^�?\7�]^�.�:l�M��
�'�5��F��������W������k��>y���W�b8s#9i�
h��]��T���zF����&>����)���MR(���z��������$�����t�z�c{��>���~�f����S�yJB��8�	W�S#a<I�F�b�	a�Y��k��3���G6s+H���H2"5*������Q�pz�.���;%���T�9:z�Q�u}�������w��	@���C=�8�I�zp�����sO�QBKBL"����x�s�4����$C��������u`?�QK���S��%w!vIso��!M�&��K����K=�N?�����JUY(Fv��Ky��uJ`jcFE�~��l��tK
�yX(,<_��\�zUr�w-�b�\�S���M�Y[c�5��w!)��DDJ���Z����<������z���9��4�"J>�����(�C�������^���P�	��?�,;�i��{Z�H��}w�O�c3�E�2U}��$�	�x�3U��O����t���t����������,L�S��H�$20f6��R�]{)���\_/�_9���{aUa�D|ct9s��=����{��,���fC"�Q���N��L�FUBKA�>4n����j7���vj�����N���./��u)����8KSPi�[	f�X#Hs�����c�
d�d�u*%F���9OM�!�g���������)�L�>d���$���8E���h]�b��O����y�]�p���
��
��`0 �)�S�J��G������#�/sZ"5y�e/(�%����t<�n�?��������K�u�$,'��:�XAt����=�D���d����� �������4�����x@�}%%�C���]w�F�����?��S�['R���c�vMT4�3�0�}�%�����+���2�kFp�" ���R���n�^��?����+;������M}t����"���>G/|#��~��|�N��;ce��XL���(��!XC@��.EsU��<w�����
@���w��U�6R�@'���p�� % ���3n��)��R�����b*��>J��W��]��������S��O�/���e�'���/�Y��C����h��E���7`��m�62�)�A��}M�G�]yG��3�0����]{H���q��UA~�p���y1R��HIH����F*M3��\��HB�d�P������=�@A���/������1���g�[��|&k��������Y�	\i���b�m7��;�O�djKV��Pj���QX9�s��/FR���^T����.$���Q
p�E�DV���9�]t�NBE���[p�E��a��t|
`��r�v�^���������� C�
EV����]������K	p��Y������^��WT�hC�S
|k�
�)R�g�;�����nN����e�BV@��S�r�����P�������������u(c�����7��~���?����e�{M��*.��`7�
6��
:�,���]��&q�t���<8��<���o��4K��R#S������U������s����{_�@o�(�"�E��(� ��6�H��$!S��
���n?������>����@�W�$R�f�fBj������]���"/(���Z��?���
w�8#	-����@�-��Ix����e��T����=G�p��y�3�����g�/����ci)D"03�!�z	�,&2E�F�"�!)wG�`���'�d�[���k�N�g���l_������L)��D>3}��g��T?W�o)���QO�z�#��+��h/8�7��u�eI7��.�0�������@8��o��K�R:�92��b�1�G>%��(4=X�
�!�p�(E�����g��A����po�rc�P�{cju /"E���R[1��i�?:kJ����e�	���$�Iw�� �L?�R�#�<�l����~���\a�	&KB��w�9
1#BVo�����6nZ�q�6N�K���%�#���H��6��������k��f%'�<�mJ
���!�[���.&���/���z���b��t�b�'�����K�Rz��9!�i��-�K����R|id������>���6�_��#B���Q4��H��L#snh�JO��v�O-<4��s#�Z�]u�B����	P,/���R���\�g!��c�AJ����D�����`�o�O����b;�H��Qb���t�11�<X��}�y��� ��5����In�RBSJ�^d�~M�a�=�(f�!�?��QX��n�.�����d�� �7/<@L�<�/B�w�����3�Fm���/���u�W�r�����.p	��P��Rv���������B��D�{W�����g����QrZ�������Co���uh���?�Gs��Y�q{����(_Q2|���P?��p��OO���T���������*:��^�Yz������o���Eb�
��!�k~G�j��A�=��)_��/����$�b|���_1���5��"������6�~l���������+7��jr�Lw����[�0]���9��Sy<N���L:>�IKh#A&Zj�]��X)G���nm"���&/�;CWrp��$lB6�_�Ww��r�77k7�3�K���+&q
��Eb��!�y�����b��b�����Q�n���s��F��u����V���8X��R.��u9����q�Gf��%Ww����b��G������g@n`��������^!�J��g���}�_K���*��c>S�J1t#I�F�R�}�8:YL8<[*(�f>���*�]4���d�`Z������g#�������\�aXi$@���V�M�����
��v��A3���5O~�2�l�\6�!*P�������
n��A`<��A`�)��gZot9 p��sMd?4@��0_�t2~YX'���<�\8��_�|����^�2�z:�%0����}��im<�"��FE#���M'K���)�Y��F�.����T����/���jO�>���P���e,!�����r_O���96�����b�#�O�{=����a����j���Nn9#��x��2Mb��VL���Z#���[���P�����j���5O���9������Z��
�a��
�]��^�G�2�iZ���q]�.�������o��$����;���:�Z���I*�%��7�������_��jL���D�d����Y}	�%�������S�B���}��h]h.�

�l��d�@~�R�)��_�g�FW�l��,M���&���H�k��cj���Q���fuc���$|�$���-'Y@���S)o�������u9����)���x�7��B�Y��b���9T��
���!F#81[���I$*�D�w,'�������~x���������r.X�<��5�k��H+Sxe��B��\}Q]w��;�Y�j-�����$0e��N����R�������Cd�*&P��b�u��Q�E|E'�����6�������xc���\z�M��tx��Ib0�\K���dc����������%��S�g��Mpy�*_��Wh�]����}�}��5y\#V�T��
W�nD12��������3Fp���7+Y��+�������l���N���R�<����
�|�%���7���o�.W�4B�]��NV�����\�'CvRG��{�M�,a_�����Y�=E�Y��d�|D���!�InTH*.�_��v�gnvN(�,���5H�����)�p���U7{�J�k�������������xz�a�H� �������1ga��C�	�W�]s��~�����R�:�=Nr������}���[�W�|�VR��!{>����q�����y��^'��-��[KV)S�.�q��h�Y���E�'�����K���u��GZ��R�	>����]���I���y�)���>���\��rK�\C��$��d7���\�������_�w�2���%���0$w��M��H�A�y����w?R��Nd���67�Q�4 �p���a�n2��t��N����I�+�/qOSw7�w������iN&��=w:#s�9����[�9_32������g\��������A��P����)Y��:�1��!�8�����}��o���b�j/P9FV�QP��I�k����Y��K��@����_��2M�G&d/��<�
g�9���'r�-�_!�H6�3����0Ev����Rml1�Z/��u�x#_������X��v�����|���T���I�������|g��Yi?��}����x��i,���O���Fr$
�
�
6l}�~�|���$��e�TR��*]Id����b)���3VA��o�Fr���-WS(����`��A`����p�m�J�G-�F��k�6�26o�*"�ze)��*�Yy1&���g\����^eJ"�orw��
w'���#3]SJ�U`�uHd�I��HVQ����_��^�W�^�.���DN����R�Q��(����Di������!/�;���|�>��n�7,��BAi���IeHEQ��aS�������y�|j�)0r�����.�E�uw���n�)�*�G�z��54��
�>e'�8��oY4me�_�V����F.l�5�+/����ex�k�����T�"��`�2������~�Gw���[p��<���i���{m���>DlY��%��[�����o��/�|�o8�7t����dw!#6,M'��f����(/�,k�|}|���L^����<T^��W2c�:~�_��tl.��c����.�E�+�OqN$�i�R���M�~��hR
�������%�d������q5#��[�1��
�(��~���n?��q+�6*�c�|�� �����6�HW��!��x��N�j��B��<K�^Hp���h!����7/�<}33�vI���A��������/�z��e�t�m���;��2�����C��t�@�Z����������@Z0��������Y,�A����KC���q`N�N ^�Cz�T�uz�6�Gv�|�n����Z�X�������(]#D�����E)���E�ehs���,���V^����%�{����������=��C�V���4y����{�MA
�?`H�&�k�P4'�^@�(���@Q��y8<�]{tJ}����,qY@$�E�U���W�Z�1��j����Z�j1��C4/74=#B��j|j�
�6��^����u�������N���w���D���7�����7�2Y��>r8v!�t7&B�A���`�JB�0u�z���\��{&
G2/�AK�����\�n�����~�P����-1��E��ba�^[�^�t�y��y5�@�b�'�JY���2��������Rq���+$C�TT�����e����sH���B���C�������Q%JQ��e�����G�Rp��y�9%���,%����l���:����9�y���?7��>%2<x�.���7O|P.k�p1q�
�&����G�������*X!?}����|����

SX�l��S?\��ecdVK�\#C��"j����^P��pnv�Csp�B^�������m�"�tCI1�����J���E����Gr#�YP���Sbb�^�h�N���b�t
(��)��>y����qf�=�R!�p�0H_���Bu����p��B��uh�sH3��kH��:[�� ]���X�7<���}����g�'z�wS�[o'��LOht9�\�Pa�8j�/h'z[���E����x��E�)�8
f�Q�M����t�>��]�@~�]EB|f����Sc�d�@�!�P�J�;�>Mj3�xm")��6�%�������l����l���m�8>�yRBYT��\�JR�U��9r��1�~I�Yw��G��I�R������H.���\)�#z��<�?��vQ�l������>gy��0M�$��L������P\���EA\�c����������DK�
:9����,
���"=8g!����3>rfx�K<$�j�~������IKD��9}�:D��$�-C`�%p�S�Z$V���E���f���Y�~p�H,7�Q����H��	�(�b�K�6����{H��G}��������3C3�g's~xe-�^�R�����^��n_u����D}���Q^P�o���_����{{>\���;	���&�A7�9J��T����|�e��%([$n�\_�%	W������%�����e��w�s����T���8e!���Rx^��������������/\5�!s8���J��/O�������mV���s�������M��a�)�`�9A]O���B����w���	�\'uR�~9���N�d*4\��^�T��~�����&z��:������
��s,~��w^�~�EE����,*�N������&���!�R����������EE���j#��>�����,%�Z���12��p���zJ�_���}�"p�\�3O��C�e%4X��\��������Kz����Yx�@I	��Fs
A4��6&$�������y�������z�z�]�a��������>�idr����\u�f���OO�<y���8��h4�@�93vE�:j���D��z���uw�����r=uE�F���C�&8��\���u8����fr!�vse(j���N����������[(D�^�����a<19.Z�d�����"�L.�T�Z������r��P��7~��]�A��?�)��m��2���H�b�r������;O�vA&!^	���ULb�+3Cs����A��`���D�N�Q3�?M���)Z�������fh�'��=����+C�q��r�:�+;��,8�+6�`��Z����B��Zz�`�`�Z�R��\-bX��h�0���k�����CV�6����L0i&�:#M���n^���!v�����c�8�����FK���=�L&��^���3J���>����d��������z��uIr�{]�y,$������.��y:���sCPVD,�Z��XN 
I�{��
����������:M���#�QQ�H�VL5K�)t��+`!����7r�����{2�����/.�
��=	�H�bc�\������[@P�������2�n�6<�3N����(�8������a\�B����F������(� ����	�����K��-��V�b��f.I����u0���d|����O�����h�l��'Q�����k�|�����i�8�W�^MnyA���S\7���+�NOe�g���L���bX����������W�?p!�����Y��<�_Q��_ME'�-[O��u���Q�������7��%���(�	�+��1g"��'���
���q��k/C;F������V?s#�&�m�KA����?o?��Y�fI'��hN!���C���yx�`L0f����G�z!��E �$[�
����(��
�`����������U���or�<�-����|�
��Z�M)
Q����v�\d��!*d&���}����&$oSy����9f���5X.�X<�W���Y�/��$r�c��PV�1�����5�Se���fr�0KZf��;A*���c\��0��C��xM+jd�5b,�&�X�t���:��o�[E�Y��@�S�T�����"��<.�FB�]1��{�c����'YbJ�X����F�"'r��\��K�2�����=��x��\�H^W�q�F��m���swv�#���4�q16F�;��������W�CgT���Y����<�.����6��k-�4��b
��O��=*�k�M�����Se����`+:�M��63c�9b��+V��#h���q 2/,%a�^��������
v}V1[�=�
���,���G���UJ!!��T�������B)������L{v�#���[U�!�JJ9�^��8��}6��8K���E�����qw3�o�����uc�W�(�R��������E'��p���0�x1up�Y����
��A����u��+�����o�>�_�,b�~��2�����dHka=����j<�����pJ9����d	�K\;G�N��F�xa��R�_��u���BC:�����]�����n�]jb���&o"z��;�S�\k�����[@��������='���bC����5:�,6�C����x��qs��G2�H��rea�h4�p?I���w�/�U>7��>�x���t����B+1��Ry-�����������_A	��v�>��B�	\.5�[�P*O<`ArYm���6hv��4{lD)r���!TL������t����q�:��2�$��C�$
���bdd�8���d�~�S�@>��@�$|#�����DX���D^/u{�r�d1t�x��	�Xs�F��q��E��9
w�).���f����c�����=��9`����F��sf����E3k��\��
��LD�����d��M�����Z�`8wz
�?U�X,���S4$�Wq�,�
'��"�9�0�b~�U���c�P>Q*�����&���HP��$���������$�
�y��(@[B���Q^��xj?�7��?;�k��(i�d+������#N��Hx���L�
���-P���;��s�1
�G����1Si��r?��&���$|�wm�H.��Q���a�=9�d����AxPc�`��9�6[_wKA"�����J	��V���h)�!R�K����I,:����Jy�
J� �D�� P)�5�Z��Q�.�In����q
�$�-��+y�|���v��`��S����D
���L8e�sn��!�U�4Rs�S���X�0'�q{0'�F�7_�u*,b��r�P�
r-��kL)�R
������I�a)�aj��*lS�G.�����L���V��\O�����������/�H�����������t���\��[�F!@�����Y[�tdp�`���'G���g�[S�o��\�?�j���M�|����lpX�{[���A���_I��0�o���_���~����:��z����2f����xS�aC����r���������~������aE?�*n+�:DKMpV,`���(���O������&��2��PIrj4�o�,��n-���ZDM�<)	V�2�����
�r�}�R���;B�g�w�%���cU����@u�|����T_On��X���m;wm�A�
6%b��+�:J�i]��{����;o�>W>��C��0S 07��*�S��I2�fs�o�?�f�L�Z(�S�xCF!�?��L)�e��4��J��8>�F���
���b�3�2����d�&��T�c��_���~�w���O��=��_�
����D��"T�|�i����������h��'����K�^�.Or��B���\B��`�r
mf1������o]�9@(�r;%Zr��Y�lR��-U�k��};�"f�Cw����E�1������y�z&<�r����$^U��,~=����'�z��,>�o�UDu�$d�
��js��b��N����s��Yh��H����7����FE9�����C�q���?�k`&�Kr?��jo&�G�/u��-�5g4HR���_����!o�T&��T�<j��)�gVg�n�"�5��E3E
�!���8�K��1F�������\�4����ch��a��?�����O�}�o�}�l(����.bx�T4HQf�H�g�2.�l2���,�k�'���8ad�KIO�V���bJ�������n��K��������*M[��3�KO=���"���=V
{�KHe�RW��bC��
�����"gUntXWd��G�v���
���?��E��k��������T?v�����(Aq�_p�
7h�)�fkY7J��B��P���!������������:������*C,����
������+�m��]�_��+C/E�T�e�m�#�\.�I�����tc2X���P#yD���w��D��A�����Z�f�ji����Rs�;	����������;���8�O���
4�&EU�TL�fpaK��@P�8���i�I�5������q����S�Z�o��n?|�{�W�@X�~���N��4�O��i��yt
=��O�8�k�/�b����������LY����iv�}����@{����������q�O�a�$��8(�������T�����l����1�3�&�}��R��Fz��d��F�nT������v�����u3�E+�����w#��Se)�|����/c�V�(j{~�(����`AJi���J{^AW�����'��B���q7P]JE?��]������)����S��52�bXu��X[��b�v
���gR����MawU�"������@`��q��j�W\������K1Z����h�b�q$:�Z�,�U�lI�sY�\T�����������i����}���#�6�b���������0�P���]3��O�r]�^�+����0[���V����=��GU�#��K��y�M�����
��pQ�^�e�1N\f-�����&�(uU�,JF���D��y��U��PL��7�,�Ch�D�K�O�KJY�����ox+��&`�g�J2U��T7	����%
js��-�a�������wMc$`mM�����6�\�*�;1�N�/���kN��������}\�,v#����?�r�>{�	fT�����^n(�����g�F�$I������u_5+�Q�������*�-�,�G�r��[e��B�;����������QXD��B�X�_Y�SE�I�:�bR��<�����_�~��/~��������9;P��_z�CbE���]�r���u���b�Y��L���?�5Z���k
D�r�v��=
����kO����z�t�K�E�Sv���`8�
~�����]�F������9��|��|t�Q��n�������S�6���#_�������E�*����U+g�II��xE����*y���K=c���n�������)�<�/�y�li �.��>��J�J���>��
S���%�:V�9�����e�>������)� 'F���^�s@O6#4=��PCB+eSq8�?^/�z��\����"&f�b�Ie�Y����z�p�U1�Dc��u�hss1�E�PW�s����z��	u�s�����������L���"�.�����/�Y\���o&�#���O����H%�4\l)�"k�^�6�����y`�-��+Z2����Q���X"�IM��D�+0~�CQ�5������|���??�����x��24�������7�^P��r����c
����� ����9o%�!�}�V&�����H|���
S����2���1����
92��w?IN������o>���|<��P�� ���$�<��md1�~7i�G`�|�����9���G/����r_�R�����R������[7{��g`/��qA���^���	nTTp9q���g�.��X��4�
�5cu�"(��2�ai������������R�P|v�V��?G�R����h���g�������G��o#Y� ���hY��}�`m,�r8������h�
���2s��������c�)��S��h�4V�(�r�0K���9����4�@|�>�LA����ZT����*�&���W#�ix���iM����duw�����K�@��<�K�0��2x	�\��"5���4�tG���>?�������
�pV�����2Ds���h%X��O@|�3�?�����k
nTH����3�6s��a8�7vc�9��}C���Vu{��d���f�t�h�|�B��08���9bC����#� �o������y����x��	`��H���$1�����\�I��c{|h�E����le����{�'�ZPpnD&75�b�`grO?���%F�5�
-����ZE:�XC0�@��xY�5�F���	���w�JL�?8�p�P�����=8���5���������.��In���1~�z($���NM�R�x����?�s��(�4�p�7��F��$�SX)m�W^�u=�d�� ]2�V�V�<)f��[
H7�NY��e#�ki����TE��j6Rpr����~�9����������:����S)$<�_��/�b
F����[M�����4��,������"02(3�X�V� 9����AJ:�<���%S��^Hv��^�}~��"s��nT��fS$���n�;6
|u�A�Y\����ms���N�}3���}���?���2���FWk��C*k��(�����v�	����+LvH�s7�6>���h4���U���
����9���xj#�W=O�6}/���1�7a
D������d`��������h��d�1�T��J�zJ�X���L���|��l.h�^�����+�J�&�G �Lr�q�v�����o�B{4sx7��O�1�p��)&2��QL����U��y�����.*e��Z�K�Bg���4�������`.n#�!�����9f�2>*���&�`�z�cO*�|}�����A�,+������L��iP����i=C�'#&����U1\~�;I�Kw_��O���B�T��~�Z��;�|i��A����%�$�}&
n��=+e����y<^7��6��*hq�<�M�4b?���e��gx�!�,@��\���)C#�IH|'�+_+���=-A:���_��i���h;r��Oh���!�t�0�����`z����U�e&����9Vi���u����Y@-?�]��y������<�k��|e/0z	*.V��B"�y�M�/�92��^�r�T���#`jj�\�}�cF����f~n�T����hC���eN�K�R�n'�D��iC>��������Q��_{���Z9y;�Q']���� Z����������)�f�YL�S��cG*2q��?�8P
j�����+H��y8�ydbJ�������I���p���Lh0���v�dl�\��t��7��j����WVk�X��DI�=� �s�����~��T>B��fC���M����b8Z��kJ18#���f��p~���U8J]]�M�kl#��;�P��f)S��|�D�,�C�;�����	����v�cgT��r��������+J���DM�����Xj��a�[���[tU�o:�����E��b����o��%eF���d���7	�"��o��� ����/�f��|��y#��[�$I(4`��9Qem�����
�Lo������X��X\��n*)*\���F�R���d{��9���d�~9����.(W����r=��C�?����>��	�6"�LR����x�V���,��?��8�x�V��������,
�Wg�e�����0��R
�������c�]O�-G�h �q��m��b������K\:��$X"���Ob�������yY�Fh��-���(&D�dA����P�w����e��B�Bv�!Q���S���1�����_t:�������K�N�Edh�����u�O��
P� ����7�Z$�=Y�B���SZ��7���7}��Z������\�R�I�����/Y�< ��!��+{�$�����i-����S���[�/��qwRT��>q�	��W�����"!0���)k�����_t��������;��l�l�P^q�;����y��e�f����g-P�A9Bs���������|�j�4`t$J@�����@"\Ft��v��Ws6��u~wlD's��(eB�?>�.�����9�B�Y��uf�<�j%����=�"�+��������V���`��j
�Ftl0�b�,��<����K��}���7<�jr{���a]Y���R!��f�D6�k���������y3���N��J,tJ�T"5[�
��O�����}��s��sd"O�bU1��Q�6��|��Q��1�a���

��+q�1\��-&�r=�OMw:�w���	O���D�	y���5��"M+x� ��q���[,|���k�2�&����4u��}���=����ETD�����NO"i����*f7�?��,�.#?���o�
���8��,��}����|�t2-
v2k}
��_>��B>����	~��:I �b.���t�V�?�3w<�/���z�&�F�Rzi�>�.� !��D>�$����D��C�.&��e;.V������f��N(��c�M^@�~�{��53?V�gr���l�o�6V�3%�`�����������nN����G2&5�-Ny��q���
8�NUS*��y�B�f������tJ�}n���
�;N���sY)o���
`?!��L�����:UXP����?�����sf���{���-��`Z�F���RL������n���{-_�{v������ 
 h��]L
���'#������J$)����NSB�^��xG�i�?���(��!fQ�6w�G���{R=��?��<4��(_�F1y�a$���<e�|�Xr����������0������
v�xjr=��8��sT3/@>>�������O��n����;_������?����$D�T�/�a8#*\	MdJ�H�+!��yW�b�o��s^~��A��r,d�3\~�S�����s?<u��R��k����`~���5h���8�}��~k�i�-����=��R�����Z����+�41q����R��������2������l�X�p���*1�]���dTL���,�H�	�gcq���>"�6������~���H0g����r�?�[<X�-\t����0�}x�{%h�$R���������SVZ�	�_��I��fH�����	�w���\�{��t�������d���w����R
�G�K�o9���������1�%��9�52M���l�C�>�1���bs�~����V<��V!�Q��k!���D�i�s�����q�����"u6�	=�4
(9/��[;���\��l�Sk����]h!,��&�p|�8�+�#G��c|q#�A�i���d���90uc
�B�f��R���q���l��*9:/W�I���!���#L-X�>���|l�~z:����J�]�n�����y�t#��qZ�bRv��b_o}�t%��
���@2T�s�x��-.�>����z�����e�b�Ea�F�g�L J� ?4������m������/i�~`\.Y�����+�K�-����{4!z�CX��%�!)&4m\$����rF$�]H^
n��[&

���E|�;es���,�����7��2�D�s� ��R�=���K�q��
A}pE��R_������<{����2L;��	!uz��*���H��)��V�^��?���/)*T7�&�2����Jg���oEd_��g�h�H��+@
����[����^OO������7��s�!h��$l!����T9������f��}R�������Gh:����~A��^=���(�
�p?����%�d�~O/d�����L#��'C�r70�)-xHE@���*�-���P������'���E/k�X���,�(S���pc���g��M"�l�(P'��J��M�2��1�^��
;o�-������	F�w~���FdlG��(x���;O�(`f����������{��3��`!�~n���i�G�BJt�b�pV��~����������t}�R4*9����IU��x��[�
3���H2�����;	m��b�D`*E�l P�����_w�����H���7��0���d7��C���z����v��c{|h�@�}�>f�W�#N����kQ+�SFX���`���B�uBB��|:��\�����AZEI)O����z
5	���g$c��&fC�ah�uH�+��fm�����u��y����F6X���]���@Tr<��kPHl�����@���\P��B�������6�d���<HYA�nw_�)�r_����,�]
,=����8l���i��_��&��3���Z@�~~���s�������TT`�c�V	R�#hL����Ro���~��n@�����zL#!�
���7J�=HV�CE��v�O���]���n?���7��sR$<[����X��I�)D=���rL��(Q�9�R��+�!@�9�f�Z��R��j�.��o�CRdn���>����!�f�XL
L�[
K4m�6/��`<T[��!b�0���5������,^[���9������3
M��n'�D�t�)&���<c�d��)��� '�Y&6���<�n���Dle���p�W��=&i0M/`���6
���L�������{�����!��%��^V�v�������,��1��)�6FRDx�+����Y)�~���b���@�s�1����1�Tr�d��3�{Y�T���]{j?�W�������tu�d��GJ���42��������[�2��V��'j_@	
���H������@�*�H�}����#��'dr��X6B�������H��L���^s3���h�hr���P���O�^�o�n�a�����4��j]������G��q3�u�/���*�zy��p-w�"�,�FB8�+��N�I2������������o�@�`b�3�!A��+����lH)���:�~9_O����8�E��m%��p��?D���Vw�����Oc�p�x�����.� �`
��>{��E���Pu�A�In0��E�4C�/��t��;o�������������^�2�Z�#��*��������'��,�C�/}2LE'y���$�|�H�q�����b�2�U�:���^�?^/����9OD3�F�����j�	�"u9�+�9Q{�F5hk)( ��)�	������t��_~zc�]{������!u���#7g��@n�z���?7�~�OW���R,ob��2���9Y+�3w��j��wF���{D=�c����=�'�[>����A"��49J��C�B�G�'�i*�����������)8xN���,��F�?dD��X��)U2�������`'e�\������W��f������������
�\��!w�m�!�d�s�[Ho��F���o����|�@�����K�]E�L>������L�J#z,7���j�����7��S�������K�����]���F�f!*M��"cl�n���_{�
��.����|��G)�`��#�)L
��uR�X�����
t���:j;����n#����	3���/����m��"��'����)����a�@u}���6h�:��@���{L0k4X�$��.���4Do�����*%gG)6,��/M��)W'�����e�~��v�k����=��7y�,�'O}��}�LT��v��.&n�C������,GPam����}����f���(���Sk�wt�"�]��t�$�5'�A�G�1��,���_���7����C���O������y�t�,lL�w��n�%����yTJ�m��7��
�Bd���������!�)LN���/������}�T
�g'I7,6�0�T�RD7�d3"��4���������+4���,��o6Ai�,)u�'6G���t)/�����5�?�}�5��IV�v�t�If��
,AXr�S��C���fh���!���?����}ktQ�J�@�����������F=)����������"
hZ
;������D�E����(�i9��b�H�*aS��N t����
�A?�'
8�n{=��b��r�+���(d�5Z�c&��[S�U�F����."ec&���~�I�#��P��#3"���D��o������u{�6��!�B��AAA�������W�.���:69��3)�M�!I��%��$�oOO�pR����*
�H��V����0P<(�
�0xjOm��������kw�B+�Sg�Y/	)��P������[�R��/`���St�F'%o�������*fi{�]�O��\�5���c��c���F���A����&Q7*
��T=i;�~h������l.Igi���O"�q�������M��!���2q��(X�r��c���p�p�t�$8?{�������E�����	�7��
��?����k���o
~�7J�9n�q&��3���%�l��!���������+WV�"|��I�Z�-�X�����'0*��Pj|*��(��k"
��2������&cn��z��IIN����j��f��v^�����@+�q	R�7����*ZW�U1(��������R�L����|�o��.v|����������7�r4�5��(]�U�Pkf�]k'�UW����	=���j��F9���*2\��?Z@C~�_��q��a&m�.h.^��)��~�o.���|�}�;>��)K��HB���E�6�b����n���2��o��ei���T���5��I�rle4V�H�
� ���D�4@�z�V�Vp��2I�t��{9q���l��E���u��91M^�0�T�W
���1;�p�?��S�Pw%��b�0������B6���0>Ep������6�����Y��D� %11�Z:�H-� ��� �+��v����������(�t ����DR�����C4
.Dj�4���5�wseM��8j�a�N~��n����z����E�����;FtM<�:�A���&m"h���o���E�����d�4���c%q�z��F�R�G��������hwG��f�[w�x)g���A��,E��
����{�- �L��r
�K��D�J���J��H����M
�>����}[��R� ����${K&����t-&'�o����"����m�.�����q�v.�1�9{�t�-�./���ma8��o�:�_������v'��I��
�`d��� ��B�������F��V< (�H�#�H-+�p�+������~���P��d��(x�<$�P�Q��(e(zi��gI�#�=��#*M�\
�����>�4��"��&�'l�}����t��t}���x���5������������
��g����������<-��J���rNb���q��*�=$�l?��z��.�Z+��U\�$�����>��t��D����8����s��
8:����1�A��@��2Q
�>����!��_�;!����k"k��A�{SJiu�z,�������N8�Y��U)����:��?��~Ve[��JR,��g�����o��F�1s�[�Ty���lKy{�!6���V�o�������LY�����0/2���
'���f���~�o5�ku3]���[�c��WP
+"���&����H�_c]�}*N�CpI�q�^��~�YyP&��-�x)�	���p��e���}���]����,�4%�`��~A����������HCF�79��S�]���A��G��t�x%�$����&����mU�@��oF��@?�"A�Y����f|�^�������1	�k�R�M�O�gE�?��\�ny|"a�0K
�����8�SY
���~h�Whs:yE�j�[��MbAs���K�ty�|�|u�#������
���Y��v������U��t�>�H)sp�Yj�Rf�)�i�W����$�_�.3���^�.�$"�������V��D{{�/�����e*�����Y��������p�:t��0��sw���=���9���8���.=4����J"=�D��P{j�s����_�����/���^��,-����@���X��������g67��,="����z!����kD��� ���B������.�2����l��.:<�P�/�5F�)z"S���K��^����4��~�T�$�JlD1���C���q��k/+��K��E�0���b��/`��-Ap���0���.�������sf���X�$V�������O�q�{�o����F����l�p��i���CF,�p
��u��\
���`F����$,����h��:�g���pa�t�7��\����C�t���Oo���J�[��YO[j@��Ngf��CO�:|����R�G������{��Ksa|�E���t/1���]�k[>�]/(14N�g$!�\K��r4���)�������xEl�#��H�VxE�2��C��+���s;@���{f�Z�B��}�<����P�F� �(�s�yh���v $s�
.D&��������c+���O�L$�o�gs��am<N���i�c���mI�Z�x������
��P�^o&2L,��8���P���*`����U�R������_��p����"o\J�Ly��(`^P1����$L�"���v�������n��?�S��\�c�����o�NL�,R��������y}{<_r��J�2����6]6�?b��=��q�_��t���yvrT��2�%�K5�:*3k��������'��<��'��.hp�_��&�H��3l����]��$�����b2��b�*+#�8�bfC�Q=��?��O��/��)�,f����":��]�0oE���*w�Y1.��#��;jA���\�H���V�#C��? O�������C�|�(��i�}� ��YRs��c��w	�/c��C���;���:�nZ��s�\4�O��i* 	#�_�^�?`4�f�����$�^1�`�MW�W����Lv/��^�����o@G\I�T�����<I&������g�F�+��� ���I1��w������9�� �V�;����]l%R����]G�+<�Y�-��������ms�"��
5KT�=��M���)��t}���}������ t��R\%6@Fr>%��K��C�k����8��K#����I��-�~�~^�[��P�C{�x�2C=�)�&���o�j����O���������-���������'4�����
<�}����F�'� z.�����{l`z�Jo�&���z���i���}JJ����9bgkB��v6�e�P�75U�}���p��~��7��s�
I�J�7��Iz�i)��/	���f���91�Kr�IxaA�bJ��O:��}�W
��1#�T#	���W�{�D�J���y����b�E�!���F�����,�_�?4cB������",pQ�Bm$/��~�<��;���-.��D�?��cTo��/�q�(X���{0����XO����"���H�6V��8��7��������<��/������B�oQ-8���b.�G��i�n|�L.`�Z��QJR	|��2lm
�5_%3V7���{)|J>��s/�I�Vn�,E��:����������y�v(;�23�x;t�l�xvLF\8^���&�5i���������Q��yD�������>��I<D&%vt��d��g)p��>�O����9��)S"B1�f�2W�]���n�@H*����C�z1��h��A����L'���(8�7��,������_m}�msU�5'�IdL�>8�������#����}������
w2������>�.P�!�]�F�*��w��/�o?<�����]���<l�}�l$�yG
��T���X
���76����L!<KjA��`����'��
�:�v��������r������[�.�F���Vws_����g���c�X��.D��5F������H��0#AGz��2&y�2XOu+�F�xl���}��zN�q/5�Q�d?�>kB��	l���|��6pk��������;��'�VM!?gN�0��c5Wt�g4-|I�BfRX�B>��n��"v�t�n�m����������/��^;���z��i���^���sVaM
�~�U�?G�Cs���1L�9����}��L~�o�1����|��D7��n���?>dbr�E�B
w'r�&�������v������.�<3
�+�V��$����s��R5x�B���|~��v9�qw6���fK�yen>wOC����?�g�Y��r��$����Hbw�j9������J_����u��t��/Q���5G�a�<�����?�V�=:��tGIpW���c<nO�{��y�oa���� dc�;�����=i3<X�eU��!�R�����(��n�Cw�A0U?�`�V"f-aB�j�O�[tfd,�u{����!���F#�V���!��i�S��`����	�r����@.b�QsY�������"i2Z��'X��g�������$m�WT+d��L{���mA�G�N�IT���Q�KI��H��j���R�1���$��ju��h0�]"�)`�`[��u�F����G~l>����x�1y�g�����x����Z�4��G!I1F������3�UDrcn������8ZE��gp��<�_����{�m�Kq
���IJ��k>�����H�[�T��L�x$WW�R\���A�u0�T��c��s��� �}ms�R�u�����EX	�Z���Cw���{�&��$M���Q���_���R���g�+j{l��1����f��D����'<�*��p���X���L�Y�hD����e���+22��E^F3)K������J�R���sY|&
.���$�����{�W��3�e�1�������"#�������P���s���o��r�s�u�Y��#ws��`,Y����h�t0�X��p�����t@ �U����d�����7���]j#�h�w��WO����S�HFI14�>3���[rFAz@4�n��>���=��em'��Wg��d�Q�E�km�1�0��5��~~�����G�i��e��X��%+U�����m���_?���uOc����(���%���K����+6t<���u]�2�*��r0KE��&��R�J��U��8/����@�_����'w��"v��������z�)�&����)���\�k1����]����p����+?}���i�!�y�� ����3O�������p�������k�g�����&t��g3���6���W!Cd��K��^�y�>���C����LB��t�0'�b�g�����Of�����G!y���/����7�M�mjx�/�����N�����#��u*h$���d��@��b�B&���H�~��5]%�yB��j����
�h(�H���-����Wc�����k3(�=Y��-t�9�O��
����s��O���������"�rK�����3����������s�\�-3���]{�����>�}�Qbu��R��� �X�>4F!�����p�ov�s����^1s��B������M������������y��k��������r%�G�\B�#�0[F59RV���L��l�(��a�(��g�o���y�u�57��i�<@�y��iY��d$r"�'���s:W��J��
;Wr��b�k���/�]��O��o!�d�,6|���nr�&��t�������W<v1���J��m��*lA�2x,{�������-N[���?��~A��S�&M�q ����3cV��Oss8�����aw9?���L����=��aH7=��9����}$�<�����O�����oB�8�_���	�,EF$���wV��yC�@���W���H���'���_Zn]�p��`��J���Ftk"14�.K�2C"�����/�]VCa"�u�f����e�uH<vjx	SF�H�+��5U#1�B��~���S}h?�fi��t�Y.�\�al���r&Xc#��Dpx-�L"�o���������}�����}*��������,r}	��������������T�RD��X�����g��~����� ���b���0h3�<a�%��
'2�&��>��a}z�*��,�9C)��l,�5�R�O_���_��F���/�eB�:�.f���Q�EH���>���L�4dZ�3�������]�C�i���S�r}���iO}�pT*�%�sp�TH�9�Ps�����nu}:���9� 	��9�#I1��B����}P<nw��d����07�o%U��zQq���}���Rrn<���|���B��g61,m��r��r>&y�1��yE�Z.:v�=D�}��=�d��3�(:�P��;Wuz���gP��T&�;���������R�8F�E�����|M����xG��@F����8�Rd+�C�e����~438�F��1��Ci��R�Z�0S�}
���\�a������g`��8��Y�2a�P��i?���<��T����XBj4�s����T����>���RXZu��O��r~��	�����k�mnf,�hR�'�H��8���(f�����.�[�U���`,$� ��[E�90#��q��S7���W�{����{���k����������'��H���h�
R���O]{>C(@����>�G����X���S�<U2i/j+�Z��/V���, T2����20�q=�J$���,$�nt0�.`+Q���!=6g�>!k$��kH��huP@{yR���a{�-+orI�4���}�p��8��8`w�����?\�]�~t	�x���iTb1���H!i���!A!��^��*����hs��k������=&��n�xe�I�L� �.��O3�������osMa��1w�u���r��"@9<Z�e���]H<�dMK�|^'d9����D��M�8�������L����y�������fZ�B��R��0]� ���n6gs�d4�����vg�����3����-��q���B��^I�H@	GR�DAN��Vg�X.�z--���Tr=j������������N�Cj�}0l9r�g�+�����v�8���e5��o$Kb*�L�weT}���r������t����R-�x(`�����>K��Q|����R��������V;�=xk
�����w�����`�_�7Y���[��|n�JM����7��E�A����G*�,���������Gr?�������?����v&�u�vL�X'iL&����k�G����^~o��K�n�th��[7��k;�>��=����v\�+7��u�zA%�ax@7m9��<�7g7�k�8�P�H�F�������F2( ��	J�5�����_�23��P�HdO���S�#J�}�RF�S�=d�85H�iS��qM��{p�\73�������������������-�`tyS��J��ac(����!�zS�������AJ�T ege�B\�J�ZKfW�����Fo�_?���s���4,���p>�����RV1�%�Ya��2R$�E�\a����r�C���CQ������Xsix�gP�����K�LD|,N��.��B��K!���W�@@�C�[�K�B+4���I�,I]w��GI���Mf����w8a���z*L��/��30�Ygr�+��N�	rZ�����q��26g/�$w�Kt�q���_��[6D�t<o?g���s�E3��Te'�r�'�`���X>}
F2mAxo�Z��B26���/�gYFO:���=$\SUkjoW��Udg�������g�|�����$r!0w,P�FK0�����D���	(i�k�V,�z@�8�5��i�V�|$�H0��`���]l�N�^k���c���Ay��/�������\��3�
a�������=��:�|�k���O���U��>dQ������y��f(Z�VB'�r�,	���}��ziWL�:Dr�M�*e+����	���zjQt8n���eH��9K&�"A��1j����������pvU'�>��������gr�,_��+e�;A�2`w5���hf�Rzu����&(}�*���'N�>O6�����/& R�&li2��������>���.���~�Q���h*a���$�TZaC)�B����������}|��=��q�Z���m����?'�JS	$y�[�f��_�E�1��s�_�i��=��`���SDt��s��c��������$$��zo,����H���}d1����2��D"��"�Q���tV��<���\,7����Io������
.K�_e@g�gP>���99%Z��������"v����T�{��I�M�}<+@���q������q;f�T�A�;C�`�������O�n{�23�����D�/4�"��������������\7�a�BN�r���`r�@+��S$�@��-�O�U;>7�#�@������%0#+[��S�:�}���4���H5�N]n��I�m����GnO��KOhb�2�d$mB�/2�@wo�R'��=����xny��Y��Y�)�w5�� rE��2$��0��*������*����c�
=<��ZH��%jJ��a�$������j����.m~nLXy#�s��o��Vh�b-�+j.�- �?�hf���-0��S�����f�k��b��r��?/����w�����b/��j	�B���jX�"2�����A�������g@��na�g3W�\x�.6+��5�HG�6T���(��
ln����cug�����0��UM�L����s�F�����_������� br��{�T����������Fl)u)Y�9�s�b��Y*����{ ��z|�>T�J��G����Z�?��>�QH)�KIJi�'Y�Y�&
��&����]nA*���"QJ�4�|~h�����)I��.4��@+����EZ��Wfd�6�@s��dC�+�4	��O���������c�(,�!��*.����K�n^
Pu3y��o�����!+��}1����g"�+�np���356H�/�r�R:/��`j����4%'!�<�+K�J7(��O���0�����"�f2W$h	'����1T	����^�����q6~���(	|p��G�8De��u��w���cjqo�����'�8W��4te�Iz���S}>m�7�x��S�w�����v��?���z�����Yk1�����/�,L>S�"�6�p�������i�Y���1}�g�\O��jc�������p����x�q
�P}j���z'����{��@U��dWZ�p�wm��O��k��'��:&�p����d���Y��l:E2�X�9�Q�p��X��q@�Y1��okF�snl��1j)&�a�@u6���?���������-]??!+y���@��g��t����Q@������|�E�~+�E������[�!��5��5�;�(��1_��n91���(n��xn�9<�q����q�N���<�]�4/�S�G��,N0
4�8s".�J�b��a���YXD�w���>F%�3K���R����~�����@b�{��p�aL5F���\����LTR#a%���5�x�}t�������r��5�V�3�N��!�en��i���%���fhI%�}���-e�yxex���u��2	���%��,��r�w]�-�0�b>�a���{%�a�����X�~����sW��������c�I7V�Zs�d�	�V��V�^����]����(S�2�C}�L��P�er���BF1��M�{�k��2������Mo��m>3"gcO��}��}�[gE)c���H�E~�w��'���	
[	����A�w�_���;������>�m�kB���'/����#����B^@Y�����o'�^fM��1�OV�M�"uU)�y�<z~z���`!�k�N�8��*���+8��������S�^3�7�H�i$Y����]�Q��J�d���"�Y�.}}8������dc�������k��?c*�e�?y�x��p�p�@.���L/�n�����_��������Yr'�]������H�c���Q_@j��G3�2�/���T��uSuG�Jqd��D�Q���U�>��a�8��{���`~��9�
��Q���.<���(����P��A�m��@F��%�W+.��2*�����]}������F��Q�=y��D�+�D5�{���|5�g�ik>�t��PO�X.���
@{���Vjh�������M�Z��#+E��z��L���\�pT"��6t\�����kwms�=�~���X��N!���7��2.��u0�y6��}��7
����T">*�d.������� 3��RF����v�u�oz�=�#��%g���@�}�X�z�z
� ��A�r����ryY�?��&&>�fp�*���p�r�~�����i?g9(n$Uw�g��,�,����_������A��pH���[Y����nh��V�l��[]a�s����F�����r]������
��n&2��XL����T�|1.�:iV����t�~}���~��<RPs�H�N�8��_��(�fC�����b���G*�b�A�Q��gzt�'	~�s�B9�{yO�aR�����H�u����$����:����0�3\�$�Bs���*�VT�QJv�u&����j�e�9��V`����w���V����C�^Z�t��`dj&�0b���=Oh������$��FN�
�����X�&	��b���7�����)����h�i�V�1�������/��E���:"@Td#���p�p�B\3�w_�#L�e�D�i�?��6��C�Y�����r�7�����_<�a�-��\<�9��9���~����������M�N�#��3�JE������l���6&��z���WsX2u���VXo]�>�8���H�5#u��l��o/Q��9��'���������CV��
������TFU5r+���8����O���6�v���F�����*�������r���a�??f�I5�����Is}-�F���q	T��@��3���d� `�7�����`��lwy
'1F3g#�����R��������u���]�)����k2r�'Km�6'	��I'�jMF	��.��/�]�2F+�,od���A������ ��������<��Ns�<���q�P��b��[s���8eK�)n����o���x����ajwEW<�t�t�@S9=MA�k�FE0s)@Q��o`�>v�����qa<#��<.����6�L���@��3l�b��'C�y��'���-��?E�lG��A�,�%��c�`���LT5�nj��HI<��k�����b�f��|L����d���'SS���l}������a�����GQq;9M�^K�9	��i��� �;O��R�@!�����d��B�\���~���g� #��h�[�L�i?�7���U����Z��<*�a^H�f
���C�e1"<�8�[������rw�X�V�p-�Fra1P�
pT�:���#5'���`q����D��.���2�g�-R-���MO.���������R:��������1�(��5QU: �3N�����;(5	j�t8��o��]$���y���8O��T`��j"�k���Q#�����<=�vM����������p{Y�|��1�P�j�(s����~������*v��jQ������J [r-jY�#y9m`N<u�u{>��v�5�b����wf��l"�k�!Ero�����M�7����.�uy�*�\�T!�n�^��'�ZW�5� ���Q�A���3����;��t�=���^����}���L�5�$�����$��N�����T*���d]�*�H)�����y���g�B�[�1jM�J�T�����!��Am��w_�^1_�P0�!�l��qP5
���p?I{�����s�"��qP�)$����0�{j�S��}����Po�������)���"=�)��.\�iD�q��|*�MF/��������>����p<���9ey�	�Bk?k�R�V@B��8yCqZN���W�iOW��&J�Bw��S��oB�3����x1���x<��������^62����<�R~�����X���<���������������d	�5�� 4��������=��v�oGW�������\F��f��k�{:��#��?��E��ed �>�I����G�|�����jsq��+�9>��� ������X\l1&�n��>�^���,��Rc�	���"��e�0�soi���B�q{n���RX�q�p���=aq"v�����r}�!�l5��~�l�=>�Sm?Cg���p����������O6��&��
O����&����'�J�V\�<-� ����o�j7���01�w�q�t�s�}��n8��w��X�"�w�	5Wr8]�2^�xf\�T�����
Y<��0�Y����z�~9�Gc:F�}��H��)s�k�Q
%H�m1�D�m�Z!��C���dG
!�Un�j�u��"?H#h��
�=x�`����Ji.w�
����3+;D��?���>�/4�}�P9�
�F���^h�b\���\���I���00�H)SJ�
���I�� ���g2����R�����E0p�v��d�q���*M�r�%A�Y����������O�4�OM��T+��(�C!e�l������>���a�L�K�p�a!>iz(M��I��{F+u0o*��y�w��8~�q�0�r�����|���T��c�����|�sS�������	i��R�0��D��������e0T������$OOC��'$�i��"�l��A�A�|���9`	w�i���t���`�OQ�Mi)��������1�M4O�R%E��VV!������r�c�^��Pf2�@0z�<.X�?"���-�������;���>Z6���
8a,�9����^������.���0�2�a��(�0�niw��zT��R�H'��k	zj��6�0�gI����v���`W[��w���p���|>�F}}��]�B%�!�-?Lo+�� %+Rh�����G)g���;:���('��t!'Oy�����������k���k/4�����r��^�����V0���W*�>��J�R�U7�e�N
 ��������|�=�k��	y��f;������}�>�tI�Di�)�v3�����@�����c]W��!�I��k1��e��&}�cW��|�A�o���������@�#9m�]���6��d�#��{'�@�3j�V��B�����������5^aFf�&���P"C���PQr�1�k!�ubV$<�w?��( ���	)(_�k|�(��{VP9�}�����>��`�3Z�\������mR�x
{C�#t�����w�������7yY���w�G����2���,��HV�teV����W�?f�<[Z������&���[z�,��;��u��lw���$��c�d#a���N��l
�4.�w�#�X�m��&������2G�;xHq27����)T�3�,�os�������.��Z���\r��
�����x��p�U�i}e��j���W"v�
	����H,07��Tu������Ho���]nz��h6��������?R\igR�����������\������R�vj\�HJ!5i�608�2�b$�����!x}j�}*�]�)�

�>J�z5x�ce��R���]��q������P��?~����M�Lj��JO!����C�|4s��~����������|��7��!SioY4�'Y�y���;�l0S���5��>n��u>�19����:~��C�\Y�����H��`W@��8lo%r�V������p���\w���)��������P{�-(�%�l`f���^��yu��X����4_!i&?w�u%Ez6
4��b����hM+:O@�Q?Q�����1����uHf�`�	"���|�.s�W�b$��4Cn��=��#���T)mf��kv����7�}�=�6e�=���
���\W���q��������3���z�D��u���@�|�
Q��^�7�9
!�8[%{��H���m/N�C���\����]��	]o�~{~�������7����\y����sz�E��&*��'��G)��w�_O^v���eg��~��d��<�&=��\32�3��u�J���\�����`���s9,{L����$�Hd��r/'/&)���H����n����
����0
E��(jB��aR�H�:3��9�z��:a|L;�I�g����R�����u����D6���Xo�����U��!��p��3		\T�W�M��O��(���a{~���=������_��� ����,dD*����AjT� �t�0n��?6��>>���#�]�yoM�#�1��%!���9U���T�R��dF��aA�o8�de���[��x:����* (B��(������)��18;��<0��4]-?@~�@��&��pY�,������8������3���Y9�L�*6��<� T�=B4/��r����k�Z:���b�Q/7�M�b���
�:�[�j���R���>������~l��e�
�m��A�Bx�o�H2#w�^nO�+��z�:7����� ���BW���"Wz�������5���=��;>v�S�u(:&gg�7 QNK�EW��t1~����u�����)4�n3auA\�����������~�-��\���:;�6�b�WS�`'Qp��}���������u�j�� rP��Q"OR�d1I��D��G7���?RW�f�%�S��D���P��-
V�J��P��I��j�e0m����c��P�`XV�U����(�,�c�{7XF�����K�Lq�u�H���FC�-/�N�|9������5FY-������^���4��K�_�Be��������V���k�w5����B�w�1�,����qt�6#����t�zKp����e�%,����v�u�N�$l��)�l?u7�E����{Q�3I�;OUbz��bD�u}��]{��������py������L�E)�u���E�@�-'�X�wk@0?f-�(�&�l6�gbR�q�g����������G������%E�M!��_��.#lV�H���'��n7,Z�>HQL_�����z�����~2���t�����u��w'��T��@�B^O����'���fj4#][b�4����>~�����4����\�=q{����Nb���\NpRkB�J���������s1e�aO6E�7EX��WB�����_�kp����R����-�bT(nmF�h ~��x�&'�fv��;O��*Iw���2�{����n������vMc�����W����L�
����R�q]�?�L��D�)��K�vv44���(S�����G�iF$���<�$�f����%6N����N��%�i]F^������n��4b�%����	� ��	{����&w�$Vw����)��>l�������!�4��%�P{�����GV���4.��-FQ1y0��z%z���f�E)���tF9H8($�������>�K����~�v�v�g��o4L��e��(��3������E���dQ�����rJ1�/��bZm��|�%���R�"(�|������"�k��MB9�-��������_J����r�k&��jM����`Y�M�2�sP��G=@��ImE���!yN��b�����5+�]s��	g�/A�^'7�fI������n?t�����5Ev�0��9�fvXI�X����|�2�q�l�7b�{��$�s�9�@.uH��������V�L
����������e�7��Y.2�ok�[]�G=�
����H���
�~��g�*2�� �W��Ud	�,����t�a���y���T�%���L�D��tG��_���@Q/��t��7M�������}���E,�[gs8���r��C�pZ��x9��S��3�	���)�<
u���%�,lD��9�l�R�����O�{7��?����YFI"Z��L�tw�-�~��
q|����I��F����ab�(�5u�y�"�K��J_�����m�������+�=3[Y��`>����W���L������q�6�<�#��[G�m�����4��z�
R���Ar��><��
�<���
�2�d����J�������2B�>��!�������������
�B/���R�)���n���V�������g92�Q����XV��#G���g�B�3\
,�Q�`r�t���H�`X��C���7������u{�m�9g(�z-k����P[�**&:��>�7f�|b�1[�$fn"�g.:3���i��-��_����6���n���ikC�!���>x
�$T"�+&D�4$����,f^b�G{0�� ���h�G����`�$�*?��S���n����:>���n�ev�p��[L�k�Mn��P�+w���jP\�����l�������A7�~jAg������cL7�t��D�"
$+e3���n��}�l�WP=�A��9@�����<7��������~��� �O��0y)�J�>OEhH��R�L��(��+�	~.���~�����0��v�#9/�1i$$��'�x�Pm�R�V��"����g���:n��u���9�H��G��LZ
t������}�!�	<�9��,@���7��-/��m�t�����7���3$�2�!(��f�Z�b�^E��Yk��{g[~��X�(&>w���q�s�}w	5�c��w�������t����US�u�kht)d�����|���L�`"8���TU70j��+�`��K�^���(�s��$E���y�'��7_�B�X��n���	�O�^Iw0�;��R%?K�*�UB��������t��|���Z�K�pIO�7�����?�����=r��!����y��X�(UE)����
��C���|h.�>+(�*������f��U�^���]�&��X?���/�������5B:v�%����4Kf�R#c�$���%��o�����&�k?���n���_��?�k/��dc���8��k�i(��(k�$F����X��:�U�y��D(������+�\��K�������&�k�N�f
�u�z����P�h5��9T��S,���$#K��� ���@��S:���-��Yq~O������h���3���!��u�%��I������;�vW�x�>�s�`�E����,��5�g`?1�$�������JO�t�!���)�S��Mu�]8�e������i�]�=�vn���bD��1�:S��86��9ze>u����� �n���'9��r�&.���/+���O�f��G��F���$��{�'d�+�J�T�F��}3���w}{�����������(�M
�iO�Z_�
��V�D������sV?����@��W%�)e��qs��}�?�hMC�B�� 5�
�kj-����[��W�q:�S�F+�bL���O����"�w/�mS$��0;Jdo�		b����_cVR�<��e�C��x���?y��js��M� �F�6�[.sth���
P��+����A�����-��x)��]������i����d����w*�+{}����_��e���!���+��*=��l$^��Y�9�w��\������������`,��i���\�!+0!�����%,:��P��4�:C��~X�bH����M)���-��a563.H����%q����-�����9�N2��z(��[�
��E������v}<���������[�f�5F�d���*���9�^\�����p
�u����p L[>����L-�Np���������2B)�h�,r�9��f8��`te���L,�]�y����������7�G'q%"A�"�E$+�%����
4���>����C������s:Q�l
a���M��L����b�����w��}pt�`^0g[�/�D:2�d���P 
�0���(-e���s
��]��g���'�%+m��0M7���� 6��#�]�0}-�6�M���������G�u�%����l/���gE)���h���y0�y�w�R��>:IW�n'����������=.��|]p(��U���[����;B��'{8����x{~�������K>�V��*um��!����:�����[�c��������`�tv}��K{i��13�����f��]%R`�,�<�a
�<�������'Tv9�5�ViD�����e����6������;��|�����Wzu^��M����_H=3��h�GF��h��Od�~�C��=�25������n�`U@0e�R�����ezFhf���2�!�s	��n���-����]�I)���8���~�'���\@+<AW��[��D&x����h����*��t1��Ss���z�G08&�A�{�|7X%p�U��R�M��9��|���xZ��I&��tNGH�,1������SD)t�����u
-��{���
E���t��iN+�������|�����������Qr.#��Q���������r�����E<���Jt��Y��j��Q*u�t�Wm9��<v��\Q��O���<]�#��� I)e���������#��e���Q��I��W������6���s�=��/������>�:�g��������#B�{g	K���9Z]%�&���b$\O�2������H��/�����9��=8�\�v��&q�3��g��d
�Z�*�!���DDK�q��
���"����EZ����&_?�/��r�����q��,��r[V�J��v��/���;���>|�Y�H5���xH�r��0i�?�o�v���o�l
I�z�F�|r���U y�t��x����9D�eB����g[Y��v�fp^��5��������������}�]�+��F��^R����49S�&t0VS�V�D~�#����p����elS�� h!�X�E��"�q������h�*��5H�o]T'������tM�;�%��������%�dJ[
����%a9�P4�4���M���M<#%�C����&������/2,D�mvREG��x�>~QL$����m������b�J�c.���u��`ZWt��c'�o��h.���6���?�+�4od��%��b��=��{�pF�NC�s~��O���������a
���������$3�\�����������?�����m��]3�����V%��!�HmZ��R�
���~�]RJ�v��w�t����s���LR��U��c��6tEi)F����	�8w�Rn]��dq�K)Cg����GfW�5�l��q���nhlw�iqE��A����),�&v%
��'�|�e?������><�hO)Qc���3	^�H�%Q*X���9� %$8�~2�Av)��B	#�
h(��
��Q����
W��%deE)nO��fW{~`�"�>�j3���q��u��<WO�%�����u?G�D���}��l~�7YW��"Kr�I�Rcf_�����\������C)��M�
\+F&��/�mq����B&�w��Z?���F��j�U#��U^����hw�M1 ����������3_�|e����l��7����?FS}�mg�E����ow�O�����B�^�����N��`N��a���C��DF�����0HX
������*�x���Y����@ ���2u�%���(�� C)`J|M�d3���R
�dP��I!#�vbEu)�0%���k�bfw*���{-�Q2$P�p�G�#�o������7e:�Y��cdl,:��Q��?����Yl-)����>m_�*��i\��)ej|���������3�<�����O4�"�.�\{^q��2���R$��i��=����.�>�SD���Ze�q ����������=8����Gw�A���N�/��(���u�[U�a�TW��!6��e-vLX��[So����Hb%�U�J^��rF�����=�*��2FzLA���^ �$!>�����f�����#�m���ug����,���;�*&��2+<Q<�l`�)� �q�i��r`Qk���z��J���>���H!Ht�Y�cv7���������"��l�f��8�FH0]�el��gRn"��Z��g�^�����K��U���)�.�e�K�+����pov10�����x)�$�������Kun
�N��:�[G�eB�����1/�n���6�5a�~�>ny��cpiv-���dH�+YL)z�=�aW��H�S}teTZ����6m0����3em�_���Mw8Bp�� �b��\���_�w�g����f����y���~����YI}/J"����.+c+
����g��h6���~���/��SNu�$���(NO�#'�����������s^j��g���p������<�����qJ����1y9��C;&������y9���qt�6
��d���u��s�O�����l��}�b3�v�:�i9Y:��x�nR���9����a{����������u�#������'
��X����t������#P�v8�A[Y�&L,��oU�l�ND��� 4*H���mN�][$��lO����_�}.zB��
�� ����$R�
Xnl�
X�yO�+����$�m�\������H(���hu}���9�=���4|��n=)C�$A�np���b���O��<��u!������:H��0+]L�(t��:�����������N�4��O{M�t����X'{��XSJ�����������r��-8�0�)=.�y��	'6@��Jx���6�~?���o�\��Ow��5/�3�9�1�H�s����3��YJ����07��K ������>���Sf�����I�9���X �!5V�l��5�Fe!r~�a������\���I���\_�2j������*b�D�t�1R*�XU�T�*�����p��	.����a�	��e�B;:z�7l
�Uved)��S�!F	`��(ie4D��tUL�B���u��pa��CNk��U���u4:��M+�B"����Q����c�-�o����2Y"�G�UFb��/�)�}(\s:�s�F"0������`<_�%iq���^�b��"�����O�n�,���E#fo�����Bh���WT���v�n����'eya�f���&HsQX��b�
@����y\]����,��� ��1����:����e���:�\������,��'c��L
r���$�|����������c&�X��y(@�/\�T��ztYL(@]q�y|i�-z�����)����+�K��,�����PE��U���d[�F���l���@3��Sm�K��,1��C���ss�����L%Q��=B1����wW����3��:��I3��/p|"��4IS���6�8#zD6�3w}����yd��
����4�t��E�p&���e�O��RJ����x���o�m78�C�jr���G��H���xp:m��^������%���]J�M�q�\.�1�u���LX����������n����qs��M4����{M�Fs�XcW�7�hn���ok��{�f1�2csV��������r�$�gd5x������cw����S�m������4���E��n�t����0�����������2�<T<���9D�������c�����gH?����|��Kn�#;�B�5@"���o������ZS:"9I�����	,B�
������?���C��x|���n
2>�X��QI�4��U�u���w!W��B$����vPzB����S�.^v+C��*�lI%��^*�z��*_L s�D#;?�O��J��^���%���f���Wc���m�^v:UF�P�C���3��L�zT�Z�7�kk���L��hny95��f��I��V�[W���]����d\�o�%�/�$-l�Hr*�K~����6���r������q=k8U�Y�-�^��tZiw�8�W�PRJ}mqv�g9.=t�e)X�";O�2��Y���7�W�m��x���X�,b�a���I__C��h��\w_��0UNA���.H��7�(E,]��v����ep�t�������G�WDr��YU��9�-eC�u����ju���{���b�W��������)�G5%����G���������U�����m%��)3�&��x�z���N��F��B-���'w���8�nQf�D��`�8�tq�'n��@�oD\�������bn���*��������h�nX�M��,
����	�����p�����~�6<`��<��/��r�-=Z��p���J�d�%
��
.�4$	�#r�Jzt�����|��iS�H�YrZ�Cd�����w�6�����S�q����p��E���!�kc+VL�S0���M8sc�8�NF��T������.�k|�O2+QL�q�z����.�����H���J���4��9
O$����m�M��/�E(h'���$-x�_ �+��j�$B�_����?�^r��_O��B/����3���b�v�tm��p�����lV�c�
g2�"_���TV %�=a#q��-�r���*N��E�BV��������@o���A(�?�Hl�i��F��f�A:������Rd�7M�<(.��D�o2���J��J �U��d���Kfs�=����[ qKZ
����3�g�7� <������w����mP@���e&�:��,$�H�����x�2�I=.�[tg�u��e�XB��IX�v����!A��=�� ��1�K)��>�"����2v�FCi�6P���#1s�B�������s���^����!�CW`������a�@E��8v���+�����&B�O��aGk��qHf��/�[�H��G��O��}����<>��h�
��	�R$?M�+�*�c�Z���X���23��5���IA)�yQ�R����e���!�8G�B����LJ'H��8��+f����*#��9ZR2RZ�y��r�e�-�K�n���w����y�I����T4����e�F�C��1!��,�+��/�r��,$������G���7�!���cte���_����^��M�MHRn������L�����n��g���9�=��*������W8��K���IJ!�����[7J��)����[==N�,��-r_'��
b�e1�D�����uc���������5��}�|a���,���R��t�(������|��K���
�
!dZ�:3�"!	�*E	0�e�����}����u�\\�
0�e�N	�K�ri��@�#�T,�������B
Y����{/��|�?����d���M���-)��9N��dCD�$w�����!�k]��Y��
�Y�r���)�9�5K���&��}f�����}�W?��"�l�$�Y�O���(����v�i���e�����W���V���Pa`�-��$�P������$�v7]�3EG���������X��{����L����+L������[n$;MYQH�F����0��hn}�o�Cg`87X���\9�����~������as5��sdq�!�|Z��R��C�����XQR
�
�_������CddR7��<;�o��z�W��s��=z���y��Qq�'��v��I'���Q)������������rB�8������\��eqU���?�6G����!F[r� 9	R(�
"���g27T�3�$�Q9����W�M<��"k��>Y�%Or�������Z��RmD�r�t��E��$�C�
��{�h�((j�����������tT(����J�F�MV����}��|j�x9�F�V��)��bG��H!���^yd�U2#������<}���x�.���9�z�E�5��*�0��������vX���D&�$zI*�%b��^Y���2,SQ�2I
��/�-���]���g����3�"t?$W��e��Z�\�������YGH�����\�uM$����.�l����'@{��\��9(�f�$��O�|�l17�H��U2Hs�8gH�K����+��[u]�~���$I)�������r�Ag?�(+�']�;���B�����(H��D��Qb���
���������t�?o�����D�Ho\e�o��,�lq������;���#=��/~	3�5���K���_�}������SD�;�}�F!�
�;�0��V7���n�j�zs���+��3I+�~G���*�^��t���D���P7����d���N�$�$�^���c<��~�b�E��)jNOy�[O}7t��V�JK$`��	��?K~��@35=Sub%$"�w��@��A��S�kV����8p0e����Ew��h$��i���]��cF��C{��MnD��F)Su`�I��.�O�ct��_v@���9�������>���Im�^�������6��[J
�J�.��m�^������H�����������0+.�!��la��%�	ICQ�fK�
���[@�y����
��B�ri��!Q
RljX��*`y��>��[�C5	
.O�
�P�9+���k�p�
��]��u����	Up�c&�&de���~�D��$~-�Q��7���;�����# =��Z�b$=�#�&'cL��$�E�[HC<V����3�O��m���l�F��I���
�d�&�<
&x�J\������Qinq��(m�����>��?"���}��5"�_�����L�{�{P���$XQs�o���;x�K%��r�/����W2���IS�����+����_	����]V;o��!D��:[;O��i�j0Ez�X+�TS]��}������o�����F������m%%2YOQ]���{����+�����]���,�"#-��R�F$!H�JT���������kt���������p���s�	���\"�}p*	�?�u�*t]wL�Jrl�x�����d/d*
}%�9��0��s��(�T���ax��P��86W�l1���\�J����(_�:�%5�<������H*�$"0��j��;���9�%b��!�
�L�p�\/�.��1Mz\�����tC����Bn���`��Ji�&B�<�BC����L�^K/'��B��dA
U�[��^��O��/
>�yvk�>3�I��,h����G�z�������<]�A��"a���8!/��7���3~�tFW"Jr��.�N�^�7o[������&kkn
�
���)�5���u����u)�y�X�������k
�����"�+�$���w�U)��`��M..�5`r���V9���^���������S����&�Y3Ot�r0�{�]��F�_�vk��. ���b��*��vH��A2D|O���w��X������V��}����������z�~������v�|>����(q+�����^2�br�S����Y��r��`��51������~l�f�������!�y!ilvL"B��+�%�(�N�p���@�y8O����Ic���K!\���)�4%+�KQ>���;���mz�?jz��	�}��l"P
��{����e]s�b,s�
��J���@�
#�D?�/������I|�3�HM����n���]��gB1��y�TF����sz6��-5E��(��B���WA�������2�������RW�� f%u)}�+�p����m������K��T�����u�B})`�s�5~���1�9���M�b]2a�d��]�����'|�]~=��f��7P�-�G�nR���h{R��[O�������/���ZQ���5^4�V���+��r�x��������L/PK/������x�"R�7���*R)�)�{m1h�i�j��s�4�ms�=�J�*j�D���I����f�j��y����8Qr?�D�z=��y�^��M,h�ZJ��}�����E��be��s8���T���o�]L�J��&rL�(@m�9�����&/g0�^�z&���N�4}����Wf�Gh�t�T�7M����~���w}O������
Z��i<o#So���W�}����MK�"g�|���:R�L���D�I�-��!�����l�������r��T���o��RV�Y"Q��yU�!n��O��p
��$����a�hl��n\�Ro����2	��L�`<q�3��r3�b��Wd��p��)a�������Y�ob}<��Sw<����w��[����}��M���hR}���,�y��k��9�&�(Msi#X��
��q��\A�c��E��4�98I~s�6x�j�<�R��u�|������x)i��>t��?v�@�1�p:Z.\w�o4���@),`�|�A�G�.�v�X_�]��8��]�P�b�x~�d��]��]{KC+?�l��l���i����1�05��c�x�-'����<B/�a�\�W�7*L������@qe��\+������C/����=c��d�^&���NK��%���b���Z�]��c���._\�R�#����q��2�Y�b��?�G����E�Q��o�B�0,��+H�F	H����������`ff-Q�HG�3�iRx1�oG�d+]�YI�%+#W\F���n8U$PX)��_�O���|ny��O��NH*�_���z%�_�����K���6e�$S7�c'��0�p��0]�R,��|�A��k�j"��e���{s7���z�������~����M�[~<.�?� %>��)�r����D�TI�JS,��B4�����x_�:�Rh� ����\�	�������������^���F���2�3�sw���aSx'y)O���Ja���q.�fxR>��_���+�q��v��/��/�q���aq6��,#�kNDd�I���$u��u��-e��G��k;N��by-Snx�[�2�|-�]#����Z�y�1�@�0�s$q���FSX�i��u
�����H�1�n����L/�#mdr+�\��t$���
�y�zs:��RA���<lyfS���%eH����+fK^_sx��k;k�.�ff]M��$�1�G.
�idL��$0
�~�I��z���f��;��
L��r�I�������zPF�)0,F�$X�Mu�|�D�r|I�J�������@�\�����:D����q�\��P��Z�^���.Q�?�6�%�@���6����EE)��f���5g��������J�	lV6�s�R��s�DJd���n7V���	����@�����'��s�z�r.��a���E���G�H��\A��?��;�?�\8�1�
��������~�HNH�S/`�J ��?�Q����6�iX��S\+D���WL�9~nj��d.<,%�������nh�}���`�=�v���x3��Zk<1�_Jw��E*#��5*��|�-o!�b��v'2 ���L��S�;�T7��U������L`�� u�ZZZ��}���T
b����5���V�{�����]�/������uMP���n��!��e_�P[o���*����������-"QB�^�B�Ra��2I[7Ia��^�>�`��*�r��������<��
���<��� �6nHP�`���q��mM�q�OSB��k�P5�h4R�D��9l�nS��y���/��P����R����G�I)K�8��VH�?U�*���/�]^�Y
�Z\�qb��NOI�P�9rK��3^���M�$�l2����x;m�l:�����$�!K������Cm������LJ����4��j�������jH��BXm}��J�R(����X�D��d�	�V��>��� ���w���|�t���O����a<}����>g�|��������E�E������x4������ww�����H��Ws~�)�F7aZ��[��D��?OW������������Y�,�����k���p�1��A���9=b,�|��)���QH����q�M{���Cb����7�)��4�?���;�g�;/w�C�Gi�Td��<Iq�V|h+��%������t��w�Ygj�P%���
���^+����'#%	t}��>���C�A.�@�G�Ya���������}{�o�'{X�o�t���}e�PII�f�4/��jjVT��"��������1�
J�n&�)�A+����I��F��.i�=����������a�/(��Q����@O�sT�E��+8L
?����s�3�ijwa�<J��J�R��W����*���E����������(+��2*;WJF9�`	�"���7I*��O%	E��@;�N9�{��gH���f7��4'c��/���TK���`�$%�vV����?�3<��I}��8l���1�~�O�E~T��jU v0j�d���i���,d�
	����kUT�o�����9�_�2��E���$�`�(�S��6@��>�k�]�i�_i�r�h�D���,��n9+S���g�@^�����R�������j��W!�U�OR+SHe'��������{,��v1���?�+��nw-���R�r>F����0�`���A���n}9������]y�����K�qm�B�U��%����%3�b��?36��,�
�	,&%J��G���$�f���c�g�#���n��3���PdZ$��L1����3����fb���C�W��_��pZ���w����"������$�����,�TX�`�4��&{s�l�,,�Js������,Z��JL�:��V��3�V�P�j���o#p��*�i����! ��6y.WV�Bb����T���������LT��',�t�3F���>sbCtn,`pP�2����+g:2v���*�mjMR2>�	J�������R����	�`���	���p��c��:	�������������9]g��9����y���#��L{���mp�%���}����]��JXP��G
7��(k����mM��X.�&_|OG;�lr�d��s�w�*/�8���������������`�Q[��
�G�q{!����/;u}�5��<������%gm�����^&��c�C ��*N"�$���\�I�g�b}K �����?l�z{xj�m���i��t�O$+�'lO3U$�3�,��t����)�X~��/����^Ed�����9�hA�������bM)D"W��.��<�7us8��?<|��t2��F`���t�b�>������z������W��\���������aS��`VG�F27-�o���n�d�M�
O�Q�$�+m��go�}���S:�Z0���z�	*�vS$���U���w���=�9�]6�Ms;��8>+q����LF�Tv����l69�D[[$v��C�C`G�2�3���H$����+T��F#��Q^]_y������W����s�!�����~_Q[������sE���"���g�LY�� T6��Kq�Y������,��'>�������X��hf��_�v�,�\�i)`f��W	���i���}VN�e!z&�S����9�C98��T�{%�������SM�	�����+���S-�	L�f�q
r���v{�e�
:����\O��?M�"��N�|K"yp�,us����]s87�Z2��>D��(���=��L����/��(���e
�P��J$6<]�?���3h���c�hYP��@>���\�A8����QN_�5e�r����8��A����l���/j_���!BQKk|��*����x�F/�d�{9��6�����}
JY~"%aQ��BZ����I7��#7��\?��=�]����������������w�����\�k��O��m�f�����$st]�!X�M�����}��C����������ow�%��kY�����wW��s���s�I-7f$����$*�PPo���t���qw�F\���	����1�t�iL��d���s?�%����M��
���>�M������������1�BT��(*`����������G�r�s����$�UUJ"�/t'��������t�,R��Z7�(��*x�������������H��'	������b��}{r�a{~B���X����
��������9S����X����n���2"E�� H�qk[Oe5����f���9�%&�bt���L?2�9��l��`����������<@9�3F�rk�����I�H���)���Z h{�C�m��y���.�>o�������.��`a��<�a�Y�}���R>�D?�����vBbD��@�rC�/Q��I��r_i Z#Rz�����\"�'�6�H�������l���4]����������Ef�#!����]�:���b�S�$�����~��YS�2��O����S��d:hKn���&B�����!U�8j������������<>��w�p�/4���j}�������Av=�b�WR)*�Z�L���:���a������!��M=�����K������[(le
�����������%{��O���R���)R������~��(�� ��!���u�&����a�$-C�~���JcRR_'�n}����z���zt(��U�-��	���P:���W���
�U:z5�v����*:8
D���(�����!\�L1�hw<��n%��������P������jt�d�IX����g�)�9Kr@4�3���6��N�\��)����m<��}��a:#���.z9&�#x�\=v��^L����nIs�������e�p)����*J�`�p����+e�B{�b1�]*
����{��vg���a"����~��s'\c"��d3	����X�(��]��y���In�O��������37P����A*�P���i����.i���Yd=���.)!!�W�-�����������`�`qb�3f�vK�SP
������N��Q�iq�%�cA�Y��t�Q���gr����^�����A;1'^�6�`\�����2$i�L���\?����~j7E�9�5�B�DGH4��l����H1���m����4���p9�+W"6�(C�2rjm��G	�Mp�q�cnd����2w�������>��/���JJ,��HX�|�7)�N��X4��De-Vq�V��h�����n�����v�v����}�8��Q����U����B
g\;�Y�b0���.������6��A�!,2�`�6G�}���Ft���(��d��N���N�"��o������;�����b�$�`��qF���
i�6w�-Ez�1���o��u��w���O�-X����W
o�a�����N#�:)1�-2A�_wZ�Axs������d���\T�<z	(�����=l��v���Bd`o6����7I*����u1����
d�w��]ww��vS�.�n�o�!��s_Y4{\�Z�����m��������n2��x jzN���������t������:�5-+���?v���p����tT/14,Wi
�"��L���������a��M�d��y��zQp�]��>����`ly�tu6��f�����_]p�v��l�����������cro����o��9?��������������'D��l�)SZ��
���,Q,6'`�t��wV
6w�:x�v�8���#2��EsW�8vl�k��v��+-;������/Y���L�Y2� y_]�~�P��j�n��f�9����q}(����^W������cx)�'������C��>�+�����1��M� D�"3�1R������/�~�'���`;IA#��W���,���:J!V���$���w�s�"�������r�#��r���u�BP��^_�����C���a�5�H��3I���p����R���s�bf�F����z�D���p�������j^��(F�!J�0$�����S$8G�4����,�@����]���<#����Y'�3��=�~����N	����:J�����Xn-��_����q���c����6@[���'B��P}E+u}g(q�k�$h��!rd�H��{��(5g&�G�CJ������D�Q��se�(�3O7�����3b��keu-	�����?<�qt�Zdo���~/T���F��(�X�j:�_��\��k\:��?=z������"��'cI�]��6o�+k��e���������������_����Q�7��J#X�%�V�T�\v��)!C�}_`f$�Tc$F�ley-�}��,XC���������y=<i���Z�� 2/����'m���N����H[T������eT�Y������y��I���E������P��O�r
�Y=���Hh�d��6�n�6�D����g��D�C��=��7st<ZA�
L^�I
�HF��L]��v����P]�%�UdaE��t��m�H���g�jV6���4"�:��=7�j l�6�x[��*�pn�����D,�����^H���F�EkV�E�-��z)���,�
":Jk/h�S_Z_s��Z>Ep�?�����-�K8(��������g���&L�@�a�^�J����7
��������f�����{��|>y�F�����b6iK�*���z�$p�w/�6K@%i��<�����S�l�a���&m����?��7�o������{U(j���/;���+?�OCS�� �]����K�j���`<��*�`V�\��6�����Q~�K��s�V��T���\����]d��Jv��}�X����.��!
+0-+e��/����h���;�!,�"�"*[��(�L��K}��.���D\�!�L� �����
�j	�s��������S��8>/�E�"+�Y�Uz�HZ�����q��x8�#��������q��n����>�!�"��2f�����^������H&�,?��ew���
�-���aDTL���,���j�2������/�!_���n�;x,����3���u9���D��B{��uX������

�;�I:[�����H��s�9����nzQ�R���� /C�5%m����z_�����rw�O���;�>��fYy��fGNE&X7�DV���S�e+r>�0S�63����7����7$!�f���#2�P��h�,���j�������s����j�\��4�6��9Q��3�`���r������2��ii$��Pw�6����Q,?��b�"B��1���N�G�%s�f�aC��N�������E�=������/�"Z�]
r[!��yZ>�
�#�m��Ou
M��9fM��)��Y����Q�	%���{��Q�Q�[c(��OJ��{��w:����P���� c~�����5
�O�����_\��n7��&[�Kd��<=9����������%����?��/�������t���Ly��rU�;:<'���U�t>r~2cu:*
v�I[j�+[�!G"���`��.�v���u��I�qF���iUu�K�9�e���?n����Wfm���$���4�C�j���_���rCw��?����D��\KL�#X�����J����94����T-��7�dk�T@)�z>f�c� ��q)��2e��{�=`[w<m^�@��N1��JH���R$�Ke�����g�j�����ax���M�7�8
#15Tom Lane
tgl@sss.pgh.pa.us
In reply to: Aleksander Alekseev (#13)
Re: Patch: ResourceOwner optimization for tables with many partitions

Aleksander Alekseev <a.alekseev@postgrespro.ru> writes:

Oops, wrong patches - here are correct ones.

This is certainly not doing what I had in mind for communication
between ResourceOwnerGetAny and ResourceOwnerRelease, because the
latter pays zero attention to "lastidx", but instead does a blind
hash search regardless.

In general, I'm suspicious of the impact of this patch on "normal"
cases with not-very-large resource arrays. It might be hard to
measure that because in such cases resowner.c is not a bottleneck;
but that doesn't mean I want to give up performance in cases that
perform well today. You could probably set up a test harness that
exercises ResourceOwnerAdd/Release/etc in isolation and get good
timings for them that way, to confirm or disprove whether there's
a performance loss for small arrays.

I still suspect it would be advantageous to have two different operating
modes depending on the size of an individual resource array, and not
bother with hashing until you got to more than a couple dozen entries.
Given that you're rehashing anyway when you enlarge the array, this
wouldn't cost anything except a few more lines of code, ISTM --- and
you already have a net code savings because of the refactoring.

Also, there are defined ways to convert between Datum format and
other formats (DatumGetPointer() etc). This code isn't using 'em.

regards, tom lane

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

#16Aleksander Alekseev
a.alekseev@postgrespro.ru
In reply to: Tom Lane (#15)
3 attachment(s)
Re: Patch: ResourceOwner optimization for tables with many partitions

Hello, Tom

Also, there are defined ways to convert between Datum format and
other formats (DatumGetPointer() etc). This code isn't using 'em.

Fixed. But I was not sure how to deal with File and Buffer types since
they are ints (see fd.h and buf.h) and there is no DatumGetInt macro,
only DatumGetInt32/Int64. I don't know if there is a good reason for
this. So for now I just added these definitions right into resowner.c:

```
/* Convert File to Datum */
#define FileGetDatum(file) ((Datum)(file))

/* Convert Datum to File */
#define DatumGetFile(datum)((File)(datum))

/* Convert Buffer to Datum */
#define BufferGetDatum(buffer)((Datum)(buffer))

/* Convert Datum to Buffer */
#define DatumGetBuffer(datum)((Buffer)(datum))
```

... to make all code look similar and all intentions --- clear.

I have a feeling you could suggest a better solution :)

This is certainly not doing what I had in mind for communication
between ResourceOwnerGetAny and ResourceOwnerRelease, because the
latter pays zero attention to "lastidx", but instead does a blind
hash search regardless.

In general, I'm suspicious of the impact of this patch on "normal"
cases with not-very-large resource arrays. It might be hard to
measure that because in such cases resowner.c is not a bottleneck;
but that doesn't mean I want to give up performance in cases that
perform well today. You could probably set up a test harness that
exercises ResourceOwnerAdd/Release/etc in isolation and get good
timings for them that way, to confirm or disprove whether there's
a performance loss for small arrays.

I still suspect it would be advantageous to have two different
operating modes depending on the size of an individual resource
array, and not bother with hashing until you got to more than a
couple dozen entries. Given that you're rehashing anyway when you
enlarge the array, this wouldn't cost anything except a few more
lines of code, ISTM --- and you already have a net code savings
because of the refactoring.

To be honest I don't have much faith in such micro benchmarks. They
don't consider how code will be executed under real load. Therefore any
results of such a benchmark wouldn't give us a clear answer which
solution is preferable. Besides different users can execute the same
code in different fashion.

I compared two implementations - "always use hashing" (step2a.path) and
"use hashing only for large arrays" (step2b.path). Both patches give
the same performance according to benchmark I described in a first
message of this thread.

Since none of these patches is better in respect of problem I'm trying
to solve I suggest to use step2b.path. It seems to be a good idea not to
use hashing for searching in small arrays. Besides in this case
behaviour of PostgreSQL will be changed less after applying a patch.
Users will probably appreciate this.

Best regards,
Aleksander

Attachments:

resource-owner-optimization-v5-step1.patchtext/x-patchDownload
diff --git a/src/backend/utils/resowner/resowner.c b/src/backend/utils/resowner/resowner.c
index b1d71b5..69217b5 100644
--- a/src/backend/utils/resowner/resowner.c
+++ b/src/backend/utils/resowner/resowner.c
@@ -29,6 +29,168 @@
 #include "utils/snapmgr.h"
 
 /*
+ * ResourceArray is a common structure for storing different types of resources.
+ *
+ * ResourceOwner can own `HeapTuple`s, `Relation`s, `Snapshot`s, etc. For
+ * each resource type there are procedures ResourceOwnerRemember* and
+ * ResourceOwnerForget*. There are also ResourceOwnerEnlarge* procedures
+ * which should be called before corresponding ResourceOwnerRemember* calls
+ * (see below). Internally each type of resource is stored in separate
+ * ResourceArray.
+ */
+typedef struct ResourceArray
+{
+	Datum	   *itemsarr;		/* buffer for storing values */
+	uint32		capacity;		/* capacity of array */
+	uint32		nitems;			/* how many items is stored in items array */
+}	ResourceArray;
+
+/*
+ * This number is used as initial size of resource array. If given number of
+ * items is not enough, we double array size and reallocate memory.
+ */
+#define RESARRAY_INIT_SIZE 16
+
+/* Convert File to Datum */
+#define FileGetDatum(file) ((Datum)(file))
+
+/* Convert Datum to File */
+#define DatumGetFile(datum)((File)(datum))
+
+/* Convert Buffer to Datum */
+#define BufferGetDatum(buffer)((Datum)(buffer))
+
+/* Convert Datum to Buffer */
+#define DatumGetBuffer(datum)((Buffer)(datum))
+
+/*
+ * Initialize ResourceArray
+ */
+static void
+ResourceArrayInit(ResourceArray * resarr)
+{
+	Assert(resarr->itemsarr == NULL);
+	Assert(resarr->capacity == 0);
+	Assert(resarr->nitems == 0);
+}
+
+/*
+ * Add a resource to ResourceArray
+ *
+ * Caller must have previously done ResourceArrayEnlarge()
+ */
+static void
+ResourceArrayAdd(ResourceArray * resarr, Datum data)
+{
+	Assert(resarr->capacity > 0);
+	Assert(resarr->itemsarr != NULL);
+	Assert(resarr->nitems < resarr->capacity);
+
+	resarr->itemsarr[resarr->nitems] = data;
+	resarr->nitems++;
+}
+
+/*
+ * Remove a resource from ResourceArray
+ *
+ * Returns true on success, false if resource was not found
+ */
+static bool
+ResourceArrayRemove(ResourceArray * resarr, Datum data)
+{
+	int			i,
+				j,
+				lastidx;
+
+	Assert(resarr->capacity > 0);
+	Assert(resarr->itemsarr != NULL);
+
+	lastidx = ((int) resarr->nitems) - 1;
+
+	for (i = lastidx; i >= 0; i--)
+	{
+		if (resarr->itemsarr[i] == data)
+		{
+			for (j = i; j < lastidx; j++)
+				resarr->itemsarr[j] = resarr->itemsarr[j + 1];
+			resarr->nitems--;
+			return true;
+		}
+	}
+
+	return false;
+}
+
+/*
+ * Make sure there is a room for at least one more resource in an array.
+ *
+ * This is separate from actually inserting a resource because if we run out
+ * of memory, it's critical to do so *before* acquiring the resource.
+ */
+static void
+ResourceArrayEnlarge(ResourceArray * resarr)
+{
+	uint32		i,
+				oldcap,
+				oldnitems;
+	Datum	   *olditemsarr;
+
+	if (resarr->nitems < resarr->capacity)
+		return;					/* nothing to do */
+
+	olditemsarr = resarr->itemsarr;
+	oldcap = resarr->capacity;
+	oldnitems = resarr->nitems;
+
+	resarr->capacity = oldcap > 0 ? oldcap * 2 : RESARRAY_INIT_SIZE;
+	resarr->itemsarr = (Datum *)
+		MemoryContextAlloc(TopMemoryContext,
+						   resarr->capacity * sizeof(Datum));
+	resarr->nitems = 0;
+
+	if (olditemsarr != NULL)
+	{
+		for (i = 0; i < oldnitems; i++)
+			ResourceArrayAdd(resarr, olditemsarr[i]);
+		pfree(olditemsarr);
+	}
+}
+
+/*
+ * Get any convenient element.
+ *
+ * Returns true on success, false on failure.
+ */
+static bool
+ResourceArrayGetAny(ResourceArray * resarr, Datum *out)
+{
+	if (resarr->nitems == 0)
+		return false;
+
+	Assert(resarr->capacity > 0);
+
+	*out = resarr->itemsarr[resarr->nitems - 1];
+	return true;
+}
+
+/*
+ * Return ResourceArray to initial state
+ */
+static void
+ResourceArrayFree(ResourceArray * resarr)
+{
+	Assert(resarr->nitems == 0);
+
+	resarr->capacity = 0;
+
+	if (!resarr->itemsarr)
+		return;
+
+	pfree(resarr->itemsarr);
+	resarr->itemsarr = NULL;
+}
+
+/*
  * To speed up bulk releasing or reassigning locks from a resource owner to
  * its parent, each resource owner has a small cache of locks it owns. The
  * lock manager has the same information in its local lock hash table, and
@@ -56,53 +218,22 @@ typedef struct ResourceOwnerData
 	ResourceOwner nextchild;	/* next child of same parent */
 	const char *name;			/* name (just for debugging) */
 
-	/* We have built-in support for remembering owned buffers */
-	int			nbuffers;		/* number of owned buffer pins */
-	Buffer	   *buffers;		/* dynamically allocated array */
-	int			maxbuffers;		/* currently allocated array size */
-
 	/* We can remember up to MAX_RESOWNER_LOCKS references to local locks. */
 	int			nlocks;			/* number of owned locks */
 	LOCALLOCK  *locks[MAX_RESOWNER_LOCKS];		/* list of owned locks */
 
-	/* We have built-in support for remembering catcache references */
-	int			ncatrefs;		/* number of owned catcache pins */
-	HeapTuple  *catrefs;		/* dynamically allocated array */
-	int			maxcatrefs;		/* currently allocated array size */
-
-	int			ncatlistrefs;	/* number of owned catcache-list pins */
-	CatCList  **catlistrefs;	/* dynamically allocated array */
-	int			maxcatlistrefs; /* currently allocated array size */
-
-	/* We have built-in support for remembering relcache references */
-	int			nrelrefs;		/* number of owned relcache pins */
-	Relation   *relrefs;		/* dynamically allocated array */
-	int			maxrelrefs;		/* currently allocated array size */
-
-	/* We have built-in support for remembering plancache references */
-	int			nplanrefs;		/* number of owned plancache pins */
-	CachedPlan **planrefs;		/* dynamically allocated array */
-	int			maxplanrefs;	/* currently allocated array size */
-
-	/* We have built-in support for remembering tupdesc references */
-	int			ntupdescs;		/* number of owned tupdesc references */
-	TupleDesc  *tupdescs;		/* dynamically allocated array */
-	int			maxtupdescs;	/* currently allocated array size */
-
-	/* We have built-in support for remembering snapshot references */
-	int			nsnapshots;		/* number of owned snapshot references */
-	Snapshot   *snapshots;		/* dynamically allocated array */
-	int			maxsnapshots;	/* currently allocated array size */
-
-	/* We have built-in support for remembering open temporary files */
-	int			nfiles;			/* number of owned temporary files */
-	File	   *files;			/* dynamically allocated array */
-	int			maxfiles;		/* currently allocated array size */
-
-	/* We have built-in support for remembering dynamic shmem segments */
-	int			ndsms;			/* number of owned shmem segments */
-	dsm_segment **dsms;			/* dynamically allocated array */
-	int			maxdsms;		/* currently allocated array size */
+	/* We have built-in support for remembering: */
+
+	ResourceArray catrefarr;	/* `HeapTuple`s */
+	ResourceArray catlistrefarr;	/* `ResourceOwner`s */
+	ResourceArray relrefarr;	/* `Relation`s */
+	ResourceArray planrefarr;	/* `CachedPlan*`s */
+	ResourceArray tupdescarr;	/* `TupleDesc`s */
+	ResourceArray snapshotarr;	/* `Snapshot`s */
+	ResourceArray dsmarr;		/* `dsm_segment*`s */
+	ResourceArray bufferarr;	/* `Buffer`s  */
+	ResourceArray filearr;		/* `File`s */
+
 }	ResourceOwnerData;
 
 
@@ -168,6 +299,16 @@ ResourceOwnerCreate(ResourceOwner parent, const char *name)
 		parent->firstchild = owner;
 	}
 
+	ResourceArrayInit(&(owner->catrefarr));
+	ResourceArrayInit(&(owner->catlistrefarr));
+	ResourceArrayInit(&(owner->relrefarr));
+	ResourceArrayInit(&(owner->planrefarr));
+	ResourceArrayInit(&(owner->tupdescarr));
+	ResourceArrayInit(&(owner->snapshotarr));
+	ResourceArrayInit(&(owner->dsmarr));
+	ResourceArrayInit(&(owner->bufferarr));
+	ResourceArrayInit(&(owner->filearr));
+
 	return owner;
 }
 
@@ -229,6 +370,7 @@ ResourceOwnerReleaseInternal(ResourceOwner owner,
 	ResourceOwner child;
 	ResourceOwner save;
 	ResourceReleaseCallbackItem *item;
+	Datum		foundres;
 
 	/* Recurse to handle descendants */
 	for (child = owner->firstchild; child != NULL; child = child->nextchild)
@@ -252,45 +394,34 @@ ResourceOwnerReleaseInternal(ResourceOwner owner,
 		 * During a commit, there shouldn't be any remaining pins --- that
 		 * would indicate failure to clean up the executor correctly --- so
 		 * issue warnings.  In the abort case, just clean up quietly.
-		 *
-		 * We are careful to do the releasing back-to-front, so as to avoid
-		 * O(N^2) behavior in ResourceOwnerForgetBuffer().
 		 */
-		while (owner->nbuffers > 0)
+		while (ResourceArrayGetAny(&(owner->bufferarr), &foundres))
 		{
+			Buffer		res = DatumGetBuffer(foundres);
+
 			if (isCommit)
-				PrintBufferLeakWarning(owner->buffers[owner->nbuffers - 1]);
-			ReleaseBuffer(owner->buffers[owner->nbuffers - 1]);
+				PrintBufferLeakWarning(res);
+			ReleaseBuffer(res);
 		}
 
-		/*
-		 * Release relcache references.  Note that RelationClose will remove
-		 * the relref entry from my list, so I just have to iterate till there
-		 * are none.
-		 *
-		 * As with buffer pins, warn if any are left at commit time, and
-		 * release back-to-front for speed.
-		 */
-		while (owner->nrelrefs > 0)
+		/* Ditto for relcache references. */
+		while (ResourceArrayGetAny(&(owner->relrefarr), &foundres))
 		{
+			Relation	res = (Relation) DatumGetPointer(foundres);
+
 			if (isCommit)
-				PrintRelCacheLeakWarning(owner->relrefs[owner->nrelrefs - 1]);
-			RelationClose(owner->relrefs[owner->nrelrefs - 1]);
+				PrintRelCacheLeakWarning(res);
+			RelationClose(res);
 		}
 
-		/*
-		 * Release dynamic shared memory segments.  Note that dsm_detach()
-		 * will remove the segment from my list, so I just have to iterate
-		 * until there are none.
-		 *
-		 * As in the preceding cases, warn if there are leftover at commit
-		 * time.
-		 */
-		while (owner->ndsms > 0)
+		/* Ditto for dynamic shared memory segments */
+		while (ResourceArrayGetAny(&(owner->dsmarr), &foundres))
 		{
+			dsm_segment *res = (dsm_segment *) DatumGetPointer(foundres);
+
 			if (isCommit)
-				PrintDSMLeakWarning(owner->dsms[owner->ndsms - 1]);
-			dsm_detach(owner->dsms[owner->ndsms - 1]);
+				PrintDSMLeakWarning(res);
+			dsm_detach(res);
 		}
 	}
 	else if (phase == RESOURCE_RELEASE_LOCKS)
@@ -351,47 +482,63 @@ ResourceOwnerReleaseInternal(ResourceOwner owner,
 		 * As with buffer pins, warn if any are left at commit time, and
 		 * release back-to-front for speed.
 		 */
-		while (owner->ncatrefs > 0)
+		while (ResourceArrayGetAny(&(owner->catrefarr), &foundres))
 		{
+			HeapTuple	res = (HeapTuple) DatumGetPointer(foundres);
+
 			if (isCommit)
-				PrintCatCacheLeakWarning(owner->catrefs[owner->ncatrefs - 1]);
-			ReleaseCatCache(owner->catrefs[owner->ncatrefs - 1]);
+				PrintCatCacheLeakWarning(res);
+			ReleaseCatCache(res);
 		}
+
 		/* Ditto for catcache lists */
-		while (owner->ncatlistrefs > 0)
+		while (ResourceArrayGetAny(&(owner->catlistrefarr), &foundres))
 		{
+			CatCList   *res = (CatCList *) DatumGetPointer(foundres);
+
 			if (isCommit)
-				PrintCatCacheListLeakWarning(owner->catlistrefs[owner->ncatlistrefs - 1]);
-			ReleaseCatCacheList(owner->catlistrefs[owner->ncatlistrefs - 1]);
+				PrintCatCacheListLeakWarning(res);
+			ReleaseCatCacheList(res);
 		}
+
 		/* Ditto for plancache references */
-		while (owner->nplanrefs > 0)
+		while (ResourceArrayGetAny(&(owner->planrefarr), &foundres))
 		{
+			CachedPlan *res = (CachedPlan *) DatumGetPointer(foundres);
+
 			if (isCommit)
-				PrintPlanCacheLeakWarning(owner->planrefs[owner->nplanrefs - 1]);
-			ReleaseCachedPlan(owner->planrefs[owner->nplanrefs - 1], true);
+				PrintPlanCacheLeakWarning(res);
+			ReleaseCachedPlan(res, true);
 		}
+
 		/* Ditto for tupdesc references */
-		while (owner->ntupdescs > 0)
+		while (ResourceArrayGetAny(&(owner->tupdescarr), &foundres))
 		{
+			TupleDesc	res = (TupleDesc) DatumGetPointer(foundres);
+
 			if (isCommit)
-				PrintTupleDescLeakWarning(owner->tupdescs[owner->ntupdescs - 1]);
-			DecrTupleDescRefCount(owner->tupdescs[owner->ntupdescs - 1]);
+				PrintTupleDescLeakWarning(res);
+			DecrTupleDescRefCount(res);
 		}
+
 		/* Ditto for snapshot references */
-		while (owner->nsnapshots > 0)
+		while (ResourceArrayGetAny(&(owner->snapshotarr), &foundres))
 		{
+			Snapshot	res = (Snapshot) DatumGetPointer(foundres);
+
 			if (isCommit)
-				PrintSnapshotLeakWarning(owner->snapshots[owner->nsnapshots - 1]);
-			UnregisterSnapshot(owner->snapshots[owner->nsnapshots - 1]);
+				PrintSnapshotLeakWarning(res);
+			UnregisterSnapshot(res);
 		}
 
 		/* Ditto for temporary files */
-		while (owner->nfiles > 0)
+		while (ResourceArrayGetAny(&(owner->filearr), &foundres))
 		{
+			File		res = DatumGetFile(foundres);
+
 			if (isCommit)
-				PrintFileLeakWarning(owner->files[owner->nfiles - 1]);
-			FileClose(owner->files[owner->nfiles - 1]);
+				PrintFileLeakWarning(res);
+			FileClose(res);
 		}
 
 		/* Clean up index scans too */
@@ -418,16 +565,7 @@ ResourceOwnerDelete(ResourceOwner owner)
 	Assert(owner != CurrentResourceOwner);
 
 	/* And it better not own any resources, either */
-	Assert(owner->nbuffers == 0);
 	Assert(owner->nlocks == 0 || owner->nlocks == MAX_RESOWNER_LOCKS + 1);
-	Assert(owner->ncatrefs == 0);
-	Assert(owner->ncatlistrefs == 0);
-	Assert(owner->nrelrefs == 0);
-	Assert(owner->ndsms == 0);
-	Assert(owner->nplanrefs == 0);
-	Assert(owner->ntupdescs == 0);
-	Assert(owner->nsnapshots == 0);
-	Assert(owner->nfiles == 0);
 
 	/*
 	 * Delete children.  The recursive call will delink the child from me, so
@@ -444,25 +582,15 @@ ResourceOwnerDelete(ResourceOwner owner)
 	ResourceOwnerNewParent(owner, NULL);
 
 	/* And free the object. */
-	if (owner->buffers)
-		pfree(owner->buffers);
-	if (owner->catrefs)
-		pfree(owner->catrefs);
-	if (owner->catlistrefs)
-		pfree(owner->catlistrefs);
-	if (owner->relrefs)
-		pfree(owner->relrefs);
-	if (owner->planrefs)
-		pfree(owner->planrefs);
-	if (owner->tupdescs)
-		pfree(owner->tupdescs);
-	if (owner->snapshots)
-		pfree(owner->snapshots);
-	if (owner->files)
-		pfree(owner->files);
-	if (owner->dsms)
-		pfree(owner->dsms);
-
+	ResourceArrayFree(&(owner->catrefarr));
+	ResourceArrayFree(&(owner->catlistrefarr));
+	ResourceArrayFree(&(owner->relrefarr));
+	ResourceArrayFree(&(owner->planrefarr));
+	ResourceArrayFree(&(owner->tupdescarr));
+	ResourceArrayFree(&(owner->snapshotarr));
+	ResourceArrayFree(&(owner->dsmarr));
+	ResourceArrayFree(&(owner->bufferarr));
+	ResourceArrayFree(&(owner->filearr));
 	pfree(owner);
 }
 
@@ -575,26 +703,9 @@ UnregisterResourceReleaseCallback(ResourceReleaseCallback callback, void *arg)
 void
 ResourceOwnerEnlargeBuffers(ResourceOwner owner)
 {
-	int			newmax;
-
-	if (owner == NULL ||
-		owner->nbuffers < owner->maxbuffers)
-		return;					/* nothing to do */
-
-	if (owner->buffers == NULL)
-	{
-		newmax = 16;
-		owner->buffers = (Buffer *)
-			MemoryContextAlloc(TopMemoryContext, newmax * sizeof(Buffer));
-		owner->maxbuffers = newmax;
-	}
-	else
-	{
-		newmax = owner->maxbuffers * 2;
-		owner->buffers = (Buffer *)
-			repalloc(owner->buffers, newmax * sizeof(Buffer));
-		owner->maxbuffers = newmax;
-	}
+	if (owner == NULL)
+		return;
+	ResourceArrayEnlarge(&(owner->bufferarr));
 }
 
 /*
@@ -608,12 +719,9 @@ ResourceOwnerEnlargeBuffers(ResourceOwner owner)
 void
 ResourceOwnerRememberBuffer(ResourceOwner owner, Buffer buffer)
 {
-	if (owner != NULL)
-	{
-		Assert(owner->nbuffers < owner->maxbuffers);
-		owner->buffers[owner->nbuffers] = buffer;
-		owner->nbuffers++;
-	}
+	if (owner == NULL)
+		return;
+	ResourceArrayAdd(&(owner->bufferarr), BufferGetDatum(buffer));
 }
 
 /*
@@ -625,33 +733,15 @@ ResourceOwnerRememberBuffer(ResourceOwner owner, Buffer buffer)
 void
 ResourceOwnerForgetBuffer(ResourceOwner owner, Buffer buffer)
 {
-	if (owner != NULL)
-	{
-		Buffer	   *buffers = owner->buffers;
-		int			nb1 = owner->nbuffers - 1;
-		int			i;
+	bool		res;
 
-		/*
-		 * Scan back-to-front because it's more likely we are releasing a
-		 * recently pinned buffer.  This isn't always the case of course, but
-		 * it's the way to bet.
-		 */
-		for (i = nb1; i >= 0; i--)
-		{
-			if (buffers[i] == buffer)
-			{
-				while (i < nb1)
-				{
-					buffers[i] = buffers[i + 1];
-					i++;
-				}
-				owner->nbuffers = nb1;
-				return;
-			}
-		}
+	if (owner == NULL)
+		return;
+
+	res = ResourceArrayRemove(&(owner->bufferarr), BufferGetDatum(buffer));
+	if (!res)
 		elog(ERROR, "buffer %d is not owned by resource owner %s",
 			 buffer, owner->name);
-	}
 }
 
 /*
@@ -667,6 +757,8 @@ ResourceOwnerForgetBuffer(ResourceOwner owner, Buffer buffer)
 void
 ResourceOwnerRememberLock(ResourceOwner owner, LOCALLOCK *locallock)
 {
+	Assert(locallock != NULL);
+
 	if (owner->nlocks > MAX_RESOWNER_LOCKS)
 		return;					/* we have already overflowed */
 
@@ -714,25 +806,7 @@ ResourceOwnerForgetLock(ResourceOwner owner, LOCALLOCK *locallock)
 void
 ResourceOwnerEnlargeCatCacheRefs(ResourceOwner owner)
 {
-	int			newmax;
-
-	if (owner->ncatrefs < owner->maxcatrefs)
-		return;					/* nothing to do */
-
-	if (owner->catrefs == NULL)
-	{
-		newmax = 16;
-		owner->catrefs = (HeapTuple *)
-			MemoryContextAlloc(TopMemoryContext, newmax * sizeof(HeapTuple));
-		owner->maxcatrefs = newmax;
-	}
-	else
-	{
-		newmax = owner->maxcatrefs * 2;
-		owner->catrefs = (HeapTuple *)
-			repalloc(owner->catrefs, newmax * sizeof(HeapTuple));
-		owner->maxcatrefs = newmax;
-	}
+	ResourceArrayEnlarge(&(owner->catrefarr));
 }
 
 /*
@@ -743,9 +817,7 @@ ResourceOwnerEnlargeCatCacheRefs(ResourceOwner owner)
 void
 ResourceOwnerRememberCatCacheRef(ResourceOwner owner, HeapTuple tuple)
 {
-	Assert(owner->ncatrefs < owner->maxcatrefs);
-	owner->catrefs[owner->ncatrefs] = tuple;
-	owner->ncatrefs++;
+	ResourceArrayAdd(&(owner->catrefarr), PointerGetDatum(tuple));
 }
 
 /*
@@ -754,25 +826,12 @@ ResourceOwnerRememberCatCacheRef(ResourceOwner owner, HeapTuple tuple)
 void
 ResourceOwnerForgetCatCacheRef(ResourceOwner owner, HeapTuple tuple)
 {
-	HeapTuple  *catrefs = owner->catrefs;
-	int			nc1 = owner->ncatrefs - 1;
-	int			i;
+	bool		res = ResourceArrayRemove(&(owner->catrefarr),
+										  PointerGetDatum(tuple));
 
-	for (i = nc1; i >= 0; i--)
-	{
-		if (catrefs[i] == tuple)
-		{
-			while (i < nc1)
-			{
-				catrefs[i] = catrefs[i + 1];
-				i++;
-			}
-			owner->ncatrefs = nc1;
-			return;
-		}
-	}
-	elog(ERROR, "catcache reference %p is not owned by resource owner %s",
-		 tuple, owner->name);
+	if (!res)
+		elog(ERROR, "catcache reference %p is not owned by resource owner %s",
+			 tuple, owner->name);
 }
 
 /*
@@ -785,25 +844,7 @@ ResourceOwnerForgetCatCacheRef(ResourceOwner owner, HeapTuple tuple)
 void
 ResourceOwnerEnlargeCatCacheListRefs(ResourceOwner owner)
 {
-	int			newmax;
-
-	if (owner->ncatlistrefs < owner->maxcatlistrefs)
-		return;					/* nothing to do */
-
-	if (owner->catlistrefs == NULL)
-	{
-		newmax = 16;
-		owner->catlistrefs = (CatCList **)
-			MemoryContextAlloc(TopMemoryContext, newmax * sizeof(CatCList *));
-		owner->maxcatlistrefs = newmax;
-	}
-	else
-	{
-		newmax = owner->maxcatlistrefs * 2;
-		owner->catlistrefs = (CatCList **)
-			repalloc(owner->catlistrefs, newmax * sizeof(CatCList *));
-		owner->maxcatlistrefs = newmax;
-	}
+	ResourceArrayEnlarge(&(owner->catlistrefarr));
 }
 
 /*
@@ -814,9 +855,7 @@ ResourceOwnerEnlargeCatCacheListRefs(ResourceOwner owner)
 void
 ResourceOwnerRememberCatCacheListRef(ResourceOwner owner, CatCList *list)
 {
-	Assert(owner->ncatlistrefs < owner->maxcatlistrefs);
-	owner->catlistrefs[owner->ncatlistrefs] = list;
-	owner->ncatlistrefs++;
+	ResourceArrayAdd(&(owner->catlistrefarr), PointerGetDatum(list));
 }
 
 /*
@@ -825,25 +864,12 @@ ResourceOwnerRememberCatCacheListRef(ResourceOwner owner, CatCList *list)
 void
 ResourceOwnerForgetCatCacheListRef(ResourceOwner owner, CatCList *list)
 {
-	CatCList  **catlistrefs = owner->catlistrefs;
-	int			nc1 = owner->ncatlistrefs - 1;
-	int			i;
+	bool		res = ResourceArrayRemove(&(owner->catlistrefarr),
+										  PointerGetDatum(list));
 
-	for (i = nc1; i >= 0; i--)
-	{
-		if (catlistrefs[i] == list)
-		{
-			while (i < nc1)
-			{
-				catlistrefs[i] = catlistrefs[i + 1];
-				i++;
-			}
-			owner->ncatlistrefs = nc1;
-			return;
-		}
-	}
-	elog(ERROR, "catcache list reference %p is not owned by resource owner %s",
-		 list, owner->name);
+	if (!res)
+		elog(ERROR, "catcache list reference %p is not owned by resource owner %s",
+			 list, owner->name);
 }
 
 /*
@@ -856,25 +882,7 @@ ResourceOwnerForgetCatCacheListRef(ResourceOwner owner, CatCList *list)
 void
 ResourceOwnerEnlargeRelationRefs(ResourceOwner owner)
 {
-	int			newmax;
-
-	if (owner->nrelrefs < owner->maxrelrefs)
-		return;					/* nothing to do */
-
-	if (owner->relrefs == NULL)
-	{
-		newmax = 16;
-		owner->relrefs = (Relation *)
-			MemoryContextAlloc(TopMemoryContext, newmax * sizeof(Relation));
-		owner->maxrelrefs = newmax;
-	}
-	else
-	{
-		newmax = owner->maxrelrefs * 2;
-		owner->relrefs = (Relation *)
-			repalloc(owner->relrefs, newmax * sizeof(Relation));
-		owner->maxrelrefs = newmax;
-	}
+	ResourceArrayEnlarge(&(owner->relrefarr));
 }
 
 /*
@@ -885,9 +893,7 @@ ResourceOwnerEnlargeRelationRefs(ResourceOwner owner)
 void
 ResourceOwnerRememberRelationRef(ResourceOwner owner, Relation rel)
 {
-	Assert(owner->nrelrefs < owner->maxrelrefs);
-	owner->relrefs[owner->nrelrefs] = rel;
-	owner->nrelrefs++;
+	ResourceArrayAdd(&(owner->relrefarr), PointerGetDatum(rel));
 }
 
 /*
@@ -896,25 +902,12 @@ ResourceOwnerRememberRelationRef(ResourceOwner owner, Relation rel)
 void
 ResourceOwnerForgetRelationRef(ResourceOwner owner, Relation rel)
 {
-	Relation   *relrefs = owner->relrefs;
-	int			nr1 = owner->nrelrefs - 1;
-	int			i;
+	bool		res = ResourceArrayRemove(&(owner->relrefarr),
+										  PointerGetDatum(rel));
 
-	for (i = nr1; i >= 0; i--)
-	{
-		if (relrefs[i] == rel)
-		{
-			while (i < nr1)
-			{
-				relrefs[i] = relrefs[i + 1];
-				i++;
-			}
-			owner->nrelrefs = nr1;
-			return;
-		}
-	}
-	elog(ERROR, "relcache reference %s is not owned by resource owner %s",
-		 RelationGetRelationName(rel), owner->name);
+	if (!res)
+		elog(ERROR, "relcache reference %s is not owned by resource owner %s",
+			 RelationGetRelationName(rel), owner->name);
 }
 
 /*
@@ -937,25 +930,7 @@ PrintRelCacheLeakWarning(Relation rel)
 void
 ResourceOwnerEnlargePlanCacheRefs(ResourceOwner owner)
 {
-	int			newmax;
-
-	if (owner->nplanrefs < owner->maxplanrefs)
-		return;					/* nothing to do */
-
-	if (owner->planrefs == NULL)
-	{
-		newmax = 16;
-		owner->planrefs = (CachedPlan **)
-			MemoryContextAlloc(TopMemoryContext, newmax * sizeof(CachedPlan *));
-		owner->maxplanrefs = newmax;
-	}
-	else
-	{
-		newmax = owner->maxplanrefs * 2;
-		owner->planrefs = (CachedPlan **)
-			repalloc(owner->planrefs, newmax * sizeof(CachedPlan *));
-		owner->maxplanrefs = newmax;
-	}
+	ResourceArrayEnlarge(&(owner->planrefarr));
 }
 
 /*
@@ -966,9 +941,7 @@ ResourceOwnerEnlargePlanCacheRefs(ResourceOwner owner)
 void
 ResourceOwnerRememberPlanCacheRef(ResourceOwner owner, CachedPlan *plan)
 {
-	Assert(owner->nplanrefs < owner->maxplanrefs);
-	owner->planrefs[owner->nplanrefs] = plan;
-	owner->nplanrefs++;
+	ResourceArrayAdd(&(owner->planrefarr), PointerGetDatum(plan));
 }
 
 /*
@@ -977,25 +950,12 @@ ResourceOwnerRememberPlanCacheRef(ResourceOwner owner, CachedPlan *plan)
 void
 ResourceOwnerForgetPlanCacheRef(ResourceOwner owner, CachedPlan *plan)
 {
-	CachedPlan **planrefs = owner->planrefs;
-	int			np1 = owner->nplanrefs - 1;
-	int			i;
+	bool		res = ResourceArrayRemove(&(owner->planrefarr),
+										  PointerGetDatum(plan));
 
-	for (i = np1; i >= 0; i--)
-	{
-		if (planrefs[i] == plan)
-		{
-			while (i < np1)
-			{
-				planrefs[i] = planrefs[i + 1];
-				i++;
-			}
-			owner->nplanrefs = np1;
-			return;
-		}
-	}
-	elog(ERROR, "plancache reference %p is not owned by resource owner %s",
-		 plan, owner->name);
+	if (!res)
+		elog(ERROR, "plancache reference %p is not owned by resource owner %s",
+			 plan, owner->name);
 }
 
 /*
@@ -1017,25 +977,7 @@ PrintPlanCacheLeakWarning(CachedPlan *plan)
 void
 ResourceOwnerEnlargeTupleDescs(ResourceOwner owner)
 {
-	int			newmax;
-
-	if (owner->ntupdescs < owner->maxtupdescs)
-		return;					/* nothing to do */
-
-	if (owner->tupdescs == NULL)
-	{
-		newmax = 16;
-		owner->tupdescs = (TupleDesc *)
-			MemoryContextAlloc(TopMemoryContext, newmax * sizeof(TupleDesc));
-		owner->maxtupdescs = newmax;
-	}
-	else
-	{
-		newmax = owner->maxtupdescs * 2;
-		owner->tupdescs = (TupleDesc *)
-			repalloc(owner->tupdescs, newmax * sizeof(TupleDesc));
-		owner->maxtupdescs = newmax;
-	}
+	ResourceArrayEnlarge(&(owner->tupdescarr));
 }
 
 /*
@@ -1046,9 +988,7 @@ ResourceOwnerEnlargeTupleDescs(ResourceOwner owner)
 void
 ResourceOwnerRememberTupleDesc(ResourceOwner owner, TupleDesc tupdesc)
 {
-	Assert(owner->ntupdescs < owner->maxtupdescs);
-	owner->tupdescs[owner->ntupdescs] = tupdesc;
-	owner->ntupdescs++;
+	ResourceArrayAdd(&(owner->tupdescarr), PointerGetDatum(tupdesc));
 }
 
 /*
@@ -1057,25 +997,12 @@ ResourceOwnerRememberTupleDesc(ResourceOwner owner, TupleDesc tupdesc)
 void
 ResourceOwnerForgetTupleDesc(ResourceOwner owner, TupleDesc tupdesc)
 {
-	TupleDesc  *tupdescs = owner->tupdescs;
-	int			nt1 = owner->ntupdescs - 1;
-	int			i;
+	bool		res = ResourceArrayRemove(&(owner->tupdescarr),
+										  PointerGetDatum(tupdesc));
 
-	for (i = nt1; i >= 0; i--)
-	{
-		if (tupdescs[i] == tupdesc)
-		{
-			while (i < nt1)
-			{
-				tupdescs[i] = tupdescs[i + 1];
-				i++;
-			}
-			owner->ntupdescs = nt1;
-			return;
-		}
-	}
-	elog(ERROR, "tupdesc reference %p is not owned by resource owner %s",
-		 tupdesc, owner->name);
+	if (!res)
+		elog(ERROR, "tupdesc reference %p is not owned by resource owner %s",
+			 tupdesc, owner->name);
 }
 
 /*
@@ -1099,25 +1026,7 @@ PrintTupleDescLeakWarning(TupleDesc tupdesc)
 void
 ResourceOwnerEnlargeSnapshots(ResourceOwner owner)
 {
-	int			newmax;
-
-	if (owner->nsnapshots < owner->maxsnapshots)
-		return;					/* nothing to do */
-
-	if (owner->snapshots == NULL)
-	{
-		newmax = 16;
-		owner->snapshots = (Snapshot *)
-			MemoryContextAlloc(TopMemoryContext, newmax * sizeof(Snapshot));
-		owner->maxsnapshots = newmax;
-	}
-	else
-	{
-		newmax = owner->maxsnapshots * 2;
-		owner->snapshots = (Snapshot *)
-			repalloc(owner->snapshots, newmax * sizeof(Snapshot));
-		owner->maxsnapshots = newmax;
-	}
+	ResourceArrayEnlarge(&(owner->snapshotarr));
 }
 
 /*
@@ -1128,9 +1037,7 @@ ResourceOwnerEnlargeSnapshots(ResourceOwner owner)
 void
 ResourceOwnerRememberSnapshot(ResourceOwner owner, Snapshot snapshot)
 {
-	Assert(owner->nsnapshots < owner->maxsnapshots);
-	owner->snapshots[owner->nsnapshots] = snapshot;
-	owner->nsnapshots++;
+	ResourceArrayAdd(&(owner->snapshotarr), PointerGetDatum(snapshot));
 }
 
 /*
@@ -1139,25 +1046,12 @@ ResourceOwnerRememberSnapshot(ResourceOwner owner, Snapshot snapshot)
 void
 ResourceOwnerForgetSnapshot(ResourceOwner owner, Snapshot snapshot)
 {
-	Snapshot   *snapshots = owner->snapshots;
-	int			ns1 = owner->nsnapshots - 1;
-	int			i;
+	bool		res = ResourceArrayRemove(&(owner->snapshotarr),
+										  PointerGetDatum(snapshot));
 
-	for (i = ns1; i >= 0; i--)
-	{
-		if (snapshots[i] == snapshot)
-		{
-			while (i < ns1)
-			{
-				snapshots[i] = snapshots[i + 1];
-				i++;
-			}
-			owner->nsnapshots = ns1;
-			return;
-		}
-	}
-	elog(ERROR, "snapshot reference %p is not owned by resource owner %s",
-		 snapshot, owner->name);
+	if (!res)
+		elog(ERROR, "snapshot reference %p is not owned by resource owner %s",
+			 snapshot, owner->name);
 }
 
 /*
@@ -1182,25 +1076,7 @@ PrintSnapshotLeakWarning(Snapshot snapshot)
 void
 ResourceOwnerEnlargeFiles(ResourceOwner owner)
 {
-	int			newmax;
-
-	if (owner->nfiles < owner->maxfiles)
-		return;					/* nothing to do */
-
-	if (owner->files == NULL)
-	{
-		newmax = 16;
-		owner->files = (File *)
-			MemoryContextAlloc(TopMemoryContext, newmax * sizeof(File));
-		owner->maxfiles = newmax;
-	}
-	else
-	{
-		newmax = owner->maxfiles * 2;
-		owner->files = (File *)
-			repalloc(owner->files, newmax * sizeof(File));
-		owner->maxfiles = newmax;
-	}
+	ResourceArrayEnlarge(&(owner->filearr));
 }
 
 /*
@@ -1211,9 +1087,7 @@ ResourceOwnerEnlargeFiles(ResourceOwner owner)
 void
 ResourceOwnerRememberFile(ResourceOwner owner, File file)
 {
-	Assert(owner->nfiles < owner->maxfiles);
-	owner->files[owner->nfiles] = file;
-	owner->nfiles++;
+	ResourceArrayAdd(&(owner->filearr), FileGetDatum(file));
 }
 
 /*
@@ -1222,25 +1096,12 @@ ResourceOwnerRememberFile(ResourceOwner owner, File file)
 void
 ResourceOwnerForgetFile(ResourceOwner owner, File file)
 {
-	File	   *files = owner->files;
-	int			ns1 = owner->nfiles - 1;
-	int			i;
+	bool		res = ResourceArrayRemove(&(owner->filearr),
+										  FileGetDatum(file));
 
-	for (i = ns1; i >= 0; i--)
-	{
-		if (files[i] == file)
-		{
-			while (i < ns1)
-			{
-				files[i] = files[i + 1];
-				i++;
-			}
-			owner->nfiles = ns1;
-			return;
-		}
-	}
-	elog(ERROR, "temporery file %d is not owned by resource owner %s",
-		 file, owner->name);
+	if (!res)
+		elog(ERROR, "temporary file %d is not owned by resource owner %s",
+			 file, owner->name);
 }
 
 
@@ -1265,26 +1126,7 @@ PrintFileLeakWarning(File file)
 void
 ResourceOwnerEnlargeDSMs(ResourceOwner owner)
 {
-	int			newmax;
-
-	if (owner->ndsms < owner->maxdsms)
-		return;					/* nothing to do */
-
-	if (owner->dsms == NULL)
-	{
-		newmax = 16;
-		owner->dsms = (dsm_segment **)
-			MemoryContextAlloc(TopMemoryContext,
-							   newmax * sizeof(dsm_segment *));
-		owner->maxdsms = newmax;
-	}
-	else
-	{
-		newmax = owner->maxdsms * 2;
-		owner->dsms = (dsm_segment **)
-			repalloc(owner->dsms, newmax * sizeof(dsm_segment *));
-		owner->maxdsms = newmax;
-	}
+	ResourceArrayEnlarge(&(owner->dsmarr));
 }
 
 /*
@@ -1295,9 +1137,7 @@ ResourceOwnerEnlargeDSMs(ResourceOwner owner)
 void
 ResourceOwnerRememberDSM(ResourceOwner owner, dsm_segment *seg)
 {
-	Assert(owner->ndsms < owner->maxdsms);
-	owner->dsms[owner->ndsms] = seg;
-	owner->ndsms++;
+	ResourceArrayAdd(&(owner->dsmarr), PointerGetDatum(seg));
 }
 
 /*
@@ -1306,26 +1146,12 @@ ResourceOwnerRememberDSM(ResourceOwner owner, dsm_segment *seg)
 void
 ResourceOwnerForgetDSM(ResourceOwner owner, dsm_segment *seg)
 {
-	dsm_segment **dsms = owner->dsms;
-	int			ns1 = owner->ndsms - 1;
-	int			i;
+	bool		res = ResourceArrayRemove(&(owner->dsmarr),
+										  PointerGetDatum(seg));
 
-	for (i = ns1; i >= 0; i--)
-	{
-		if (dsms[i] == seg)
-		{
-			while (i < ns1)
-			{
-				dsms[i] = dsms[i + 1];
-				i++;
-			}
-			owner->ndsms = ns1;
-			return;
-		}
-	}
-	elog(ERROR,
-		 "dynamic shared memory segment %u is not owned by resource owner %s",
-		 dsm_segment_handle(seg), owner->name);
+	if (!res)
+		elog(ERROR, "dynamic shared memory segment %u is not owned by resource"
+			 " owner %s", dsm_segment_handle(seg), owner->name);
 }
 
 
resource-owner-optimization-v5-step2a.patchtext/x-patchDownload
diff --git a/src/backend/access/hash/hashfunc.c b/src/backend/access/hash/hashfunc.c
index dabaf5a..39f3115 100644
--- a/src/backend/access/hash/hashfunc.c
+++ b/src/backend/access/hash/hashfunc.c
@@ -297,6 +297,9 @@ hashvarlena(PG_FUNCTION_ARGS)
  * of 2.  There is no need to do mod a prime (mod is sooo slow!).
  * If you need less than 32 bits, use a bitmask.
  *
+ * This procedure never fails. Its important. Some code notably ResourceOwner
+ * relies on this.
+ *
  * Note: we could easily change this function to return a 64-bit hash value
  * by using the final values of both b and c.  b is perhaps a little less
  * well mixed than c, however.
diff --git a/src/backend/utils/resowner/resowner.c b/src/backend/utils/resowner/resowner.c
index 69217b5..f47069b 100644
--- a/src/backend/utils/resowner/resowner.c
+++ b/src/backend/utils/resowner/resowner.c
@@ -37,17 +37,35 @@
  * which should be called before corresponding ResourceOwnerRemember* calls
  * (see below). Internally each type of resource is stored in separate
  * ResourceArray.
+ *
+ * There are two major reasons for using ResourceArray instead of, say,
+ * regular C arrays.
+ *
+ * Firstly we would like to prevent code duplication. For instance
+ * ResourceArray provides generic Remember/Forget/Enlarge procedures, so
+ * corresponding ResourceOwner* procedures are just a typesafe wrappers for
+ * these procedures.
+ *
+ * Secondly ResourceArray must be more efficient than regular C array.
+ * Current implementation in general could be considered a hash table. It has
+ * O(1) complexity of both Remember and Forget procedures.
  */
 typedef struct ResourceArray
 {
 	Datum	   *itemsarr;		/* buffer for storing values */
+	Datum		invalidval;		/* value that is considered invalid */
 	uint32		capacity;		/* capacity of array */
 	uint32		nitems;			/* how many items is stored in items array */
+	uint32		maxitems;		/* precalculated RESARRAY_MAX_ITEMS(capacity) */
+	uint32		lastidx;		/* index of last item returned by GetAny */
 }	ResourceArray;
 
 /*
  * This number is used as initial size of resource array. If given number of
  * items is not enough, we double array size and reallocate memory.
+ *
+ * Should be power of two since we use (arrsize - 1) as mask for hash value.
+ *
  */
 #define RESARRAY_INIT_SIZE 16
 
@@ -64,14 +82,27 @@ typedef struct ResourceArray
 #define DatumGetBuffer(datum)((Buffer)(datum))
 
 /*
+ * How many items could be stored in a resource array of given capacity. If
+ * this number is reached we need to resize an array to prevent hash collisions.
+ *
+ * This computation actually costs only two additions and one binary shift.
+ */
+#define RESARRAY_MAX_ITEMS(capacity) ((capacity)*3/4)
+
+/*
  * Initialize ResourceArray
  */
 static void
-ResourceArrayInit(ResourceArray * resarr)
+ResourceArrayInit(ResourceArray * resarr, Datum invalidval)
 {
 	Assert(resarr->itemsarr == NULL);
 	Assert(resarr->capacity == 0);
 	Assert(resarr->nitems == 0);
+	Assert(resarr->maxitems == 0);
+	Assert(resarr->invalidval == 0);
+	Assert(resarr->lastidx == 0);
+
+	resarr->invalidval = invalidval;
 }
 
 /*
@@ -82,11 +113,24 @@ ResourceArrayInit(ResourceArray * resarr)
 static void
 ResourceArrayAdd(ResourceArray * resarr, Datum data)
 {
+	Datum		idx;
+	Datum		mask = resarr->capacity - 1;
+
+	Assert(resarr->maxitems > resarr->nitems);
 	Assert(resarr->capacity > 0);
 	Assert(resarr->itemsarr != NULL);
-	Assert(resarr->nitems < resarr->capacity);
+	Assert(data != resarr->invalidval);
+
+	idx = hash_any((void *) &data, sizeof(data)) & mask;
+
+	while (true)
+	{
+		if (resarr->itemsarr[idx] == resarr->invalidval)
+			break;
+		idx = (idx + 1) & mask;
+	}
 
-	resarr->itemsarr[resarr->nitems] = data;
+	resarr->itemsarr[idx] = data;
 	resarr->nitems++;
 }
 
@@ -98,24 +142,24 @@ ResourceArrayAdd(ResourceArray * resarr, Datum data)
 static bool
 ResourceArrayRemove(ResourceArray * resarr, Datum data)
 {
-	int			i,
-				j,
-				lastidx;
+	uint32		i;
+	Datum		idx;
+	Datum		mask = resarr->capacity - 1;
 
 	Assert(resarr->capacity > 0);
 	Assert(resarr->itemsarr != NULL);
+	Assert(data != resarr->invalidval);
 
-	lastidx = ((int) resarr->nitems) - 1;
-
-	for (i = lastidx; i >= 0; i--)
+	idx = hash_any((void *) &data, sizeof(data)) & mask;
+	for (i = 0; i < resarr->capacity; i++)
 	{
-		if (resarr->itemsarr[i] == data)
+		if (resarr->itemsarr[idx] == data)
 		{
-			for (j = i; j < lastidx; j++)
-				resarr->itemsarr[j] = resarr->itemsarr[j + 1];
+			resarr->itemsarr[idx] = resarr->invalidval;
 			resarr->nitems--;
 			return true;
 		}
+		idx = (idx + 1) & mask;
 	}
 
 	return false;
@@ -131,27 +175,33 @@ static void
 ResourceArrayEnlarge(ResourceArray * resarr)
 {
 	uint32		i,
-				oldcap,
-				oldnitems;
+				oldcap;
 	Datum	   *olditemsarr;
 
-	if (resarr->nitems < resarr->capacity)
+	if (resarr->nitems < resarr->maxitems)
 		return;					/* nothing to do */
 
 	olditemsarr = resarr->itemsarr;
 	oldcap = resarr->capacity;
-	oldnitems = resarr->nitems;
 
 	resarr->capacity = oldcap > 0 ? oldcap * 2 : RESARRAY_INIT_SIZE;
 	resarr->itemsarr = (Datum *)
 		MemoryContextAlloc(TopMemoryContext,
 						   resarr->capacity * sizeof(Datum));
+	resarr->maxitems = RESARRAY_MAX_ITEMS(resarr->capacity);
 	resarr->nitems = 0;
 
+	for (i = 0; i < resarr->capacity; i++)
+		resarr->itemsarr[i] = resarr->invalidval;
+
 	if (olditemsarr != NULL)
 	{
-		for (i = 0; i < oldnitems; i++)
-			ResourceArrayAdd(resarr, olditemsarr[i]);
+		while (oldcap > 0)
+		{
+			oldcap--;
+			if (olditemsarr[oldcap] != resarr->invalidval)
+				ResourceArrayAdd(resarr, olditemsarr[oldcap]);
+		}
 		pfree(olditemsarr);
 	}
 }
@@ -164,12 +214,24 @@ ResourceArrayEnlarge(ResourceArray * resarr)
 static bool
 ResourceArrayGetAny(ResourceArray * resarr, Datum *out)
 {
+	uint32		mask;
+
 	if (resarr->nitems == 0)
 		return false;
 
 	Assert(resarr->capacity > 0);
+	mask = resarr->capacity - 1;
+
+	for (;;)
+	{
+		resarr->lastidx = resarr->lastidx & mask;
+		if (resarr->itemsarr[resarr->lastidx] != resarr->invalidval)
+			break;
+
+		resarr->lastidx++;
+	}
 
-	*out = resarr->itemsarr[resarr->nitems - 1];
+	*out = resarr->itemsarr[resarr->lastidx];
 	return true;
 }
 
@@ -182,6 +244,7 @@ ResourceArrayFree(ResourceArray * resarr)
 	Assert(resarr->nitems == 0);
 
 	resarr->capacity = 0;
+	resarr->maxitems = 0;
 
 	if (!resarr->itemsarr)
 		return;
@@ -299,15 +362,15 @@ ResourceOwnerCreate(ResourceOwner parent, const char *name)
 		parent->firstchild = owner;
 	}
 
-	ResourceArrayInit(&(owner->catrefarr));
-	ResourceArrayInit(&(owner->catlistrefarr));
-	ResourceArrayInit(&(owner->relrefarr));
-	ResourceArrayInit(&(owner->planrefarr));
-	ResourceArrayInit(&(owner->tupdescarr));
-	ResourceArrayInit(&(owner->snapshotarr));
-	ResourceArrayInit(&(owner->dsmarr));
-	ResourceArrayInit(&(owner->bufferarr));
-	ResourceArrayInit(&(owner->filearr));
+	ResourceArrayInit(&(owner->catrefarr), PointerGetDatum(NULL));
+	ResourceArrayInit(&(owner->catlistrefarr), PointerGetDatum(NULL));
+	ResourceArrayInit(&(owner->relrefarr), PointerGetDatum(NULL));
+	ResourceArrayInit(&(owner->planrefarr), PointerGetDatum(NULL));
+	ResourceArrayInit(&(owner->tupdescarr), PointerGetDatum(NULL));
+	ResourceArrayInit(&(owner->snapshotarr), PointerGetDatum(NULL));
+	ResourceArrayInit(&(owner->dsmarr), PointerGetDatum(NULL));
+	ResourceArrayInit(&(owner->bufferarr), BufferGetDatum(InvalidBuffer));
+	ResourceArrayInit(&(owner->filearr), FileGetDatum(-1));
 
 	return owner;
 }
resource-owner-optimization-v5-step2b.patchtext/x-patchDownload
diff --git a/src/backend/access/hash/hashfunc.c b/src/backend/access/hash/hashfunc.c
index dabaf5a..39f3115 100644
--- a/src/backend/access/hash/hashfunc.c
+++ b/src/backend/access/hash/hashfunc.c
@@ -297,6 +297,9 @@ hashvarlena(PG_FUNCTION_ARGS)
  * of 2.  There is no need to do mod a prime (mod is sooo slow!).
  * If you need less than 32 bits, use a bitmask.
  *
+ * This procedure never fails. Its important. Some code notably ResourceOwner
+ * relies on this.
+ *
  * Note: we could easily change this function to return a 64-bit hash value
  * by using the final values of both b and c.  b is perhaps a little less
  * well mixed than c, however.
diff --git a/src/backend/utils/resowner/resowner.c b/src/backend/utils/resowner/resowner.c
index 69217b5..f47069b 100644
--- a/src/backend/utils/resowner/resowner.c
+++ b/src/backend/utils/resowner/resowner.c
@@ -37,17 +37,35 @@
  * which should be called before corresponding ResourceOwnerRemember* calls
  * (see below). Internally each type of resource is stored in separate
  * ResourceArray.
+ *
+ * There are two major reasons for using ResourceArray instead of, say,
+ * regular C arrays.
+ *
+ * Firstly we would like to prevent code duplication. For instance
+ * ResourceArray provides generic Remember/Forget/Enlarge procedures, so
+ * corresponding ResourceOwner* procedures are just a typesafe wrappers for
+ * these procedures.
+ *
+ * Secondly ResourceArray must be more efficient than regular C array.
+ * Current implementation in general could be considered a hash table. It has
+ * O(1) complexity of both Remember and Forget procedures.
  */
 typedef struct ResourceArray
 {
 	Datum	   *itemsarr;		/* buffer for storing values */
+	Datum		invalidval;		/* value that is considered invalid */
 	uint32		capacity;		/* capacity of array */
 	uint32		nitems;			/* how many items is stored in items array */
+	uint32		maxitems;		/* precalculated RESARRAY_MAX_ITEMS(capacity) */
+	uint32		lastidx;		/* index of last item returned by GetAny */
 }	ResourceArray;
 
 /*
  * This number is used as initial size of resource array. If given number of
  * items is not enough, we double array size and reallocate memory.
+ *
+ * Should be power of two since we use (arrsize - 1) as mask for hash value.
+ *
  */
 #define RESARRAY_INIT_SIZE 16
 
@@ -64,14 +82,27 @@ typedef struct ResourceArray
 #define DatumGetBuffer(datum)((Buffer)(datum))
 
 /*
+ * How many items could be stored in a resource array of given capacity. If
+ * this number is reached we need to resize an array to prevent hash collisions.
+ *
+ * This computation actually costs only two additions and one binary shift.
+ */
+#define RESARRAY_MAX_ITEMS(capacity) ((capacity)*3/4)
+
+/*
  * Initialize ResourceArray
  */
 static void
-ResourceArrayInit(ResourceArray * resarr)
+ResourceArrayInit(ResourceArray * resarr, Datum invalidval)
 {
 	Assert(resarr->itemsarr == NULL);
 	Assert(resarr->capacity == 0);
 	Assert(resarr->nitems == 0);
+	Assert(resarr->maxitems == 0);
+	Assert(resarr->invalidval == 0);
+	Assert(resarr->lastidx == 0);
+
+	resarr->invalidval = invalidval;
 }
 
 /*
@@ -82,11 +113,24 @@ ResourceArrayInit(ResourceArray * resarr)
 static void
 ResourceArrayAdd(ResourceArray * resarr, Datum data)
 {
+	Datum		idx;
+	Datum		mask = resarr->capacity - 1;
+
+	Assert(resarr->maxitems > resarr->nitems);
 	Assert(resarr->capacity > 0);
 	Assert(resarr->itemsarr != NULL);
-	Assert(resarr->nitems < resarr->capacity);
+	Assert(data != resarr->invalidval);
+
+	idx = hash_any((void *) &data, sizeof(data)) & mask;
+
+	while (true)
+	{
+		if (resarr->itemsarr[idx] == resarr->invalidval)
+			break;
+		idx = (idx + 1) & mask;
+	}
 
-	resarr->itemsarr[resarr->nitems] = data;
+	resarr->itemsarr[idx] = data;
 	resarr->nitems++;
 }
 
@@ -98,24 +142,24 @@ ResourceArrayAdd(ResourceArray * resarr, Datum data)
 static bool
 ResourceArrayRemove(ResourceArray * resarr, Datum data)
 {
-	int			i,
-				j,
-				lastidx;
+	uint32		i;
+	Datum		idx;
+	Datum		mask = resarr->capacity - 1;
 
 	Assert(resarr->capacity > 0);
 	Assert(resarr->itemsarr != NULL);
+	Assert(data != resarr->invalidval);
 
-	lastidx = ((int) resarr->nitems) - 1;
-
-	for (i = lastidx; i >= 0; i--)
+	idx = hash_any((void *) &data, sizeof(data)) & mask;
+	for (i = 0; i < resarr->capacity; i++)
 	{
-		if (resarr->itemsarr[i] == data)
+		if (resarr->itemsarr[idx] == data)
 		{
-			for (j = i; j < lastidx; j++)
-				resarr->itemsarr[j] = resarr->itemsarr[j + 1];
+			resarr->itemsarr[idx] = resarr->invalidval;
 			resarr->nitems--;
 			return true;
 		}
+		idx = (idx + 1) & mask;
 	}
 
 	return false;
@@ -131,27 +175,33 @@ static void
 ResourceArrayEnlarge(ResourceArray * resarr)
 {
 	uint32		i,
-				oldcap,
-				oldnitems;
+				oldcap;
 	Datum	   *olditemsarr;
 
-	if (resarr->nitems < resarr->capacity)
+	if (resarr->nitems < resarr->maxitems)
 		return;					/* nothing to do */
 
 	olditemsarr = resarr->itemsarr;
 	oldcap = resarr->capacity;
-	oldnitems = resarr->nitems;
 
 	resarr->capacity = oldcap > 0 ? oldcap * 2 : RESARRAY_INIT_SIZE;
 	resarr->itemsarr = (Datum *)
 		MemoryContextAlloc(TopMemoryContext,
 						   resarr->capacity * sizeof(Datum));
+	resarr->maxitems = RESARRAY_MAX_ITEMS(resarr->capacity);
 	resarr->nitems = 0;
 
+	for (i = 0; i < resarr->capacity; i++)
+		resarr->itemsarr[i] = resarr->invalidval;
+
 	if (olditemsarr != NULL)
 	{
-		for (i = 0; i < oldnitems; i++)
-			ResourceArrayAdd(resarr, olditemsarr[i]);
+		while (oldcap > 0)
+		{
+			oldcap--;
+			if (olditemsarr[oldcap] != resarr->invalidval)
+				ResourceArrayAdd(resarr, olditemsarr[oldcap]);
+		}
 		pfree(olditemsarr);
 	}
 }
@@ -164,12 +214,24 @@ ResourceArrayEnlarge(ResourceArray * resarr)
 static bool
 ResourceArrayGetAny(ResourceArray * resarr, Datum *out)
 {
+	uint32		mask;
+
 	if (resarr->nitems == 0)
 		return false;
 
 	Assert(resarr->capacity > 0);
+	mask = resarr->capacity - 1;
+
+	for (;;)
+	{
+		resarr->lastidx = resarr->lastidx & mask;
+		if (resarr->itemsarr[resarr->lastidx] != resarr->invalidval)
+			break;
+
+		resarr->lastidx++;
+	}
 
-	*out = resarr->itemsarr[resarr->nitems - 1];
+	*out = resarr->itemsarr[resarr->lastidx];
 	return true;
 }
 
@@ -182,6 +244,7 @@ ResourceArrayFree(ResourceArray * resarr)
 	Assert(resarr->nitems == 0);
 
 	resarr->capacity = 0;
+	resarr->maxitems = 0;
 
 	if (!resarr->itemsarr)
 		return;
@@ -299,15 +362,15 @@ ResourceOwnerCreate(ResourceOwner parent, const char *name)
 		parent->firstchild = owner;
 	}
 
-	ResourceArrayInit(&(owner->catrefarr));
-	ResourceArrayInit(&(owner->catlistrefarr));
-	ResourceArrayInit(&(owner->relrefarr));
-	ResourceArrayInit(&(owner->planrefarr));
-	ResourceArrayInit(&(owner->tupdescarr));
-	ResourceArrayInit(&(owner->snapshotarr));
-	ResourceArrayInit(&(owner->dsmarr));
-	ResourceArrayInit(&(owner->bufferarr));
-	ResourceArrayInit(&(owner->filearr));
+	ResourceArrayInit(&(owner->catrefarr), PointerGetDatum(NULL));
+	ResourceArrayInit(&(owner->catlistrefarr), PointerGetDatum(NULL));
+	ResourceArrayInit(&(owner->relrefarr), PointerGetDatum(NULL));
+	ResourceArrayInit(&(owner->planrefarr), PointerGetDatum(NULL));
+	ResourceArrayInit(&(owner->tupdescarr), PointerGetDatum(NULL));
+	ResourceArrayInit(&(owner->snapshotarr), PointerGetDatum(NULL));
+	ResourceArrayInit(&(owner->dsmarr), PointerGetDatum(NULL));
+	ResourceArrayInit(&(owner->bufferarr), BufferGetDatum(InvalidBuffer));
+	ResourceArrayInit(&(owner->filearr), FileGetDatum(-1));
 
 	return owner;
 }
#17Tom Lane
tgl@sss.pgh.pa.us
In reply to: Aleksander Alekseev (#16)
Re: Patch: ResourceOwner optimization for tables with many partitions

Aleksander Alekseev <a.alekseev@postgrespro.ru> writes:

I compared two implementations - "always use hashing" (step2a.path) and
"use hashing only for large arrays" (step2b.path). Both patches give
the same performance according to benchmark I described in a first
message of this thread.

Um, that's not too surprising, because they're exactly the same patch?

regards, tom lane

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

#18Aleksander Alekseev
a.alekseev@postgrespro.ru
In reply to: Tom Lane (#17)
1 attachment(s)
Re: Patch: ResourceOwner optimization for tables with many partitions

Um, that's not too surprising, because they're exactly the same patch?

Wrong diff. Here is correct one.

Everything else is right. I just re-checked :)

step2a:

number of transactions actually processed: 16325
latency average: 49.008 ms
latency stddev: 8.780 ms
tps = 163.182594 (including connections establishing)
tps = 163.189818 (excluding connections establishing)

step2b-fixed:

number of transactions actually processed: 16374
latency average: 48.867 ms
latency stddev: 8.223 ms
tps = 163.658269 (including connections establishing)
tps = 163.666318 (excluding connections establishing)

Attachments:

resource-owner-optimization-v5-step2b-fixed.patchtext/x-patchDownload
diff --git a/src/backend/access/hash/hashfunc.c b/src/backend/access/hash/hashfunc.c
index dabaf5a..39f3115 100644
--- a/src/backend/access/hash/hashfunc.c
+++ b/src/backend/access/hash/hashfunc.c
@@ -297,6 +297,9 @@ hashvarlena(PG_FUNCTION_ARGS)
  * of 2.  There is no need to do mod a prime (mod is sooo slow!).
  * If you need less than 32 bits, use a bitmask.
  *
+ * This procedure never fails. Its important. Some code notably ResourceOwner
+ * relies on this.
+ *
  * Note: we could easily change this function to return a 64-bit hash value
  * by using the final values of both b and c.  b is perhaps a little less
  * well mixed than c, however.
diff --git a/src/backend/utils/resowner/resowner.c b/src/backend/utils/resowner/resowner.c
index 69217b5..02319ef 100644
--- a/src/backend/utils/resowner/resowner.c
+++ b/src/backend/utils/resowner/resowner.c
@@ -37,17 +37,35 @@
  * which should be called before corresponding ResourceOwnerRemember* calls
  * (see below). Internally each type of resource is stored in separate
  * ResourceArray.
+ *
+ * There are two major reasons for using ResourceArray instead of, say,
+ * regular C arrays.
+ *
+ * Firstly we would like to prevent code duplication. For instance
+ * ResourceArray provides generic Remember/Forget/Enlarge procedures, so
+ * corresponding ResourceOwner* procedures are just a typesafe wrappers for
+ * these procedures.
+ *
+ * Secondly ResourceArray must be more efficient than regular C array.
+ * Current implementation in general could be considered a hash table. It has
+ * O(1) complexity of both Remember and Forget procedures.
  */
 typedef struct ResourceArray
 {
 	Datum	   *itemsarr;		/* buffer for storing values */
+	Datum		invalidval;		/* value that is considered invalid */
 	uint32		capacity;		/* capacity of array */
 	uint32		nitems;			/* how many items is stored in items array */
+	uint32		maxitems;		/* precalculated RESARRAY_MAX_ITEMS(capacity) */
+	uint32		lastidx;		/* index of last item returned by GetAny */
 }	ResourceArray;
 
 /*
  * This number is used as initial size of resource array. If given number of
  * items is not enough, we double array size and reallocate memory.
+ *
+ * Should be power of two since we use (arrsize - 1) as mask for hash value.
+ *
  */
 #define RESARRAY_INIT_SIZE 16
 
@@ -64,14 +82,27 @@ typedef struct ResourceArray
 #define DatumGetBuffer(datum)((Buffer)(datum))
 
 /*
+ * How many items could be stored in a resource array of given capacity. If
+ * this number is reached we need to resize an array to prevent hash collisions.
+ *
+ * This computation actually costs only two additions and one binary shift.
+ */
+#define RESARRAY_MAX_ITEMS(capacity) ((capacity)*3/4)
+
+/*
  * Initialize ResourceArray
  */
 static void
-ResourceArrayInit(ResourceArray * resarr)
+ResourceArrayInit(ResourceArray * resarr, Datum invalidval)
 {
 	Assert(resarr->itemsarr == NULL);
 	Assert(resarr->capacity == 0);
 	Assert(resarr->nitems == 0);
+	Assert(resarr->maxitems == 0);
+	Assert(resarr->invalidval == 0);
+	Assert(resarr->lastidx == 0);
+
+	resarr->invalidval = invalidval;
 }
 
 /*
@@ -82,11 +113,32 @@ ResourceArrayInit(ResourceArray * resarr)
 static void
 ResourceArrayAdd(ResourceArray * resarr, Datum data)
 {
+	Datum		idx;
+	Datum		mask = resarr->capacity - 1;
+
+	Assert(resarr->maxitems > resarr->nitems);
 	Assert(resarr->capacity > 0);
 	Assert(resarr->itemsarr != NULL);
-	Assert(resarr->nitems < resarr->capacity);
+	Assert(data != resarr->invalidval);
+
+	/* For small arrays don't calculate hashes */
+	if (resarr->capacity == RESARRAY_INIT_SIZE)
+	{
+		resarr->itemsarr[resarr->nitems] = data;
+		resarr->nitems++;
+		return;
+	}
+
+	idx = hash_any((void *) &data, sizeof(data)) & mask;
+
+	while (true)
+	{
+		if (resarr->itemsarr[idx] == resarr->invalidval)
+			break;
+		idx = (idx + 1) & mask;
+	}
 
-	resarr->itemsarr[resarr->nitems] = data;
+	resarr->itemsarr[idx] = data;
 	resarr->nitems++;
 }
 
@@ -98,24 +150,45 @@ ResourceArrayAdd(ResourceArray * resarr, Datum data)
 static bool
 ResourceArrayRemove(ResourceArray * resarr, Datum data)
 {
-	int			i,
+	uint32		i,
 				j,
 				lastidx;
+	Datum		idx;
+	Datum		mask = resarr->capacity - 1;
 
 	Assert(resarr->capacity > 0);
 	Assert(resarr->itemsarr != NULL);
+	Assert(data != resarr->invalidval);
+
+	/* For small arrays don't calculate hashes */
+	if (resarr->capacity == RESARRAY_INIT_SIZE)
+	{
+		lastidx = resarr->nitems - 1;
+
+		for (i = lastidx; i >= 0; i--)
+		{
+			if (resarr->itemsarr[i] == data)
+			{
+				for (j = i; j < lastidx; j++)
+					resarr->itemsarr[j] = resarr->itemsarr[j + 1];
+				resarr->nitems--;
+				return true;
+			}
+		}
 
-	lastidx = ((int) resarr->nitems) - 1;
+		return false;
+	}
 
-	for (i = lastidx; i >= 0; i--)
+	idx = hash_any((void *) &data, sizeof(data)) & mask;
+	for (i = 0; i < resarr->capacity; i++)
 	{
-		if (resarr->itemsarr[i] == data)
+		if (resarr->itemsarr[idx] == data)
 		{
-			for (j = i; j < lastidx; j++)
-				resarr->itemsarr[j] = resarr->itemsarr[j + 1];
+			resarr->itemsarr[idx] = resarr->invalidval;
 			resarr->nitems--;
 			return true;
 		}
+		idx = (idx + 1) & mask;
 	}
 
 	return false;
@@ -131,27 +204,33 @@ static void
 ResourceArrayEnlarge(ResourceArray * resarr)
 {
 	uint32		i,
-				oldcap,
-				oldnitems;
+				oldcap;
 	Datum	   *olditemsarr;
 
-	if (resarr->nitems < resarr->capacity)
+	if (resarr->nitems < resarr->maxitems)
 		return;					/* nothing to do */
 
 	olditemsarr = resarr->itemsarr;
 	oldcap = resarr->capacity;
-	oldnitems = resarr->nitems;
 
 	resarr->capacity = oldcap > 0 ? oldcap * 2 : RESARRAY_INIT_SIZE;
 	resarr->itemsarr = (Datum *)
 		MemoryContextAlloc(TopMemoryContext,
 						   resarr->capacity * sizeof(Datum));
+	resarr->maxitems = RESARRAY_MAX_ITEMS(resarr->capacity);
 	resarr->nitems = 0;
 
+	for (i = 0; i < resarr->capacity; i++)
+		resarr->itemsarr[i] = resarr->invalidval;
+
 	if (olditemsarr != NULL)
 	{
-		for (i = 0; i < oldnitems; i++)
-			ResourceArrayAdd(resarr, olditemsarr[i]);
+		while (oldcap > 0)
+		{
+			oldcap--;
+			if (olditemsarr[oldcap] != resarr->invalidval)
+				ResourceArrayAdd(resarr, olditemsarr[oldcap]);
+		}
 		pfree(olditemsarr);
 	}
 }
@@ -164,12 +243,24 @@ ResourceArrayEnlarge(ResourceArray * resarr)
 static bool
 ResourceArrayGetAny(ResourceArray * resarr, Datum *out)
 {
+	uint32		mask;
+
 	if (resarr->nitems == 0)
 		return false;
 
 	Assert(resarr->capacity > 0);
+	mask = resarr->capacity - 1;
+
+	for (;;)
+	{
+		resarr->lastidx = resarr->lastidx & mask;
+		if (resarr->itemsarr[resarr->lastidx] != resarr->invalidval)
+			break;
+
+		resarr->lastidx++;
+	}
 
-	*out = resarr->itemsarr[resarr->nitems - 1];
+	*out = resarr->itemsarr[resarr->lastidx];
 	return true;
 }
 
@@ -182,6 +273,7 @@ ResourceArrayFree(ResourceArray * resarr)
 	Assert(resarr->nitems == 0);
 
 	resarr->capacity = 0;
+	resarr->maxitems = 0;
 
 	if (!resarr->itemsarr)
 		return;
@@ -299,15 +391,15 @@ ResourceOwnerCreate(ResourceOwner parent, const char *name)
 		parent->firstchild = owner;
 	}
 
-	ResourceArrayInit(&(owner->catrefarr));
-	ResourceArrayInit(&(owner->catlistrefarr));
-	ResourceArrayInit(&(owner->relrefarr));
-	ResourceArrayInit(&(owner->planrefarr));
-	ResourceArrayInit(&(owner->tupdescarr));
-	ResourceArrayInit(&(owner->snapshotarr));
-	ResourceArrayInit(&(owner->dsmarr));
-	ResourceArrayInit(&(owner->bufferarr));
-	ResourceArrayInit(&(owner->filearr));
+	ResourceArrayInit(&(owner->catrefarr), PointerGetDatum(NULL));
+	ResourceArrayInit(&(owner->catlistrefarr), PointerGetDatum(NULL));
+	ResourceArrayInit(&(owner->relrefarr), PointerGetDatum(NULL));
+	ResourceArrayInit(&(owner->planrefarr), PointerGetDatum(NULL));
+	ResourceArrayInit(&(owner->tupdescarr), PointerGetDatum(NULL));
+	ResourceArrayInit(&(owner->snapshotarr), PointerGetDatum(NULL));
+	ResourceArrayInit(&(owner->dsmarr), PointerGetDatum(NULL));
+	ResourceArrayInit(&(owner->bufferarr), BufferGetDatum(InvalidBuffer));
+	ResourceArrayInit(&(owner->filearr), FileGetDatum(-1));
 
 	return owner;
 }
#19Tom Lane
tgl@sss.pgh.pa.us
In reply to: Aleksander Alekseev (#18)
Re: Patch: ResourceOwner optimization for tables with many partitions

Aleksander Alekseev <a.alekseev@postgrespro.ru> writes:

Um, that's not too surprising, because they're exactly the same patch?

Wrong diff. Here is correct one.

This still had quite a few bugs, but I fixed them (hope I caught
everything) and pushed it.

I did some performance testing of the ResourceArray code in isolation.
It appears that you need 100 or so elements before the hash code path
is competitive with the array path, so I increased the cutover point
quite a bit from what you had.

regards, tom lane

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

#20Aleksander Alekseev
a.alekseev@postgrespro.ru
In reply to: Tom Lane (#19)
Re: Patch: ResourceOwner optimization for tables with many partitions

Hello, Tom.

I'm a bit concerned regarding assumption that sizeof int never exceeds 4
bytes. While this could be true today for most C compilers, standard
[1]: http://www.open-std.org/jtc1/sc22/wg14/www/docs/n1124.pdf

StaticAssertStmt(sizeof(int) <= sizeof(int32),
"int size exceeds int32 size");

It costs nothing but could save a lot of time (not mentioning data
loss) some unlucky user.

[1]: http://www.open-std.org/jtc1/sc22/wg14/www/docs/n1124.pdf
[2]: http://www.open-std.org/jtc1/sc22/wg14/www/docs/n1570.pdf

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

#21Robert Haas
robertmhaas@gmail.com
In reply to: Aleksander Alekseev (#20)
Re: Patch: ResourceOwner optimization for tables with many partitions

On Wed, Jan 27, 2016 at 3:57 AM, Aleksander Alekseev
<a.alekseev@postgrespro.ru> wrote:

I'm a bit concerned regarding assumption that sizeof int never exceeds 4
bytes. While this could be true today for most C compilers, standard
[1][2] doesn't guarantee that. Perhaps we should add something like:

StaticAssertStmt(sizeof(int) <= sizeof(int32),
"int size exceeds int32 size");

I suspect that if this ever failed to be true, resowner.c would not be
the only thing having problems.

--
Robert Haas
EnterpriseDB: http://www.enterprisedb.com
The Enterprise PostgreSQL Company

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