pgproc-minimal-v13.patch
text/x-patch
Filename: pgproc-minimal-v13.patch
Type: text/x-patch
Part: 0
Patch
Same data as JSON:
GET /api/v1/attachments/:id/patch
the parsed metadata as JSON — format, series position, per-file stats; never the diff bytes.
API reference →
Format: unified
Series: patch v13
| File | + | − |
|---|---|---|
| src/backend/access/transam/twophase.c | 24 | 19 |
| src/backend/access/transam/varsup.c | 5 | 5 |
| src/backend/access/transam/xact.c | 2 | 2 |
| src/backend/commands/analyze.c | 2 | 2 |
| src/backend/commands/vacuum.c | 2 | 2 |
| src/backend/replication/walsender.c | 1 | 1 |
| src/backend/storage/ipc/procarray.c | 164 | 57 |
| src/backend/storage/lmgr/deadlock.c | 1 | 1 |
| src/backend/storage/lmgr/lock.c | 1 | 1 |
| src/backend/storage/lmgr/proc.c | 32 | 11 |
| src/backend/utils/time/snapmgr.c | 1 | 1 |
| src/include/storage/lock.h | 1 | 0 |
| src/include/storage/proc.h | 31 | 14 |
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 */