lazy vxid locks, v2

Started by Robert Haasover 14 years ago5 messages
#1Robert Haas
robertmhaas@gmail.com

Here is an updated version of the "lazy vxid locks" patch [1]https://commitfest.postgresql.org/action/patch_view?id=585, which
applies over the latest "reduce the overhead of frequent table
locks"[2]https://commitfest.postgresql.org/action/patch_view?id=572 patch.

[1]: https://commitfest.postgresql.org/action/patch_view?id=585
[2]: https://commitfest.postgresql.org/action/patch_view?id=572

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

#2Robert Haas
robertmhaas@gmail.com
In reply to: Robert Haas (#1)
1 attachment(s)
Re: lazy vxid locks, v2

On Tue, Jul 5, 2011 at 1:13 PM, Robert Haas <robertmhaas@gmail.com> wrote:

Here is an updated version of the "lazy vxid locks" patch [1], which
applies over the latest "reduce the overhead of frequent table
locks"[2] patch.

[1] https://commitfest.postgresql.org/action/patch_view?id=585
[2] https://commitfest.postgresql.org/action/patch_view?id=572

And then I forgot the attachment.

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

Attachments:

lazyvxid-v2.patchapplication/octet-stream; name=lazyvxid-v2.patchDownload
diff --git a/src/backend/access/transam/xact.c b/src/backend/access/transam/xact.c
index 7f01a83..f0c80f1 100644
--- a/src/backend/access/transam/xact.c
+++ b/src/backend/access/transam/xact.c
@@ -1725,14 +1725,7 @@ StartTransaction(void)
 	/*
 	 * Lock the virtual transaction id before we announce it in the proc array
 	 */
-	VirtualXactLockTableInsert(vxid);
-
-	/*
-	 * Advertise it in the proc array.	We assume assignment of
-	 * LocalTransactionID is atomic, and the backendId should be set already.
-	 */
-	Assert(MyProc->backendId == vxid.backendId);
-	MyProc->lxid = vxid.localTransactionId;
+	VirtualXactLockInitialize(vxid);
 
 	TRACE_POSTGRESQL_TRANSACTION_START(vxid.localTransactionId);
 
@@ -1878,6 +1871,7 @@ CommitTransaction(void)
 	 * must be done _before_ releasing locks we hold and _after_
 	 * RecordTransactionCommit.
 	 */
+	VirtualXactLockCleanup();
 	ProcArrayEndTransaction(MyProc, latestXid);
 
 	/*
@@ -2140,6 +2134,7 @@ PrepareTransaction(void)
 	 * done *after* the prepared transaction has been marked valid, else
 	 * someone may think it is unlocked and recyclable.
 	 */
+	VirtualXactLockCleanup();
 	ProcArrayClearTransaction(MyProc);
 
 	/*
@@ -2306,6 +2301,7 @@ AbortTransaction(void)
 	 * must be done _before_ releasing locks we hold and _after_
 	 * RecordTransactionAbort.
 	 */
+	VirtualXactLockCleanup();
 	ProcArrayEndTransaction(MyProc, latestXid);
 
 	/*
diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c
index b7c021d..a583399 100644
--- a/src/backend/commands/indexcmds.c
+++ b/src/backend/commands/indexcmds.c
@@ -482,7 +482,7 @@ DefineIndex(RangeVar *heapRelation,
 
 	while (VirtualTransactionIdIsValid(*old_lockholders))
 	{
-		VirtualXactLockTableWait(*old_lockholders);
+		VirtualXactLock(*old_lockholders, true);
 		old_lockholders++;
 	}
 
@@ -568,7 +568,7 @@ DefineIndex(RangeVar *heapRelation,
 
 	while (VirtualTransactionIdIsValid(*old_lockholders))
 	{
-		VirtualXactLockTableWait(*old_lockholders);
+		VirtualXactLock(*old_lockholders, true);
 		old_lockholders++;
 	}
 
@@ -665,7 +665,7 @@ DefineIndex(RangeVar *heapRelation,
 		}
 
 		if (VirtualTransactionIdIsValid(old_snapshots[i]))
-			VirtualXactLockTableWait(old_snapshots[i]);
+			VirtualXactLock(old_snapshots[i], true);
 	}
 
 	/*
diff --git a/src/backend/postmaster/autovacuum.c b/src/backend/postmaster/autovacuum.c
index 483a829..8b233ef 100644
--- a/src/backend/postmaster/autovacuum.c
+++ b/src/backend/postmaster/autovacuum.c
@@ -1992,7 +1992,7 @@ do_autovacuum(void)
 			backendID = GetTempNamespaceBackendId(classForm->relnamespace);
 
 			/* We just ignore it if the owning backend is still active */
-			if (backendID == MyBackendId || !BackendIdIsActive(backendID))
+			if (backendID == MyBackendId || BackendIdGetProc(backendID) == NULL)
 			{
 				/*
 				 * We found an orphan temp table (which was probably left
diff --git a/src/backend/storage/ipc/procarray.c b/src/backend/storage/ipc/procarray.c
index e7593fa..2174061 100644
--- a/src/backend/storage/ipc/procarray.c
+++ b/src/backend/storage/ipc/procarray.c
@@ -363,7 +363,6 @@ ProcArrayEndTransaction(PGPROC *proc, TransactionId latestXid)
 		LWLockAcquire(ProcArrayLock, LW_EXCLUSIVE);
 
 		proc->xid = InvalidTransactionId;
-		proc->lxid = InvalidLocalTransactionId;
 		proc->xmin = InvalidTransactionId;
 		/* must be cleared with xid/xmin: */
 		proc->vacuumFlags &= ~PROC_VACUUM_STATE_MASK;
@@ -390,7 +389,6 @@ ProcArrayEndTransaction(PGPROC *proc, TransactionId latestXid)
 		 */
 		Assert(!TransactionIdIsValid(proc->xid));
 
-		proc->lxid = InvalidLocalTransactionId;
 		proc->xmin = InvalidTransactionId;
 		/* must be cleared with xid/xmin: */
 		proc->vacuumFlags &= ~PROC_VACUUM_STATE_MASK;
@@ -421,7 +419,6 @@ ProcArrayClearTransaction(PGPROC *proc)
 	 * ProcArray.
 	 */
 	proc->xid = InvalidTransactionId;
-	proc->lxid = InvalidLocalTransactionId;
 	proc->xmin = InvalidTransactionId;
 	proc->recoveryConflictPending = false;
 
diff --git a/src/backend/storage/ipc/sinvaladt.c b/src/backend/storage/ipc/sinvaladt.c
index 4f446aa..807b7be 100644
--- a/src/backend/storage/ipc/sinvaladt.c
+++ b/src/backend/storage/ipc/sinvaladt.c
@@ -139,6 +139,7 @@ typedef struct ProcState
 {
 	/* procPid is zero in an inactive ProcState array entry. */
 	pid_t		procPid;		/* PID of backend, for signaling */
+	PGPROC	   *proc;			/* PGPROC of backend */
 	/* nextMsgNum is meaningless if procPid == 0 or resetState is true. */
 	int			nextMsgNum;		/* next message number to read */
 	bool		resetState;		/* backend needs to reset its state */
@@ -245,6 +246,7 @@ CreateSharedInvalidationState(void)
 	for (i = 0; i < shmInvalBuffer->maxBackends; i++)
 	{
 		shmInvalBuffer->procState[i].procPid = 0;		/* inactive */
+		shmInvalBuffer->procState[i].proc = NULL;
 		shmInvalBuffer->procState[i].nextMsgNum = 0;	/* meaningless */
 		shmInvalBuffer->procState[i].resetState = false;
 		shmInvalBuffer->procState[i].signaled = false;
@@ -313,6 +315,7 @@ SharedInvalBackendInit(bool sendOnly)
 
 	/* mark myself active, with all extant messages already read */
 	stateP->procPid = MyProcPid;
+	stateP->proc = MyProc;
 	stateP->nextMsgNum = segP->maxMsgNum;
 	stateP->resetState = false;
 	stateP->signaled = false;
@@ -352,6 +355,7 @@ CleanupInvalidationState(int status, Datum arg)
 
 	/* Mark myself inactive */
 	stateP->procPid = 0;
+	stateP->proc = NULL;
 	stateP->nextMsgNum = 0;
 	stateP->resetState = false;
 	stateP->signaled = false;
@@ -368,13 +372,16 @@ CleanupInvalidationState(int status, Datum arg)
 }
 
 /*
- * BackendIdIsActive
- *		Test if the given backend ID is currently assigned to a process.
+ * BackendIdGetProc
+ *		Get the PGPROC structure for a backend, given the backend ID.
+ *		The result may be out of date arbitrarily quickly, so the caller
+ *		must be careful about how this information is used.  NULL is
+ *		returned if the backend is not active.
  */
-bool
-BackendIdIsActive(int backendID)
+PGPROC *
+BackendIdGetProc(int backendID)
 {
-	bool		result;
+	PGPROC	   *result = NULL;
 	SISeg	   *segP = shmInvalBuffer;
 
 	/* Need to lock out additions/removals of backends */
@@ -384,10 +391,8 @@ BackendIdIsActive(int backendID)
 	{
 		ProcState  *stateP = &segP->procState[backendID - 1];
 
-		result = (stateP->procPid != 0);
+		result = stateP->proc;
 	}
-	else
-		result = false;
 
 	LWLockRelease(SInvalWriteLock);
 
diff --git a/src/backend/storage/ipc/standby.c b/src/backend/storage/ipc/standby.c
index 75b5ab4..3456e4a 100644
--- a/src/backend/storage/ipc/standby.c
+++ b/src/backend/storage/ipc/standby.c
@@ -82,7 +82,7 @@ InitRecoveryTransactionEnvironment(void)
 	 */
 	vxid.backendId = MyBackendId;
 	vxid.localTransactionId = GetNextLocalTransactionId();
-	VirtualXactLockTableInsert(vxid);
+	VirtualXactLockInitialize(vxid);
 
 	standbyState = STANDBY_INITIALIZED;
 }
@@ -201,7 +201,7 @@ ResolveRecoveryConflictWithVirtualXIDs(VirtualTransactionId *waitlist,
 		standbyWait_us = STANDBY_INITIAL_WAIT_US;
 
 		/* wait until the virtual xid is gone */
-		while (!ConditionalVirtualXactLockTableWait(*waitlist))
+		while (!VirtualXactLock(*waitlist, false))
 		{
 			/*
 			 * Report via ps if we have been waiting for more than 500 msec
diff --git a/src/backend/storage/lmgr/lmgr.c b/src/backend/storage/lmgr/lmgr.c
index 859b385..9d0994e 100644
--- a/src/backend/storage/lmgr/lmgr.c
+++ b/src/backend/storage/lmgr/lmgr.c
@@ -514,70 +514,6 @@ ConditionalXactLockTableWait(TransactionId xid)
 	return true;
 }
 
-
-/*
- *		VirtualXactLockTableInsert
- *
- * Insert a lock showing that the given virtual transaction ID is running ---
- * this is done at main transaction start when its VXID is assigned.
- * The lock can then be used to wait for the transaction to finish.
- */
-void
-VirtualXactLockTableInsert(VirtualTransactionId vxid)
-{
-	LOCKTAG		tag;
-
-	Assert(VirtualTransactionIdIsValid(vxid));
-
-	SET_LOCKTAG_VIRTUALTRANSACTION(tag, vxid);
-
-	(void) LockAcquire(&tag, ExclusiveLock, false, false);
-}
-
-/*
- *		VirtualXactLockTableWait
- *
- * Waits until the lock on the given VXID is released, which shows that
- * the top-level transaction owning the VXID has ended.
- */
-void
-VirtualXactLockTableWait(VirtualTransactionId vxid)
-{
-	LOCKTAG		tag;
-
-	Assert(VirtualTransactionIdIsValid(vxid));
-
-	SET_LOCKTAG_VIRTUALTRANSACTION(tag, vxid);
-
-	(void) LockAcquire(&tag, ShareLock, false, false);
-
-	LockRelease(&tag, ShareLock, false);
-}
-
-/*
- *		ConditionalVirtualXactLockTableWait
- *
- * As above, but only lock if we can get the lock without blocking.
- * Returns TRUE if the lock was acquired.
- */
-bool
-ConditionalVirtualXactLockTableWait(VirtualTransactionId vxid)
-{
-	LOCKTAG		tag;
-
-	Assert(VirtualTransactionIdIsValid(vxid));
-
-	SET_LOCKTAG_VIRTUALTRANSACTION(tag, vxid);
-
-	if (LockAcquire(&tag, ShareLock, false, true) == LOCKACQUIRE_NOT_AVAIL)
-		return false;
-
-	LockRelease(&tag, ShareLock, false);
-
-	return true;
-}
-
-
 /*
  *		LockDatabaseObject
  *
diff --git a/src/backend/storage/lmgr/lock.c b/src/backend/storage/lmgr/lock.c
index e809a58..665f898 100644
--- a/src/backend/storage/lmgr/lock.c
+++ b/src/backend/storage/lmgr/lock.c
@@ -38,6 +38,7 @@
 #include "miscadmin.h"
 #include "pg_trace.h"
 #include "pgstat.h"
+#include "storage/sinvaladt.h"
 #include "storage/standby.h"
 #include "utils/memutils.h"
 #include "utils/ps_status.h"
@@ -138,6 +139,9 @@ static int			FastPathLocalUseCount = 0;
 #define FAST_PATH_CHECK_LOCKMODE(proc, n, l) \
 	 ((proc)->fpLockBits & (UINT64CONST(1) << FAST_PATH_BIT_POSITION(n, l)))
 
+#define FAST_PATH_DEFER_VXID_LOCK		\
+	(UINT64CONST(1) << (FAST_PATH_BITS_PER_SLOT * FP_LOCK_SLOTS_PER_BACKEND))
+
 /*
  * The fast-path lock mechanism is concerned only with relation locks on
  * unshared relations by backends bound to a database.  The fast-path
@@ -3531,3 +3535,154 @@ lock_twophase_postabort(TransactionId xid, uint16 info,
 {
 	lock_twophase_postcommit(xid, info, recdata, len);
 }
+
+/*
+ *		VirtualXactLockInitialize
+ *
+ *		We set a flag in MyProc->fpLockState indicating that we have a
+ *		"deferred" VXID lock.  That is, we have an active VXID, but we
+ *		haven't actually taken an exclusive lock on it.  VXID locks are
+ *		rarely waited for, so it makes sense to defer the actual lock
+ *		acquisition to the point when it's needed.  Another backend wishing
+ *		to wait on the lock can acquire the lock on our behalf and then
+ *		wait on it.  We'll figure it all out in VirtualXactLockCleanup().
+ *
+ *		We set lxid while holding the lock to guarantee that anyone who
+ *		sees the lxid set and subsequently takes our backendLock will also
+ *		see the FAST_PATH_DEFER_VXID lock bit set.
+ */
+void
+VirtualXactLockInitialize(VirtualTransactionId vxid)
+{
+	Assert(VirtualTransactionIdIsValid(vxid));
+
+	LWLockAcquire(MyProc->backendLock, LW_EXCLUSIVE);
+
+	Assert(MyProc->backendId == vxid.backendId);
+	MyProc->lxid = vxid.localTransactionId;
+	MyProc->fpLockBits |= FAST_PATH_DEFER_VXID_LOCK;
+
+	LWLockRelease(MyProc->backendLock);
+}
+
+/*
+ *		VirtualXactLockCleanup
+ *
+ *		Check whether a VXID lock has been materialized; if so, release it,
+ *		unblocking waiters.
+ */
+void
+VirtualXactLockCleanup()
+{
+	VirtualTransactionId	vxid;
+	bool	cleanup = false;
+
+	Assert(MyProc->backendId != InvalidBackendId);
+	Assert(MyProc->lxid != InvalidLocalTransactionId);
+
+	GET_VXID_FROM_PGPROC(vxid, *MyProc);
+
+	LWLockAcquire(MyProc->backendLock, LW_EXCLUSIVE);
+
+	if ((MyProc->fpLockBits & FAST_PATH_DEFER_VXID_LOCK) != 0)
+		MyProc->fpLockBits &= ~FAST_PATH_DEFER_VXID_LOCK;
+	else
+		cleanup = true;
+	MyProc->lxid = InvalidLocalTransactionId;
+
+	LWLockRelease(MyProc->backendLock);
+
+	/* If someone materialized the lock on our behalf, we must release it. */
+	if (cleanup)
+	{
+		LOCKTAG	locktag;
+
+		SET_LOCKTAG_VIRTUALTRANSACTION(locktag, vxid);
+		LockRefindAndRelease(LockMethods[DEFAULT_LOCKMETHOD], MyProc,
+							 &locktag, ExclusiveLock, false);
+	}	
+}
+
+/*
+ *		VirtualXactLock
+ *
+ * If wait = true, wait until the given VXID has been released, and then
+ * return true.
+ *
+ * If wait = false, just check whether the VXID is still running, and return
+ * true or false.
+ */
+bool
+VirtualXactLock(VirtualTransactionId vxid, bool wait)
+{
+	LOCKTAG		tag;
+	PGPROC	   *proc;
+
+	Assert(VirtualTransactionIdIsValid(vxid));
+
+	SET_LOCKTAG_VIRTUALTRANSACTION(tag, vxid);
+
+	/*
+	 * If a lock table entry must be made, this is the PGPROC on whose behalf
+	 * it must be done.  Note that the transaction might end or the PGPROC
+	 * might be reassigned to a new backend before we get around to examining
+	 * it, but it doesn't matter.  If we find upon examination that the
+	 * relevant lxid is no longer running here, that's enough to prove that
+	 * it's no longer running anywhere.
+	 */
+	proc = BackendIdGetProc(vxid.backendId);
+
+	/*
+	 * We must acquire this lock before checking the backendId and lxid
+	 * against the ones we're waiting for.  The target backend will only
+	 * set or clear lxid while holding this lock.
+	 */
+	LWLockAcquire(proc->backendLock, LW_EXCLUSIVE);
+
+	/* If the transaction has ended, our work here is done. */
+	if (proc->backendId != vxid.backendId || proc->lxid != vxid.localTransactionId)
+	{
+		LWLockRelease(proc->backendLock);
+		return true;
+	}
+
+	/*
+	 * If we aren't asked to wait, there's no need to set up a lock table
+	 * entry.  The transaction is still in progress, so just return false.
+	 */
+	if (!wait)
+	{
+		LWLockRelease(proc->backendLock);
+		return false;
+	}
+
+	/*
+	 * OK, we're going to need to sleep on the VXID.  But first, we must set
+	 * up the primary lock table entry, if needed.
+	 */
+	if ((proc->fpLockBits & FAST_PATH_DEFER_VXID_LOCK) != 0)
+	{
+		PROCLOCK   *proclock;
+		uint32		hashcode;
+
+		hashcode = LockTagHashCode(&tag);
+		proclock = SetupLockInTable(LockMethods[DEFAULT_LOCKMETHOD], proc,
+									&tag, hashcode, ExclusiveLock);
+		if (!proclock)
+			ereport(ERROR,
+					(errcode(ERRCODE_OUT_OF_MEMORY),
+					 errmsg("out of shared memory"),
+		  errhint("You might need to increase max_locks_per_transaction.")));
+		GrantLock(proclock->tag.myLock, proclock, ExclusiveLock);
+		proc->fpLockBits &= ~FAST_PATH_DEFER_VXID_LOCK;
+	}
+
+	/* Done with proc->fpLockBits */
+	LWLockRelease(proc->backendLock);
+
+	/* Time to wait. */
+	(void) LockAcquire(&tag, ShareLock, false, false);
+
+	LockRelease(&tag, ShareLock, false);
+	return true;
+}
diff --git a/src/include/storage/lmgr.h b/src/include/storage/lmgr.h
index bd44d92..340f6a3 100644
--- a/src/include/storage/lmgr.h
+++ b/src/include/storage/lmgr.h
@@ -56,11 +56,6 @@ extern void XactLockTableDelete(TransactionId xid);
 extern void XactLockTableWait(TransactionId xid);
 extern bool ConditionalXactLockTableWait(TransactionId xid);
 
-/* Lock a VXID (used to wait for a transaction to finish) */
-extern void VirtualXactLockTableInsert(VirtualTransactionId vxid);
-extern void VirtualXactLockTableWait(VirtualTransactionId vxid);
-extern bool ConditionalVirtualXactLockTableWait(VirtualTransactionId vxid);
-
 /* Lock a general object (other than a relation) of the current database */
 extern void LockDatabaseObject(Oid classid, Oid objid, uint16 objsubid,
 				   LOCKMODE lockmode);
diff --git a/src/include/storage/lock.h b/src/include/storage/lock.h
index b3aeef9..eb6b953 100644
--- a/src/include/storage/lock.h
+++ b/src/include/storage/lock.h
@@ -543,4 +543,9 @@ extern void DumpLocks(PGPROC *proc);
 extern void DumpAllLocks(void);
 #endif
 
+/* Lock a VXID (used to wait for a transaction to finish) */
+extern void VirtualXactLockInitialize(VirtualTransactionId vxid);
+extern void VirtualXactLockCleanup(void);
+extern bool VirtualXactLock(VirtualTransactionId vxid, bool wait);
+
 #endif   /* LOCK_H */
diff --git a/src/include/storage/sinvaladt.h b/src/include/storage/sinvaladt.h
index c703558..a61d696 100644
--- a/src/include/storage/sinvaladt.h
+++ b/src/include/storage/sinvaladt.h
@@ -22,6 +22,7 @@
 #ifndef SINVALADT_H
 #define SINVALADT_H
 
+#include "storage/proc.h"
 #include "storage/sinval.h"
 
 /*
@@ -30,7 +31,7 @@
 extern Size SInvalShmemSize(void);
 extern void CreateSharedInvalidationState(void);
 extern void SharedInvalBackendInit(bool sendOnly);
-extern bool BackendIdIsActive(int backendID);
+extern PGPROC *BackendIdGetProc(int backendID);
 
 extern void SIInsertDataEntries(const SharedInvalidationMessage *data, int n);
 extern int	SIGetDataEntries(SharedInvalidationMessage *data, int datasize);
#3Jeff Davis
pgsql@j-davis.com
In reply to: Robert Haas (#2)
Re: lazy vxid locks, v2

On Tue, 2011-07-05 at 13:15 -0400, Robert Haas wrote:

On Tue, Jul 5, 2011 at 1:13 PM, Robert Haas <robertmhaas@gmail.com> wrote:

Here is an updated version of the "lazy vxid locks" patch [1], which
applies over the latest "reduce the overhead of frequent table
locks"[2] patch.

[1] https://commitfest.postgresql.org/action/patch_view?id=585
[2] https://commitfest.postgresql.org/action/patch_view?id=572

And then I forgot the attachment.

The patch looks good, and I like the concept.

My only real comment is one that you already made: the
BackendIdGetProc() mechanism is not "awesome". However, that seems like
material for a separate patch, if at all.

Big disclaimer: I did not do any performance review, despite the fact
that this is a performance patch.

I see that there are some active performance concerns around this patch,
specifically that it may cause an increase in spinlock contention:

http://archives.postgresql.org/message-id/BANLkTikp4EGbfw9xDx9bQ_vK8DQa11WbPg@mail.gmail.com

Fortunately, there's a subsequent discussion that shows a lot of
promise:

http://archives.postgresql.org/pgsql-hackers/2011-07/msg00293.php

I'll mark this "waiting on author" pending the results of that
discussion.

I like the approach you're taking with this series of patches, so
perhaps we shouldn't set the bar so high that you have to remove all of
the bottlenecks before making any progress. Then again, maybe there's
not a huge cost to leaving these patches on the shelf until we're sure
that they lead somewhere.

Regards,
Jeff Davis

#4Josh Berkus
josh@agliodbs.com
In reply to: Jeff Davis (#3)
Re: lazy vxid locks, v2

Robert,

I should be able to do some performance testing on this, but not today.

Questions:

(1) can you re-link me to the pgbench and sysbench setup you used to
test this originally? I'd like to implement those.

(2) the max machine I can test these on is 16 cores. Is that adequate,
or do we need more cores for real testing?

--
Josh Berkus
PostgreSQL Experts Inc.
http://pgexperts.com

#5Robert Haas
robertmhaas@gmail.com
In reply to: Josh Berkus (#4)
Re: lazy vxid locks, v2

On Fri, Jul 15, 2011 at 4:41 PM, Josh Berkus <josh@agliodbs.com> wrote:

(1) can you re-link me to the pgbench and sysbench setup you used to
test this originally?  I'd like to implement those.

I didn't use sysbench. The pgbench command I used was something like:

pgbench -n -S -T $TIME -c $CLIENTS -j $CLIENTS

...with varying times and client counts. Three minutes or so seems to
be enough to get a reasonably good measurement. We have a report from
another tester that -M prepared exacerbates the effect of the main
fastlock patch (which I just committed) so that might be worth trying
here, too.

(2) the max machine I can test these on is 16 cores.  Is that adequate,
or do we need more cores for real testing?

I think you should be able to see a small benefit on a machine of that
size. My memory is a bit fuzzy because it's 1 AM and I haven't looked
at this in over a week, but I believe that this patch produces only a
small improvement in performance by itself, apparently because most of
the gain that we get from reducing lock manager contention gets eaten
up by additional spinlock contention. However, I think the change is
worthwhile anyway, because the benefit of any changes we might make to
reduce spinlock contention is going to be limited by the rate at which
we can shove traffic through the lock manager. In other words, aside
from whatever benefit this patch may have on its own, I believe it's
an enabler for future performance work.

In mulling over this patch, it's occurred to me that there are some
stylistic things about it that can stand to be improved. So I'm going
to do that for the next version. However, if you want to test on this
version, feel free, because I think those changes are going to have
little if any effect on performance.

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