Hi All,

While working on some of the performance issues on HP-UX, I noticed a
significant data cache misses for accessing PGPROC members. On a close
inspection, it was quite evident that for large number (even few 10s)
of clients, the loop inside GetSnapshotData will cause data cache miss
for almost every PGPROC because the PGPROC structure is quite heavy
and no more than one structure may fit in a single cache line. So I
experimented by separating the most frequently and closely accessed
members of the PGPROC into an out of band array. I call it
PGPROC_MINIMAL structure which contains xid, xmin, vacuumFlags amongst
others. Basically, all the commonly accessed members by
GetSnapshotData find a place in this minimal structure.

When PGPROC array is allocated, we also allocate another array of
PGPROC_MINIMAL structures of the same size. While accessing the
ProcArray, a simple pointer mathematic can get us the corresponding
PGPROC_MINIMAL structure. The only exception being the dummy PGPROC
for prepared transaction. A first cut version of the patch is
attached. It looks big, but most of the changes are cosmetic because
of added indirection. The patch also contains another change to keep
the ProcArray sorted by (PGPROC *) to preserve locality of references
while traversing the array.

I did some tests of a 32 core IA HP-UX box and the results are quite
good. With a scale factor of 100 and -N option of pgbench (updates on
only accounts table), the numbers look something like this:

Clients HEAD    PGPROC-Patched  Gain
1       1098.488663     1059.830369     -3.52%
4       3569.481435     3663.898254     2.65%
32      11627.059228    16419.864056    41.22%
48      11044.501244    15825.132582    43.29%
64      10432.206525    15408.50304     47.70%
80      10210.57835     15170.614435    48.58%

The numbers are quite reproducible with couple of percentage points
variance. So even for single client, I sometimes see no degradation.
Here are some more numbers with the normal pgbench tests (without -N
option).

Clients HEAD    PGPROC-Patched  Gain
1       743      771    3.77%
4       1821       2315 27.13%
32      8011       9166 14.42%
48      7282       8959 23.03%
64      6742       8937 32.56%
80      6316       8664 37.18%

Its quite possible that the effect of the patch is more evident on the
particular hardware that I am testing. But the approach nevertheless
seems reasonable. It will very useful if someone else having access to
a large box can test the effect of the patch.

BTW, since I played with many versions of the patch, the exact numbers
with this version might be a little different than what I posted
above. I will conduct more tests, especially with more number of
clients and see if there is any difference in the improvement.

Thanks,
Pavan

-- 
Pavan Deolasee
EnterpriseDB     http://www.enterprisedb.com
diff --git a/src/backend/access/transam/twophase.c b/src/backend/access/transam/twophase.c
index 477982d..b907f72 100644
--- a/src/backend/access/transam/twophase.c
+++ b/src/backend/access/transam/twophase.c
@@ -114,6 +114,7 @@ int			max_prepared_xacts = 0;
 typedef struct GlobalTransactionData
 {
 	PGPROC		proc;			/* dummy proc */
+	PGPROC_MINIMAL proc_minimal;	/* dummy proc_minimal */
 	BackendId	dummyBackendId; /* similar to backend id for backends */
 	TimestampTz prepared_at;	/* time of preparation */
 	XLogRecPtr	prepare_lsn;	/* XLOG offset of prepare record */
@@ -223,6 +224,9 @@ TwoPhaseShmemInit(void)
 			 * technique.
 			 */
 			gxacts[i].dummyBackendId = MaxBackends + 1 + i;
+
+			/* Initialize minimal proc structure from the global structure */
+			gxacts[i].proc.proc_minimal = &gxacts[i].proc_minimal;
 		}
 	}
 	else
@@ -310,14 +314,15 @@ MarkAsPreparing(TransactionId xid, const char *gid,
 	gxact->proc.waitStatus = STATUS_OK;
 	/* We set up the gxact's VXID as InvalidBackendId/XID */
 	gxact->proc.lxid = (LocalTransactionId) xid;
-	gxact->proc.xid = xid;
-	gxact->proc.xmin = InvalidTransactionId;
+	gxact->proc.proc_minimal = &gxact->proc_minimal;
+	gxact->proc.proc_minimal->xid = xid;
+	gxact->proc.proc_minimal->xmin = InvalidTransactionId;
 	gxact->proc.pid = 0;
 	gxact->proc.backendId = InvalidBackendId;
 	gxact->proc.databaseId = databaseid;
 	gxact->proc.roleId = owner;
-	gxact->proc.inCommit = false;
-	gxact->proc.vacuumFlags = 0;
+	gxact->proc.proc_minimal->inCommit = false;
+	gxact->proc.proc_minimal->vacuumFlags = 0;
 	gxact->proc.lwWaiting = false;
 	gxact->proc.lwExclusive = false;
 	gxact->proc.lwWaitLink = NULL;
@@ -326,8 +331,8 @@ MarkAsPreparing(TransactionId xid, const char *gid,
 	for (i = 0; i < NUM_LOCK_PARTITIONS; i++)
 		SHMQueueInit(&(gxact->proc.myProcLocks[i]));
 	/* subxid data must be filled later by GXactLoadSubxactData */
-	gxact->proc.subxids.overflowed = false;
-	gxact->proc.subxids.nxids = 0;
+	gxact->proc.proc_minimal->overflowed = false;
+	gxact->proc.proc_minimal->nxids = 0;
 
 	gxact->prepared_at = prepared_at;
 	/* initialize LSN to 0 (start of WAL) */
@@ -361,14 +366,14 @@ GXactLoadSubxactData(GlobalTransaction gxact, int nsubxacts,
 	/* We need no extra lock since the GXACT isn't valid yet */
 	if (nsubxacts > PGPROC_MAX_CACHED_SUBXIDS)
 	{
-		gxact->proc.subxids.overflowed = true;
+		gxact->proc.proc_minimal->overflowed = true;
 		nsubxacts = PGPROC_MAX_CACHED_SUBXIDS;
 	}
 	if (nsubxacts > 0)
 	{
 		memcpy(gxact->proc.subxids.xids, children,
 			   nsubxacts * sizeof(TransactionId));
-		gxact->proc.subxids.nxids = nsubxacts;
+		gxact->proc.proc_minimal->nxids = nsubxacts;
 	}
 }
 
@@ -519,7 +524,7 @@ TransactionIdIsPrepared(TransactionId xid)
 	{
 		GlobalTransaction gxact = TwoPhaseState->prepXacts[i];
 
-		if (gxact->valid && gxact->proc.xid == xid)
+		if (gxact->valid && gxact->proc_minimal.xid == xid)
 		{
 			result = true;
 			break;
@@ -656,7 +661,7 @@ pg_prepared_xact(PG_FUNCTION_ARGS)
 		MemSet(values, 0, sizeof(values));
 		MemSet(nulls, 0, sizeof(nulls));
 
-		values[0] = TransactionIdGetDatum(gxact->proc.xid);
+		values[0] = TransactionIdGetDatum(gxact->proc_minimal.xid);
 		values[1] = CStringGetTextDatum(gxact->gid);
 		values[2] = TimestampTzGetDatum(gxact->prepared_at);
 		values[3] = ObjectIdGetDatum(gxact->owner);
@@ -712,7 +717,7 @@ TwoPhaseGetDummyProc(TransactionId xid)
 	{
 		GlobalTransaction gxact = TwoPhaseState->prepXacts[i];
 
-		if (gxact->proc.xid == xid)
+		if (gxact->proc_minimal.xid == xid)
 		{
 			result = &gxact->proc;
 			break;
@@ -841,7 +846,7 @@ save_state_data(const void *data, uint32 len)
 void
 StartPrepare(GlobalTransaction gxact)
 {
-	TransactionId xid = gxact->proc.xid;
+	TransactionId xid = gxact->proc_minimal.xid;
 	TwoPhaseFileHeader hdr;
 	TransactionId *children;
 	RelFileNode *commitrels;
@@ -913,7 +918,7 @@ StartPrepare(GlobalTransaction gxact)
 void
 EndPrepare(GlobalTransaction gxact)
 {
-	TransactionId xid = gxact->proc.xid;
+	TransactionId xid = gxact->proc_minimal.xid;
 	TwoPhaseFileHeader *hdr;
 	char		path[MAXPGPATH];
 	XLogRecData *record;
@@ -1021,7 +1026,7 @@ EndPrepare(GlobalTransaction gxact)
 	 */
 	START_CRIT_SECTION();
 
-	MyProc->inCommit = true;
+	MyProc->proc_minimal->inCommit = true;
 
 	gxact->prepare_lsn = XLogInsert(RM_XACT_ID, XLOG_XACT_PREPARE,
 									records.head);
@@ -1069,7 +1074,7 @@ EndPrepare(GlobalTransaction gxact)
 	 * checkpoint starting after this will certainly see the gxact as a
 	 * candidate for fsyncing.
 	 */
-	MyProc->inCommit = false;
+	MyProc->proc_minimal->inCommit = false;
 
 	END_CRIT_SECTION();
 
@@ -1260,7 +1265,7 @@ FinishPreparedTransaction(const char *gid, bool isCommit)
 	 * try to commit the same GID at once.
 	 */
 	gxact = LockGXact(gid, GetUserId());
-	xid = gxact->proc.xid;
+	xid = gxact->proc_minimal.xid;
 
 	/*
 	 * Read and validate the state file
@@ -1543,7 +1548,7 @@ CheckPointTwoPhase(XLogRecPtr redo_horizon)
 
 		if (gxact->valid &&
 			XLByteLE(gxact->prepare_lsn, redo_horizon))
-			xids[nxids++] = gxact->proc.xid;
+			xids[nxids++] = gxact->proc_minimal.xid;
 	}
 
 	LWLockRelease(TwoPhaseStateLock);
@@ -1972,7 +1977,7 @@ RecordTransactionCommitPrepared(TransactionId xid,
 	START_CRIT_SECTION();
 
 	/* See notes in RecordTransactionCommit */
-	MyProc->inCommit = true;
+	MyProc->proc_minimal->inCommit = true;
 
 	/* Emit the XLOG commit record */
 	xlrec.xid = xid;
@@ -2037,7 +2042,7 @@ RecordTransactionCommitPrepared(TransactionId xid,
 	TransactionIdCommitTree(xid, nchildren, children);
 
 	/* Checkpoint can proceed now */
-	MyProc->inCommit = false;
+	MyProc->proc_minimal->inCommit = false;
 
 	END_CRIT_SECTION();
 
diff --git a/src/backend/access/transam/varsup.c b/src/backend/access/transam/varsup.c
index 61dcfed..0effa1a 100644
--- a/src/backend/access/transam/varsup.c
+++ b/src/backend/access/transam/varsup.c
@@ -54,7 +54,7 @@ GetNewTransactionId(bool isSubXact)
 	if (IsBootstrapProcessingMode())
 	{
 		Assert(!isSubXact);
-		MyProc->xid = BootstrapTransactionId;
+		MyProc->proc_minimal->xid = BootstrapTransactionId;
 		return BootstrapTransactionId;
 	}
 
@@ -210,18 +210,18 @@ GetNewTransactionId(bool isSubXact)
 		volatile PGPROC *myproc = MyProc;
 
 		if (!isSubXact)
-			myproc->xid = xid;
+			myproc->proc_minimal->xid = xid;
 		else
 		{
-			int			nxids = myproc->subxids.nxids;
+			int			nxids = myproc->proc_minimal->nxids;
 
 			if (nxids < PGPROC_MAX_CACHED_SUBXIDS)
 			{
 				myproc->subxids.xids[nxids] = xid;
-				myproc->subxids.nxids = nxids + 1;
+				myproc->proc_minimal->nxids = nxids + 1;
 			}
 			else
-				myproc->subxids.overflowed = true;
+				myproc->proc_minimal->overflowed = true;
 		}
 	}
 
diff --git a/src/backend/access/transam/xact.c b/src/backend/access/transam/xact.c
index c151d3b..838bd23 100644
--- a/src/backend/access/transam/xact.c
+++ b/src/backend/access/transam/xact.c
@@ -981,7 +981,7 @@ RecordTransactionCommit(void)
 		 * bit fuzzy, but it doesn't matter.
 		 */
 		START_CRIT_SECTION();
-		MyProc->inCommit = true;
+		MyProc->proc_minimal->inCommit = true;
 
 		SetCurrentTransactionStopTimestamp();
 
@@ -1155,7 +1155,7 @@ RecordTransactionCommit(void)
 	 */
 	if (markXidCommitted)
 	{
-		MyProc->inCommit = false;
+		MyProc->proc_minimal->inCommit = false;
 		END_CRIT_SECTION();
 	}
 
diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c
index 32985a4..a6ee452 100644
--- a/src/backend/commands/analyze.c
+++ b/src/backend/commands/analyze.c
@@ -223,7 +223,7 @@ analyze_rel(Oid relid, VacuumStmt *vacstmt, BufferAccessStrategy bstrategy)
 	 * OK, let's do it.  First let other backends know I'm in ANALYZE.
 	 */
 	LWLockAcquire(ProcArrayLock, LW_EXCLUSIVE);
-	MyProc->vacuumFlags |= PROC_IN_ANALYZE;
+	MyProc->proc_minimal->vacuumFlags |= PROC_IN_ANALYZE;
 	LWLockRelease(ProcArrayLock);
 
 	/*
@@ -250,7 +250,7 @@ analyze_rel(Oid relid, VacuumStmt *vacstmt, BufferAccessStrategy bstrategy)
 	 * because the vacuum flag is cleared by the end-of-xact code.
 	 */
 	LWLockAcquire(ProcArrayLock, LW_EXCLUSIVE);
-	MyProc->vacuumFlags &= ~PROC_IN_ANALYZE;
+	MyProc->proc_minimal->vacuumFlags &= ~PROC_IN_ANALYZE;
 	LWLockRelease(ProcArrayLock);
 }
 
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index f42504c..c85c002 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -893,9 +893,9 @@ vacuum_rel(Oid relid, VacuumStmt *vacstmt, bool do_toast, bool for_wraparound)
 		 * which is probably Not Good.
 		 */
 		LWLockAcquire(ProcArrayLock, LW_EXCLUSIVE);
-		MyProc->vacuumFlags |= PROC_IN_VACUUM;
+		MyProc->proc_minimal->vacuumFlags |= PROC_IN_VACUUM;
 		if (for_wraparound)
-			MyProc->vacuumFlags |= PROC_VACUUM_FOR_WRAPAROUND;
+			MyProc->proc_minimal->vacuumFlags |= PROC_VACUUM_FOR_WRAPAROUND;
 		LWLockRelease(ProcArrayLock);
 	}
 
diff --git a/src/backend/replication/walsender.c b/src/backend/replication/walsender.c
index dd2d6ee..aa54a98 100644
--- a/src/backend/replication/walsender.c
+++ b/src/backend/replication/walsender.c
@@ -702,7 +702,7 @@ ProcessStandbyHSFeedbackMessage(void)
 	 * safe, and if we're moving it backwards, well, the data is at risk
 	 * already since a VACUUM could have just finished calling GetOldestXmin.)
 	 */
-	MyProc->xmin = reply.xmin;
+	MyProc->proc_minimal->xmin = reply.xmin;
 }
 
 /* Main loop of walsender process */
diff --git a/src/backend/storage/ipc/procarray.c b/src/backend/storage/ipc/procarray.c
index 1a48485..f0e1c5a 100644
--- a/src/backend/storage/ipc/procarray.c
+++ b/src/backend/storage/ipc/procarray.c
@@ -141,6 +141,29 @@ static void DisplayXidCache(void);
 #define xc_slow_answer_inc()		((void) 0)
 #endif   /* XIDCACHE_DEBUG */
 
+/*
+ * Get the PROC_MINIMAL structure corresponding to the given PGPROC. The trick
+ * is to avoid access to any PGPROC member to find the minimal structure
+ * because we want to avoid access to any PGPROC member unless its absolutely
+ * necessary. This reduces cache misses and faults. Except for dummy procs for
+ * prepared transactions, all other procs have a corresponding entry in the
+ * allProcs_Minimal array of PGPROC_MINIMAL. So we use pointer arithmetic to
+ * get that.
+ *
+ * XXX A branch mis-prediction can still end up touching the PGPROC
+ * proc_minimal member and that would cause cache miss. We have seen this on
+ * HP-UX compiler. It might be easier to handle with GCC by use of
+ * likely/unlikely hint knowing that in almost all cases its not needed to go
+ * to the PGPROC member or by rearranging the following code. For example, we
+ * can return (PGPROC_MINIMAL **) to avoid accessing PGPROC structure unless
+ * its required for prepared transaction
+ */
+#define PGProcGetMinimal(proc, procglobal) \
+		((proc) >= (procglobal)->allProcs && \
+		 (proc) < (procglobal)->allProcs + (procglobal)->allProcCount) ? \
+			&(procglobal)->allProcs_Minimal[(proc) - (procglobal)->allProcs] : \
+			(proc)->proc_minimal;
+
 /* Primitives for KnownAssignedXids array handling for standby */
 static void KnownAssignedXidsCompress(bool force);
 static void KnownAssignedXidsAdd(TransactionId from_xid, TransactionId to_xid,
@@ -253,6 +276,7 @@ void
 ProcArrayAdd(PGPROC *proc)
 {
 	ProcArrayStruct *arrayP = procArray;
+	int index;
 
 	LWLockAcquire(ProcArrayLock, LW_EXCLUSIVE);
 
@@ -269,7 +293,28 @@ ProcArrayAdd(PGPROC *proc)
 				 errmsg("sorry, too many clients already")));
 	}
 
-	arrayP->procs[arrayP->numProcs] = proc;
+	/*
+	 * Keep the procs array sorted by (PGPROC *) so that we can utilize
+	 * locality of references much better. This is useful while traversing the
+	 * ProcArray because there is a increased likelyhood of finding the next
+	 * PGPROC structure in the cache.
+	 * 
+	 * Since the occurance of adding/removing a proc is much lower than the
+	 * access to the ProcArray itself, the overhead should be marginal
+	 */
+	for (index = 0; index < arrayP->numProcs; index++)
+	{
+		/*
+		 * If we are the first PGPROC or if we have found our right position in
+		 * the array, break
+		 */
+		if ((arrayP->procs[index] == NULL) || (arrayP->procs[index] > proc))
+			break;
+	}
+
+	memmove(&arrayP->procs[index + 1], &arrayP->procs[index],
+			(arrayP->numProcs - index) * sizeof (PGPROC *));
+	arrayP->procs[index] = proc;
 	arrayP->numProcs++;
 
 	LWLockRelease(ProcArrayLock);
@@ -318,7 +363,9 @@ ProcArrayRemove(PGPROC *proc, TransactionId latestXid)
 	{
 		if (arrayP->procs[index] == proc)
 		{
-			arrayP->procs[index] = arrayP->procs[arrayP->numProcs - 1];
+			/* Keep the PGPROC array sorted. See notes above */
+			memmove(&arrayP->procs[index], &arrayP->procs[index + 1],
+					(arrayP->numProcs - index - 1) * sizeof (PGPROC *));
 			arrayP->procs[arrayP->numProcs - 1] = NULL; /* for debugging */
 			arrayP->numProcs--;
 			LWLockRelease(ProcArrayLock);
@@ -361,17 +408,17 @@ ProcArrayEndTransaction(PGPROC *proc, TransactionId latestXid)
 
 		LWLockAcquire(ProcArrayLock, LW_EXCLUSIVE);
 
-		proc->xid = InvalidTransactionId;
+		proc->proc_minimal->xid = InvalidTransactionId;
 		proc->lxid = InvalidLocalTransactionId;
-		proc->xmin = InvalidTransactionId;
+		proc->proc_minimal->xmin = InvalidTransactionId;
 		/* must be cleared with xid/xmin: */
-		proc->vacuumFlags &= ~PROC_VACUUM_STATE_MASK;
-		proc->inCommit = false; /* be sure this is cleared in abort */
+		proc->proc_minimal->vacuumFlags &= ~PROC_VACUUM_STATE_MASK;
+		proc->proc_minimal->inCommit = false; /* be sure this is cleared in abort */
 		proc->recoveryConflictPending = false;
 
 		/* Clear the subtransaction-XID cache too while holding the lock */
-		proc->subxids.nxids = 0;
-		proc->subxids.overflowed = false;
+		proc->proc_minimal->nxids = 0;
+		proc->proc_minimal->overflowed = false;
 
 		/* Also advance global latestCompletedXid while holding the lock */
 		if (TransactionIdPrecedes(ShmemVariableCache->latestCompletedXid,
@@ -390,10 +437,10 @@ ProcArrayEndTransaction(PGPROC *proc, TransactionId latestXid)
 		Assert(!TransactionIdIsValid(proc->xid));
 
 		proc->lxid = InvalidLocalTransactionId;
-		proc->xmin = InvalidTransactionId;
+		proc->proc_minimal->xmin = InvalidTransactionId;
 		/* must be cleared with xid/xmin: */
-		proc->vacuumFlags &= ~PROC_VACUUM_STATE_MASK;
-		proc->inCommit = false; /* be sure this is cleared in abort */
+		proc->proc_minimal->vacuumFlags &= ~PROC_VACUUM_STATE_MASK;
+		proc->proc_minimal->inCommit = false; /* be sure this is cleared in abort */
 		proc->recoveryConflictPending = false;
 
 		Assert(proc->subxids.nxids == 0);
@@ -419,18 +466,18 @@ ProcArrayClearTransaction(PGPROC *proc)
 	 * duplicate with the gxact that has already been inserted into the
 	 * ProcArray.
 	 */
-	proc->xid = InvalidTransactionId;
+	proc->proc_minimal->xid = InvalidTransactionId;
 	proc->lxid = InvalidLocalTransactionId;
-	proc->xmin = InvalidTransactionId;
+	proc->proc_minimal->xmin = InvalidTransactionId;
 	proc->recoveryConflictPending = false;
 
 	/* redundant, but just in case */
-	proc->vacuumFlags &= ~PROC_VACUUM_STATE_MASK;
-	proc->inCommit = false;
+	proc->proc_minimal->vacuumFlags &= ~PROC_VACUUM_STATE_MASK;
+	proc->proc_minimal->inCommit = false;
 
 	/* Clear the subtransaction-XID cache too */
-	proc->subxids.nxids = 0;
-	proc->subxids.overflowed = false;
+	proc->proc_minimal->nxids = 0;
+	proc->proc_minimal->overflowed = false;
 }
 
 /*
@@ -741,6 +788,10 @@ TransactionIdIsInProgress(TransactionId xid)
 	TransactionId topxid;
 	int			i,
 				j;
+#ifdef NOT_USED	
+	static PGPROC **procs = NULL;
+	static PGPROC_MINIMAL *txns = NULL;
+#endif
 
 	/*
 	 * Don't bother checking a transaction older than RecentXmin; it could not
@@ -811,15 +862,19 @@ TransactionIdIsInProgress(TransactionId xid)
 	/* No shortcuts, gotta grovel through the array */
 	for (i = 0; i < arrayP->numProcs; i++)
 	{
+		volatile PROC_HDR *procglobal = ProcGlobal;
 		volatile PGPROC *proc = arrayP->procs[i];
+		volatile PGPROC_MINIMAL *proc_minimal;
 		TransactionId pxid;
 
 		/* Ignore my own proc --- dealt with it above */
 		if (proc == MyProc)
 			continue;
 
+		proc_minimal = PGProcGetMinimal(proc, procglobal);
+
 		/* Fetch xid just once - see GetNewTransactionId */
-		pxid = proc->xid;
+		pxid = proc_minimal->xid;
 
 		if (!TransactionIdIsValid(pxid))
 			continue;
@@ -844,7 +899,7 @@ TransactionIdIsInProgress(TransactionId xid)
 		/*
 		 * Step 2: check the cached child-Xids arrays
 		 */
-		for (j = proc->subxids.nxids - 1; j >= 0; j--)
+		for (j = proc_minimal->nxids - 1; j >= 0; j--)
 		{
 			/* Fetch xid just once - see GetNewTransactionId */
 			TransactionId cxid = proc->subxids.xids[j];
@@ -864,7 +919,7 @@ TransactionIdIsInProgress(TransactionId xid)
 		 * we hold ProcArrayLock.  So we can't miss an Xid that we need to
 		 * worry about.)
 		 */
-		if (proc->subxids.overflowed)
+		if (proc_minimal->overflowed)
 			xids[nxids++] = pxid;
 	}
 
@@ -965,10 +1020,15 @@ TransactionIdIsActive(TransactionId xid)
 
 	for (i = 0; i < arrayP->numProcs; i++)
 	{
+		volatile PROC_HDR *procglobal = ProcGlobal;
 		volatile PGPROC *proc = arrayP->procs[i];
+		volatile PGPROC_MINIMAL *proc_minimal;
+		TransactionId pxid;
+
+		proc_minimal = PGProcGetMinimal(proc, procglobal);
 
 		/* Fetch xid just once - see GetNewTransactionId */
-		TransactionId pxid = proc->xid;
+		pxid = proc_minimal->xid;
 
 		if (!TransactionIdIsValid(pxid))
 			continue;
@@ -1060,9 +1120,13 @@ GetOldestXmin(bool allDbs, bool ignoreVacuum)
 
 	for (index = 0; index < arrayP->numProcs; index++)
 	{
+		volatile PROC_HDR *procglobal = ProcGlobal;
 		volatile PGPROC *proc = arrayP->procs[index];
+		volatile PGPROC_MINIMAL *proc_minimal;
+	   
+		proc_minimal = PGProcGetMinimal(proc, procglobal);
 
-		if (ignoreVacuum && (proc->vacuumFlags & PROC_IN_VACUUM))
+		if (ignoreVacuum && (proc_minimal->vacuumFlags & PROC_IN_VACUUM))
 			continue;
 
 		if (allDbs ||
@@ -1070,7 +1134,7 @@ GetOldestXmin(bool allDbs, bool ignoreVacuum)
 			proc->databaseId == 0)		/* always include WalSender */
 		{
 			/* Fetch xid just once - see GetNewTransactionId */
-			TransactionId xid = proc->xid;
+			TransactionId xid = proc_minimal->xid;
 
 			/* First consider the transaction's own Xid, if any */
 			if (TransactionIdIsNormal(xid) &&
@@ -1084,7 +1148,7 @@ GetOldestXmin(bool allDbs, bool ignoreVacuum)
 			 * have an Xmin but not (yet) an Xid; conversely, if it has an
 			 * Xid, that could determine some not-yet-set Xmin.
 			 */
-			xid = proc->xmin;	/* Fetch just once */
+			xid = proc_minimal->xmin;	/* Fetch just once */
 			if (TransactionIdIsNormal(xid) &&
 				TransactionIdPrecedes(xid, result))
 				result = xid;
@@ -1271,21 +1335,35 @@ GetSnapshotData(Snapshot snapshot)
 		 */
 		for (index = 0; index < arrayP->numProcs; index++)
 		{
+			volatile PROC_HDR *procglobal = ProcGlobal;
 			volatile PGPROC *proc = arrayP->procs[index];
+			volatile PGPROC_MINIMAL *proc_minimal;
 			TransactionId xid;
 
+			/*
+			 * All the information needed by GetSnapshotData is stored out of
+			 * band as an array of PGPROC_MINIMAL structures. The only exception
+			 * is the dummy PGPROC for prepared transaction.
+			 *
+			 * We try to avoid accessing any field from the PGPROC because that
+			 * may cause cache miss and associated overhead. We instead try to
+			 * directly access the relevant information by doing pointer
+			 * arithmatic.
+			 */
+			proc_minimal = PGProcGetMinimal(proc, procglobal);
+
 			/* Ignore procs running LAZY VACUUM */
-			if (proc->vacuumFlags & PROC_IN_VACUUM)
+			if (proc_minimal->vacuumFlags & PROC_IN_VACUUM)
 				continue;
 
 			/* Update globalxmin to be the smallest valid xmin */
-			xid = proc->xmin;	/* fetch just once */
+			xid = proc_minimal->xmin;	/* fetch just once */
 			if (TransactionIdIsNormal(xid) &&
 				TransactionIdPrecedes(xid, globalxmin))
 				globalxmin = xid;
 
 			/* Fetch xid just once - see GetNewTransactionId */
-			xid = proc->xid;
+			xid = proc_minimal->xid;
 
 			/*
 			 * If the transaction has been assigned an xid < xmax we add it to
@@ -1323,11 +1401,11 @@ GetSnapshotData(Snapshot snapshot)
 			 */
 			if (!suboverflowed && proc != MyProc)
 			{
-				if (proc->subxids.overflowed)
+				if (proc_minimal->overflowed)
 					suboverflowed = true;
 				else
 				{
-					int			nxids = proc->subxids.nxids;
+					int			nxids = proc_minimal->nxids;
 
 					if (nxids > 0)
 					{
@@ -1372,9 +1450,8 @@ GetSnapshotData(Snapshot snapshot)
 			suboverflowed = true;
 	}
 
-	if (!TransactionIdIsValid(MyProc->xmin))
-		MyProc->xmin = TransactionXmin = xmin;
-
+	if (!TransactionIdIsValid(MyProc->proc_minimal->xmin))
+		MyProc->proc_minimal->xmin = TransactionXmin = xmin;
 	LWLockRelease(ProcArrayLock);
 
 	/*
@@ -1436,14 +1513,18 @@ ProcArrayInstallImportedXmin(TransactionId xmin, TransactionId sourcexid)
 
 	for (index = 0; index < arrayP->numProcs; index++)
 	{
+		volatile PROC_HDR *procglobal = ProcGlobal;
 		volatile PGPROC *proc = arrayP->procs[index];
+		volatile PGPROC_MINIMAL *proc_minimal;
 		TransactionId xid;
 
+		proc_minimal = PGProcGetMinimal(proc, procglobal);
+
 		/* Ignore procs running LAZY VACUUM */
-		if (proc->vacuumFlags & PROC_IN_VACUUM)
+		if (proc_minimal->vacuumFlags & PROC_IN_VACUUM)
 			continue;
 
-		xid = proc->xid;	/* fetch just once */
+		xid = proc_minimal->xid;	/* fetch just once */
 		if (xid != sourcexid)
 			continue;
 
@@ -1459,7 +1540,7 @@ ProcArrayInstallImportedXmin(TransactionId xmin, TransactionId sourcexid)
 		/*
 		 * Likewise, let's just make real sure its xmin does cover us.
 		 */
-		xid = proc->xmin;	/* fetch just once */
+		xid = proc_minimal->xmin;	/* fetch just once */
 		if (!TransactionIdIsNormal(xid) ||
 			!TransactionIdPrecedesOrEquals(xid, xmin))
 			continue;
@@ -1470,7 +1551,7 @@ ProcArrayInstallImportedXmin(TransactionId xmin, TransactionId sourcexid)
 		 * GetSnapshotData first, we'll be overwriting a valid xmin here,
 		 * so we don't check that.)
 		 */
-		MyProc->xmin = TransactionXmin = xmin;
+		MyProc->proc_minimal->xmin = TransactionXmin = xmin;
 
 		result = true;
 		break;
@@ -1562,12 +1643,16 @@ GetRunningTransactionData(void)
 	 */
 	for (index = 0; index < arrayP->numProcs; index++)
 	{
+		volatile PROC_HDR *procglobal = ProcGlobal;
 		volatile PGPROC *proc = arrayP->procs[index];
+		volatile PGPROC_MINIMAL *proc_minimal;
 		TransactionId xid;
 		int			nxids;
 
+		proc_minimal = PGProcGetMinimal(proc, procglobal);
+
 		/* Fetch xid just once - see GetNewTransactionId */
-		xid = proc->xid;
+		xid = proc_minimal->xid;
 
 		/*
 		 * We don't need to store transactions that don't have a TransactionId
@@ -1585,7 +1670,7 @@ GetRunningTransactionData(void)
 		 * Save subtransaction XIDs. Other backends can't add or remove
 		 * entries while we're holding XidGenLock.
 		 */
-		nxids = proc->subxids.nxids;
+		nxids = proc_minimal->nxids;
 		if (nxids > 0)
 		{
 			memcpy(&xids[count], (void *) proc->subxids.xids,
@@ -1593,7 +1678,7 @@ GetRunningTransactionData(void)
 			count += nxids;
 			subcount += nxids;
 
-			if (proc->subxids.overflowed)
+			if (proc_minimal->overflowed)
 				suboverflowed = true;
 
 			/*
@@ -1653,11 +1738,15 @@ GetOldestActiveTransactionId(void)
 	 */
 	for (index = 0; index < arrayP->numProcs; index++)
 	{
+		volatile PROC_HDR *procglobal = ProcGlobal;
 		volatile PGPROC *proc = arrayP->procs[index];
+		volatile PGPROC_MINIMAL *proc_minimal;
 		TransactionId xid;
 
+		proc_minimal = PGProcGetMinimal(proc, procglobal);
+
 		/* Fetch xid just once - see GetNewTransactionId */
-		xid = proc->xid;
+		xid = proc_minimal->xid;
 
 		if (!TransactionIdIsNormal(xid))
 			continue;
@@ -1709,12 +1798,17 @@ GetTransactionsInCommit(TransactionId **xids_p)
 
 	for (index = 0; index < arrayP->numProcs; index++)
 	{
+		volatile PROC_HDR *procglobal = ProcGlobal;
 		volatile PGPROC *proc = arrayP->procs[index];
+		volatile PGPROC_MINIMAL *proc_minimal;
+		TransactionId pxid;
+
+		proc_minimal = PGProcGetMinimal(proc, procglobal);
 
 		/* Fetch xid just once - see GetNewTransactionId */
-		TransactionId pxid = proc->xid;
+		pxid = proc_minimal->xid;
 
-		if (proc->inCommit && TransactionIdIsValid(pxid))
+		if (proc_minimal->inCommit && TransactionIdIsValid(pxid))
 			xids[nxids++] = pxid;
 	}
 
@@ -1744,12 +1838,17 @@ HaveTransactionsInCommit(TransactionId *xids, int nxids)
 
 	for (index = 0; index < arrayP->numProcs; index++)
 	{
+		volatile PROC_HDR *procglobal = ProcGlobal;
 		volatile PGPROC *proc = arrayP->procs[index];
+		volatile PGPROC_MINIMAL *proc_minimal;
+		TransactionId pxid;
+
+		proc_minimal = PGProcGetMinimal(proc, procglobal);
 
 		/* Fetch xid just once - see GetNewTransactionId */
-		TransactionId pxid = proc->xid;
+		pxid = proc_minimal->xid;
 
-		if (proc->inCommit && TransactionIdIsValid(pxid))
+		if (proc_minimal->inCommit && TransactionIdIsValid(pxid))
 		{
 			int			i;
 
@@ -1833,9 +1932,13 @@ BackendXidGetPid(TransactionId xid)
 
 	for (index = 0; index < arrayP->numProcs; index++)
 	{
+		volatile PROC_HDR *procglobal = ProcGlobal;
 		volatile PGPROC *proc = arrayP->procs[index];
+		volatile PGPROC_MINIMAL *proc_minimal;
 
-		if (proc->xid == xid)
+		proc_minimal = PGProcGetMinimal(proc, procglobal);
+
+		if (proc_minimal->xid == xid)
 		{
 			result = proc->pid;
 			break;
@@ -1906,13 +2009,13 @@ GetCurrentVirtualXIDs(TransactionId limitXmin, bool excludeXmin0,
 		if (proc == MyProc)
 			continue;
 
-		if (excludeVacuum & proc->vacuumFlags)
+		if (excludeVacuum & proc->proc_minimal->vacuumFlags)
 			continue;
 
 		if (allDbs || proc->databaseId == MyDatabaseId)
 		{
 			/* Fetch xmin just once - might change on us */
-			TransactionId pxmin = proc->xmin;
+			TransactionId pxmin = proc->proc_minimal->xmin;
 
 			if (excludeXmin0 && !TransactionIdIsValid(pxmin))
 				continue;
@@ -2006,7 +2109,7 @@ GetConflictingVirtualXIDs(TransactionId limitXmin, Oid dbOid)
 			proc->databaseId == dbOid)
 		{
 			/* Fetch xmin just once - can't change on us, but good coding */
-			TransactionId pxmin = proc->xmin;
+			TransactionId pxmin = proc->proc_minimal->xmin;
 
 			/*
 			 * We ignore an invalid pxmin because this means that backend has
@@ -2104,7 +2207,9 @@ MinimumActiveBackends(int min)
 	 */
 	for (index = 0; index < arrayP->numProcs; index++)
 	{
+		volatile PROC_HDR *procglobal = ProcGlobal;
 		volatile PGPROC *proc = arrayP->procs[index];
+		volatile PGPROC_MINIMAL *proc_minimal;
 
 		/*
 		 * Since we're not holding a lock, need to check that the pointer is
@@ -2120,12 +2225,14 @@ MinimumActiveBackends(int min)
 		if (proc == NULL)
 			continue;
 
+		proc_minimal = PGProcGetMinimal(proc, procglobal);
+
 		if (proc == MyProc)
 			continue;			/* do not count myself */
+		if (proc_minimal->xid == InvalidTransactionId)
+			continue;			/* do not count if no XID assigned */
 		if (proc->pid == 0)
 			continue;			/* do not count prepared xacts */
-		if (proc->xid == InvalidTransactionId)
-			continue;			/* do not count if no XID assigned */
 		if (proc->waitLock != NULL)
 			continue;			/* do not count if blocked on a lock */
 		count++;
@@ -2291,7 +2398,7 @@ CountOtherDBBackends(Oid databaseId, int *nbackends, int *nprepared)
 			else
 			{
 				(*nbackends)++;
-				if ((proc->vacuumFlags & PROC_IS_AUTOVACUUM) &&
+				if ((proc->proc_minimal->vacuumFlags & PROC_IS_AUTOVACUUM) &&
 					nautovacs < MAXAUTOVACPIDS)
 					autovac_pids[nautovacs++] = proc->pid;
 			}
@@ -2321,8 +2428,8 @@ CountOtherDBBackends(Oid databaseId, int *nbackends, int *nprepared)
 
 #define XidCacheRemove(i) \
 	do { \
-		MyProc->subxids.xids[i] = MyProc->subxids.xids[MyProc->subxids.nxids - 1]; \
-		MyProc->subxids.nxids--; \
+		MyProc->subxids.xids[i] = MyProc->subxids.xids[MyProc->proc_minimal->nxids - 1]; \
+		MyProc->proc_minimal->nxids--; \
 	} while (0)
 
 /*
@@ -2361,7 +2468,7 @@ XidCacheRemoveRunningXids(TransactionId xid,
 	{
 		TransactionId anxid = xids[i];
 
-		for (j = MyProc->subxids.nxids - 1; j >= 0; j--)
+		for (j = MyProc->proc_minimal->nxids - 1; j >= 0; j--)
 		{
 			if (TransactionIdEquals(MyProc->subxids.xids[j], anxid))
 			{
@@ -2377,11 +2484,11 @@ XidCacheRemoveRunningXids(TransactionId xid,
 		 * error during AbortSubTransaction.  So instead of Assert, emit a
 		 * debug warning.
 		 */
-		if (j < 0 && !MyProc->subxids.overflowed)
+		if (j < 0 && !MyProc->proc_minimal->overflowed)
 			elog(WARNING, "did not find subXID %u in MyProc", anxid);
 	}
 
-	for (j = MyProc->subxids.nxids - 1; j >= 0; j--)
+	for (j = MyProc->proc_minimal->nxids - 1; j >= 0; j--)
 	{
 		if (TransactionIdEquals(MyProc->subxids.xids[j], xid))
 		{
@@ -2390,7 +2497,7 @@ XidCacheRemoveRunningXids(TransactionId xid,
 		}
 	}
 	/* Ordinarily we should have found it, unless the cache has overflowed */
-	if (j < 0 && !MyProc->subxids.overflowed)
+	if (j < 0 && !MyProc->proc_minimal->overflowed)
 		elog(WARNING, "did not find subXID %u in MyProc", xid);
 
 	/* Also advance global latestCompletedXid while holding the lock */
diff --git a/src/backend/storage/lmgr/deadlock.c b/src/backend/storage/lmgr/deadlock.c
index 7e7f6af..bb1e654 100644
--- a/src/backend/storage/lmgr/deadlock.c
+++ b/src/backend/storage/lmgr/deadlock.c
@@ -541,7 +541,7 @@ FindLockCycleRecurse(PGPROC *checkProc,
 					 * vacuumFlag bit), but we don't do that here to avoid
 					 * grabbing ProcArrayLock.
 					 */
-					if (proc->vacuumFlags & PROC_IS_AUTOVACUUM)
+					if (proc->proc_minimal->vacuumFlags & PROC_IS_AUTOVACUUM)
 						blocking_autovacuum_proc = proc;
 
 					/* This proc hard-blocks checkProc */
diff --git a/src/backend/storage/lmgr/lock.c b/src/backend/storage/lmgr/lock.c
index ed8344f..3e7a557 100644
--- a/src/backend/storage/lmgr/lock.c
+++ b/src/backend/storage/lmgr/lock.c
@@ -3184,7 +3184,7 @@ GetRunningTransactionLocks(int *nlocks)
 			PGPROC	   *proc = proclock->tag.myProc;
 			LOCK	   *lock = proclock->tag.myLock;
 
-			accessExclusiveLocks[index].xid = proc->xid;
+			accessExclusiveLocks[index].xid = proc->proc_minimal->xid;
 			accessExclusiveLocks[index].dbOid = lock->tag.locktag_field1;
 			accessExclusiveLocks[index].relOid = lock->tag.locktag_field2;
 
diff --git a/src/backend/storage/lmgr/proc.c b/src/backend/storage/lmgr/proc.c
index eda3a98..0261de7 100644
--- a/src/backend/storage/lmgr/proc.c
+++ b/src/backend/storage/lmgr/proc.c
@@ -113,6 +113,9 @@ ProcGlobalShmemSize(void)
 	/* ProcStructLock */
 	size = add_size(size, sizeof(slock_t));
 
+	size = add_size(size, mul_size(NUM_AUXILIARY_PROCS, sizeof(PGPROC_MINIMAL)));
+	size = add_size(size, mul_size(MaxBackends, sizeof(PGPROC_MINIMAL)));
+
 	return size;
 }
 
@@ -157,6 +160,7 @@ void
 InitProcGlobal(void)
 {
 	PGPROC	   *procs;
+	PGPROC_MINIMAL *procs_minimal;
 	int			i,
 				j;
 	bool		found;
@@ -195,6 +199,22 @@ InitProcGlobal(void)
 				(errcode(ERRCODE_OUT_OF_MEMORY),
 				 errmsg("out of shared memory")));
 	MemSet(procs, 0, TotalProcs * sizeof(PGPROC));
+
+	/*
+	 * Also allocate a separate array of PROC_MINIMAL structures. We keep this
+	 * out of band of the main PGPROC array to ensure the very heavily accessed
+	 * members of the PGPROC structure are stored contiguously in the memory.
+	 * This provides significant performance benefits, especially on a
+	 * multiprocessor system by improving cache hit ratio.
+	 *
+	 * Note: We separate the members needed by GetSnapshotData since that's the
+	 * most frequently accessed code path. There is one PROC_MINIMAL structure
+	 * for every PGPROC structure.
+	 */
+	procs_minimal = (PGPROC_MINIMAL *) ShmemAlloc(TotalProcs * sizeof(PGPROC_MINIMAL));
+	MemSet(procs_minimal, 0, TotalProcs * sizeof(PGPROC_MINIMAL));
+	ProcGlobal->allProcs_Minimal = procs_minimal;
+
 	for (i = 0; i < TotalProcs; i++)
 	{
 		/* Common initialization for all PGPROCs, regardless of type. */
@@ -203,6 +223,7 @@ InitProcGlobal(void)
 		PGSemaphoreCreate(&(procs[i].sem));
 		InitSharedLatch(&(procs[i].procLatch));
 		procs[i].backendLock = LWLockAssign();
+		procs[i].proc_minimal = &procs_minimal[i];
 
 		/*
 		 * Newly created PGPROCs for normal backends or for autovacuum must
@@ -313,18 +334,18 @@ InitProcess(void)
 	SHMQueueElemInit(&(MyProc->links));
 	MyProc->waitStatus = STATUS_OK;
 	MyProc->lxid = InvalidLocalTransactionId;
-	MyProc->xid = InvalidTransactionId;
-	MyProc->xmin = InvalidTransactionId;
+	MyProc->proc_minimal->xid = InvalidTransactionId;
+	MyProc->proc_minimal->xmin = InvalidTransactionId;
 	MyProc->pid = MyProcPid;
 	/* backendId, databaseId and roleId will be filled in later */
 	MyProc->backendId = InvalidBackendId;
 	MyProc->databaseId = InvalidOid;
 	MyProc->roleId = InvalidOid;
-	MyProc->inCommit = false;
-	MyProc->vacuumFlags = 0;
+	MyProc->proc_minimal->inCommit = false;
+	MyProc->proc_minimal->vacuumFlags = 0;
 	/* NB -- autovac launcher intentionally does not set IS_AUTOVACUUM */
 	if (IsAutoVacuumWorkerProcess())
-		MyProc->vacuumFlags |= PROC_IS_AUTOVACUUM;
+		MyProc->proc_minimal->vacuumFlags |= PROC_IS_AUTOVACUUM;
 	MyProc->lwWaiting = false;
 	MyProc->lwExclusive = false;
 	MyProc->lwWaitLink = NULL;
@@ -472,13 +493,13 @@ InitAuxiliaryProcess(void)
 	SHMQueueElemInit(&(MyProc->links));
 	MyProc->waitStatus = STATUS_OK;
 	MyProc->lxid = InvalidLocalTransactionId;
-	MyProc->xid = InvalidTransactionId;
-	MyProc->xmin = InvalidTransactionId;
+	MyProc->proc_minimal->xid = InvalidTransactionId;
+	MyProc->proc_minimal->xmin = InvalidTransactionId;
 	MyProc->backendId = InvalidBackendId;
 	MyProc->databaseId = InvalidOid;
 	MyProc->roleId = InvalidOid;
-	MyProc->inCommit = false;
-	MyProc->vacuumFlags = 0;
+	MyProc->proc_minimal->inCommit = false;
+	MyProc->proc_minimal->vacuumFlags = 0;
 	MyProc->lwWaiting = false;
 	MyProc->lwExclusive = false;
 	MyProc->lwWaitLink = NULL;
@@ -1053,8 +1074,8 @@ ProcSleep(LOCALLOCK *locallock, LockMethod lockMethodTable)
 			 * wraparound.
 			 */
 			if ((autovac != NULL) &&
-				(autovac->vacuumFlags & PROC_IS_AUTOVACUUM) &&
-				!(autovac->vacuumFlags & PROC_VACUUM_FOR_WRAPAROUND))
+				(autovac->proc_minimal->vacuumFlags & PROC_IS_AUTOVACUUM) &&
+				!(autovac->proc_minimal->vacuumFlags & PROC_VACUUM_FOR_WRAPAROUND))
 			{
 				int			pid = autovac->pid;
 
diff --git a/src/backend/utils/time/snapmgr.c b/src/backend/utils/time/snapmgr.c
index 50fb780..bcfe3f3 100644
--- a/src/backend/utils/time/snapmgr.c
+++ b/src/backend/utils/time/snapmgr.c
@@ -577,7 +577,7 @@ static void
 SnapshotResetXmin(void)
 {
 	if (RegisteredSnapshots == 0 && ActiveSnapshot == NULL)
-		MyProc->xmin = InvalidTransactionId;
+		MyProc->proc_minimal->xmin = InvalidTransactionId;
 }
 
 /*
diff --git a/src/include/storage/lock.h b/src/include/storage/lock.h
index bc746a3..093a56e 100644
--- a/src/include/storage/lock.h
+++ b/src/include/storage/lock.h
@@ -21,6 +21,7 @@
 
 /* struct PGPROC is declared in proc.h, but must forward-reference it */
 typedef struct PGPROC PGPROC;
+typedef struct PGPROC_MINIMAL PGPROC_MINIMAL;
 
 typedef struct PROC_QUEUE
 {
diff --git a/src/include/storage/proc.h b/src/include/storage/proc.h
index 6e798b1..a136309 100644
--- a/src/include/storage/proc.h
+++ b/src/include/storage/proc.h
@@ -35,8 +35,6 @@
 
 struct XidCache
 {
-	bool		overflowed;
-	int			nxids;
 	TransactionId xids[PGPROC_MAX_CACHED_SUBXIDS];
 };
 
@@ -57,6 +55,8 @@ struct XidCache
  */
 #define		FP_LOCK_SLOTS_PER_BACKEND 16
 
+struct PGPROC_MINIMAL;
+
 /*
  * Each backend has a PGPROC struct in shared memory.  There is also a list of
  * currently-unused PGPROC structs that will be reallocated to new backends.
@@ -87,14 +87,7 @@ struct PGPROC
 								 * being executed by this proc, if running;
 								 * else InvalidLocalTransactionId */
 
-	TransactionId xid;			/* id of top-level transaction currently being
-								 * executed by this proc, if running and XID
-								 * is assigned; else InvalidTransactionId */
-
-	TransactionId xmin;			/* minimal running XID as it was when we were
-								 * starting our xact, excluding LAZY VACUUM:
-								 * vacuum must not remove tuples deleted by
-								 * xid >= xmin ! */
+	PGPROC_MINIMAL	*proc_minimal;
 
 	int			pid;			/* Backend's process ID; 0 if prepared xact */
 
@@ -103,10 +96,6 @@ struct PGPROC
 	Oid			databaseId;		/* OID of database this backend is using */
 	Oid			roleId;			/* OID of role using this backend */
 
-	bool		inCommit;		/* true if within commit critical section */
-
-	uint8		vacuumFlags;	/* vacuum-related flags, see above */
-
 	/*
 	 * While in hot standby mode, shows that a conflict signal has been sent
 	 * for the current transaction. Set/cleared while holding ProcArrayLock,
@@ -161,6 +150,32 @@ struct PGPROC
 
 extern PGDLLIMPORT PGPROC *MyProc;
 
+/*
+ * A minimal part of the PGPROC. We store these members out of the main PGPROC
+ * structure since they are very heavily accessed members and usually in a loop
+ * for all active PGPROCs. Storing them in a separate array ensures that these
+ * members can be very effeciently accessed with minimum cache misses. On a
+ * large multiprocessor system, this can show a significant performance
+ * improvement.
+ */
+struct PGPROC_MINIMAL
+{
+	TransactionId xid;			/* id of top-level transaction currently being
+								 * executed by this proc, if running and XID
+								 * is assigned; else InvalidTransactionId */
+
+	TransactionId xmin;			/* minimal running XID as it was when we were
+								 * starting our xact, excluding LAZY VACUUM:
+								 * vacuum must not remove tuples deleted by
+								 * xid >= xmin ! */
+
+	uint8		vacuumFlags;	/* vacuum-related flags, see above */
+	bool		overflowed;
+	bool		inCommit;		/* true if within commit critical section */
+
+	int			nxids;
+};
+
 
 /*
  * There is one ProcGlobal struct for the whole database cluster.
@@ -169,6 +184,8 @@ typedef struct PROC_HDR
 {
 	/* Array of PGPROC structures (not including dummies for prepared txns) */
 	PGPROC	   *allProcs;
+	/* Array of PGPROC_MINIMAL structures (not including dummies for prepared txns */
+	PGPROC_MINIMAL	*allProcs_Minimal;
 	/* Length of allProcs array */
 	uint32		allProcCount;
 	/* Head of list of free PGPROC structures */
-- 
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

Reply via email to