snapshot scalability: Move subxact info to ProcGlobal, remove PGXACT.

Similar to the previous changes this increases the chance that data
frequently needed by GetSnapshotData() stays in l2 cache. In many
workloads subtransactions are very rare, and this makes the check for
that considerably cheaper.

As this removes the last member of PGXACT, there is no need to keep it
around anymore.

On a larger 2 socket machine this and the two preceding commits result
in a ~1.07x performance increase in read-only pgbench. For read-heavy
mixed r/w workloads without row level contention, I see about 1.1x.

Author: Andres Freund <andres@anarazel.de>
Reviewed-By: Robert Haas <robertmhaas@gmail.com>
Reviewed-By: Thomas Munro <thomas.munro@gmail.com>
Reviewed-By: David Rowley <dgrowleyml@gmail.com>
Discussion: https://postgr.es/m/20200301083601.ews6hz5dduc3w2se@alap3.anarazel.de
This commit is contained in:
Andres Freund 2020-08-14 14:30:38 -07:00
parent 5788e258bb
commit 73487a60fc
7 changed files with 113 additions and 113 deletions

View File

@ -295,7 +295,7 @@ TransactionIdSetPageStatus(TransactionId xid, int nsubxids,
*/ */
if (all_xact_same_page && xid == MyProc->xid && if (all_xact_same_page && xid == MyProc->xid &&
nsubxids <= THRESHOLD_SUBTRANS_CLOG_OPT && nsubxids <= THRESHOLD_SUBTRANS_CLOG_OPT &&
nsubxids == MyPgXact->nxids && nsubxids == MyProc->subxidStatus.count &&
memcmp(subxids, MyProc->subxids.xids, memcmp(subxids, MyProc->subxids.xids,
nsubxids * sizeof(TransactionId)) == 0) nsubxids * sizeof(TransactionId)) == 0)
{ {
@ -510,16 +510,15 @@ TransactionGroupUpdateXidStatus(TransactionId xid, XidStatus status,
while (nextidx != INVALID_PGPROCNO) while (nextidx != INVALID_PGPROCNO)
{ {
PGPROC *proc = &ProcGlobal->allProcs[nextidx]; PGPROC *proc = &ProcGlobal->allProcs[nextidx];
PGXACT *pgxact = &ProcGlobal->allPgXact[nextidx];
/* /*
* Transactions with more than THRESHOLD_SUBTRANS_CLOG_OPT sub-XIDs * Transactions with more than THRESHOLD_SUBTRANS_CLOG_OPT sub-XIDs
* should not use group XID status update mechanism. * should not use group XID status update mechanism.
*/ */
Assert(pgxact->nxids <= THRESHOLD_SUBTRANS_CLOG_OPT); Assert(proc->subxidStatus.count <= THRESHOLD_SUBTRANS_CLOG_OPT);
TransactionIdSetPageStatusInternal(proc->clogGroupMemberXid, TransactionIdSetPageStatusInternal(proc->clogGroupMemberXid,
pgxact->nxids, proc->subxidStatus.count,
proc->subxids.xids, proc->subxids.xids,
proc->clogGroupMemberXidStatus, proc->clogGroupMemberXidStatus,
proc->clogGroupMemberLsn, proc->clogGroupMemberLsn,

View File

@ -21,9 +21,9 @@
* GIDs and aborts the transaction if there already is a global * GIDs and aborts the transaction if there already is a global
* transaction in prepared state with the same GID. * transaction in prepared state with the same GID.
* *
* A global transaction (gxact) also has dummy PGXACT and PGPROC; this is * A global transaction (gxact) also has dummy PGPROC; this is what keeps
* what keeps the XID considered running by TransactionIdIsInProgress. * the XID considered running by TransactionIdIsInProgress. It is also
* It is also convenient as a PGPROC to hook the gxact's locks to. * convenient as a PGPROC to hook the gxact's locks to.
* *
* Information to recover prepared transactions in case of crash is * Information to recover prepared transactions in case of crash is
* now stored in WAL for the common case. In some cases there will be * now stored in WAL for the common case. In some cases there will be
@ -447,14 +447,12 @@ MarkAsPreparingGuts(GlobalTransaction gxact, TransactionId xid, const char *gid,
TimestampTz prepared_at, Oid owner, Oid databaseid) TimestampTz prepared_at, Oid owner, Oid databaseid)
{ {
PGPROC *proc; PGPROC *proc;
PGXACT *pgxact;
int i; int i;
Assert(LWLockHeldByMeInMode(TwoPhaseStateLock, LW_EXCLUSIVE)); Assert(LWLockHeldByMeInMode(TwoPhaseStateLock, LW_EXCLUSIVE));
Assert(gxact != NULL); Assert(gxact != NULL);
proc = &ProcGlobal->allProcs[gxact->pgprocno]; proc = &ProcGlobal->allProcs[gxact->pgprocno];
pgxact = &ProcGlobal->allPgXact[gxact->pgprocno];
/* Initialize the PGPROC entry */ /* Initialize the PGPROC entry */
MemSet(proc, 0, sizeof(PGPROC)); MemSet(proc, 0, sizeof(PGPROC));
@ -480,8 +478,8 @@ MarkAsPreparingGuts(GlobalTransaction gxact, TransactionId xid, const char *gid,
for (i = 0; i < NUM_LOCK_PARTITIONS; i++) for (i = 0; i < NUM_LOCK_PARTITIONS; i++)
SHMQueueInit(&(proc->myProcLocks[i])); SHMQueueInit(&(proc->myProcLocks[i]));
/* subxid data must be filled later by GXactLoadSubxactData */ /* subxid data must be filled later by GXactLoadSubxactData */
pgxact->overflowed = false; proc->subxidStatus.overflowed = false;
pgxact->nxids = 0; proc->subxidStatus.count = 0;
gxact->prepared_at = prepared_at; gxact->prepared_at = prepared_at;
gxact->xid = xid; gxact->xid = xid;
@ -510,19 +508,18 @@ GXactLoadSubxactData(GlobalTransaction gxact, int nsubxacts,
TransactionId *children) TransactionId *children)
{ {
PGPROC *proc = &ProcGlobal->allProcs[gxact->pgprocno]; PGPROC *proc = &ProcGlobal->allProcs[gxact->pgprocno];
PGXACT *pgxact = &ProcGlobal->allPgXact[gxact->pgprocno];
/* We need no extra lock since the GXACT isn't valid yet */ /* We need no extra lock since the GXACT isn't valid yet */
if (nsubxacts > PGPROC_MAX_CACHED_SUBXIDS) if (nsubxacts > PGPROC_MAX_CACHED_SUBXIDS)
{ {
pgxact->overflowed = true; proc->subxidStatus.overflowed = true;
nsubxacts = PGPROC_MAX_CACHED_SUBXIDS; nsubxacts = PGPROC_MAX_CACHED_SUBXIDS;
} }
if (nsubxacts > 0) if (nsubxacts > 0)
{ {
memcpy(proc->subxids.xids, children, memcpy(proc->subxids.xids, children,
nsubxacts * sizeof(TransactionId)); nsubxacts * sizeof(TransactionId));
pgxact->nxids = nsubxacts; proc->subxidStatus.count = nsubxacts;
} }
} }

View File

@ -222,22 +222,31 @@ GetNewTransactionId(bool isSubXact)
*/ */
if (!isSubXact) if (!isSubXact)
{ {
Assert(ProcGlobal->subxidStates[MyProc->pgxactoff].count == 0);
Assert(!ProcGlobal->subxidStates[MyProc->pgxactoff].overflowed);
Assert(MyProc->subxidStatus.count == 0);
Assert(!MyProc->subxidStatus.overflowed);
/* LWLockRelease acts as barrier */ /* LWLockRelease acts as barrier */
MyProc->xid = xid; MyProc->xid = xid;
ProcGlobal->xids[MyProc->pgxactoff] = xid; ProcGlobal->xids[MyProc->pgxactoff] = xid;
} }
else else
{ {
int nxids = MyPgXact->nxids; XidCacheStatus *substat = &ProcGlobal->subxidStates[MyProc->pgxactoff];
int nxids = MyProc->subxidStatus.count;
Assert(substat->count == MyProc->subxidStatus.count);
Assert(substat->overflowed == MyProc->subxidStatus.overflowed);
if (nxids < PGPROC_MAX_CACHED_SUBXIDS) if (nxids < PGPROC_MAX_CACHED_SUBXIDS)
{ {
MyProc->subxids.xids[nxids] = xid; MyProc->subxids.xids[nxids] = xid;
pg_write_barrier(); pg_write_barrier();
MyPgXact->nxids = nxids + 1; MyProc->subxidStatus.count = substat->count = nxids + 1;
} }
else else
MyPgXact->overflowed = true; MyProc->subxidStatus.overflowed = substat->overflowed = true;
} }
LWLockRelease(XidGenLock); LWLockRelease(XidGenLock);

View File

@ -4,9 +4,10 @@
* POSTGRES process array code. * POSTGRES process array code.
* *
* *
* This module maintains arrays of the PGPROC and PGXACT structures for all * This module maintains arrays of PGPROC substructures, as well as associated
* active backends. Although there are several uses for this, the principal * arrays in ProcGlobal, for all active backends. Although there are several
* one is as a means of determining the set of currently running transactions. * uses for this, the principal one is as a means of determining the set of
* currently running transactions.
* *
* Because of various subtle race conditions it is critical that a backend * Because of various subtle race conditions it is critical that a backend
* hold the correct locks while setting or clearing its xid (in * hold the correct locks while setting or clearing its xid (in
@ -85,7 +86,7 @@ typedef struct ProcArrayStruct
/* /*
* Highest subxid that has been removed from KnownAssignedXids array to * Highest subxid that has been removed from KnownAssignedXids array to
* prevent overflow; or InvalidTransactionId if none. We track this for * prevent overflow; or InvalidTransactionId if none. We track this for
* similar reasons to tracking overflowing cached subxids in PGXACT * similar reasons to tracking overflowing cached subxids in PGPROC
* entries. Must hold exclusive ProcArrayLock to change this, and shared * entries. Must hold exclusive ProcArrayLock to change this, and shared
* lock to read it. * lock to read it.
*/ */
@ -96,7 +97,7 @@ typedef struct ProcArrayStruct
/* oldest catalog xmin of any replication slot */ /* oldest catalog xmin of any replication slot */
TransactionId replication_slot_catalog_xmin; TransactionId replication_slot_catalog_xmin;
/* indexes into allPgXact[], has PROCARRAY_MAXPROCS entries */ /* indexes into allProcs[], has PROCARRAY_MAXPROCS entries */
int pgprocnos[FLEXIBLE_ARRAY_MEMBER]; int pgprocnos[FLEXIBLE_ARRAY_MEMBER];
} ProcArrayStruct; } ProcArrayStruct;
@ -239,7 +240,6 @@ typedef struct ComputeXidHorizonsResult
static ProcArrayStruct *procArray; static ProcArrayStruct *procArray;
static PGPROC *allProcs; static PGPROC *allProcs;
static PGXACT *allPgXact;
/* /*
* Bookkeeping for tracking emulated transactions in recovery * Bookkeeping for tracking emulated transactions in recovery
@ -325,8 +325,7 @@ static int KnownAssignedXidsGetAndSetXmin(TransactionId *xarray,
static TransactionId KnownAssignedXidsGetOldestXmin(void); static TransactionId KnownAssignedXidsGetOldestXmin(void);
static void KnownAssignedXidsDisplay(int trace_level); static void KnownAssignedXidsDisplay(int trace_level);
static void KnownAssignedXidsReset(void); static void KnownAssignedXidsReset(void);
static inline void ProcArrayEndTransactionInternal(PGPROC *proc, static inline void ProcArrayEndTransactionInternal(PGPROC *proc, TransactionId latestXid);
PGXACT *pgxact, TransactionId latestXid);
static void ProcArrayGroupClearXid(PGPROC *proc, TransactionId latestXid); static void ProcArrayGroupClearXid(PGPROC *proc, TransactionId latestXid);
static void MaintainLatestCompletedXid(TransactionId latestXid); static void MaintainLatestCompletedXid(TransactionId latestXid);
static void MaintainLatestCompletedXidRecovery(TransactionId latestXid); static void MaintainLatestCompletedXidRecovery(TransactionId latestXid);
@ -411,7 +410,6 @@ CreateSharedProcArray(void)
} }
allProcs = ProcGlobal->allProcs; allProcs = ProcGlobal->allProcs;
allPgXact = ProcGlobal->allPgXact;
/* Create or attach to the KnownAssignedXids arrays too, if needed */ /* Create or attach to the KnownAssignedXids arrays too, if needed */
if (EnableHotStandby) if (EnableHotStandby)
@ -476,11 +474,14 @@ ProcArrayAdd(PGPROC *proc)
(arrayP->numProcs - index) * sizeof(*arrayP->pgprocnos)); (arrayP->numProcs - index) * sizeof(*arrayP->pgprocnos));
memmove(&ProcGlobal->xids[index + 1], &ProcGlobal->xids[index], memmove(&ProcGlobal->xids[index + 1], &ProcGlobal->xids[index],
(arrayP->numProcs - index) * sizeof(*ProcGlobal->xids)); (arrayP->numProcs - index) * sizeof(*ProcGlobal->xids));
memmove(&ProcGlobal->subxidStates[index + 1], &ProcGlobal->subxidStates[index],
(arrayP->numProcs - index) * sizeof(*ProcGlobal->subxidStates));
memmove(&ProcGlobal->vacuumFlags[index + 1], &ProcGlobal->vacuumFlags[index], memmove(&ProcGlobal->vacuumFlags[index + 1], &ProcGlobal->vacuumFlags[index],
(arrayP->numProcs - index) * sizeof(*ProcGlobal->vacuumFlags)); (arrayP->numProcs - index) * sizeof(*ProcGlobal->vacuumFlags));
arrayP->pgprocnos[index] = proc->pgprocno; arrayP->pgprocnos[index] = proc->pgprocno;
ProcGlobal->xids[index] = proc->xid; ProcGlobal->xids[index] = proc->xid;
ProcGlobal->subxidStates[index] = proc->subxidStatus;
ProcGlobal->vacuumFlags[index] = proc->vacuumFlags; ProcGlobal->vacuumFlags[index] = proc->vacuumFlags;
arrayP->numProcs++; arrayP->numProcs++;
@ -534,6 +535,8 @@ ProcArrayRemove(PGPROC *proc, TransactionId latestXid)
MaintainLatestCompletedXid(latestXid); MaintainLatestCompletedXid(latestXid);
ProcGlobal->xids[proc->pgxactoff] = 0; ProcGlobal->xids[proc->pgxactoff] = 0;
ProcGlobal->subxidStates[proc->pgxactoff].overflowed = false;
ProcGlobal->subxidStates[proc->pgxactoff].count = 0;
} }
else else
{ {
@ -542,6 +545,8 @@ ProcArrayRemove(PGPROC *proc, TransactionId latestXid)
} }
Assert(TransactionIdIsValid(ProcGlobal->xids[proc->pgxactoff] == 0)); Assert(TransactionIdIsValid(ProcGlobal->xids[proc->pgxactoff] == 0));
Assert(TransactionIdIsValid(ProcGlobal->subxidStates[proc->pgxactoff].count == 0));
Assert(TransactionIdIsValid(ProcGlobal->subxidStates[proc->pgxactoff].overflowed == false));
ProcGlobal->vacuumFlags[proc->pgxactoff] = 0; ProcGlobal->vacuumFlags[proc->pgxactoff] = 0;
for (index = 0; index < arrayP->numProcs; index++) for (index = 0; index < arrayP->numProcs; index++)
@ -553,6 +558,8 @@ ProcArrayRemove(PGPROC *proc, TransactionId latestXid)
(arrayP->numProcs - index - 1) * sizeof(*arrayP->pgprocnos)); (arrayP->numProcs - index - 1) * sizeof(*arrayP->pgprocnos));
memmove(&ProcGlobal->xids[index], &ProcGlobal->xids[index + 1], memmove(&ProcGlobal->xids[index], &ProcGlobal->xids[index + 1],
(arrayP->numProcs - index - 1) * sizeof(*ProcGlobal->xids)); (arrayP->numProcs - index - 1) * sizeof(*ProcGlobal->xids));
memmove(&ProcGlobal->subxidStates[index], &ProcGlobal->subxidStates[index + 1],
(arrayP->numProcs - index - 1) * sizeof(*ProcGlobal->subxidStates));
memmove(&ProcGlobal->vacuumFlags[index], &ProcGlobal->vacuumFlags[index + 1], memmove(&ProcGlobal->vacuumFlags[index], &ProcGlobal->vacuumFlags[index + 1],
(arrayP->numProcs - index - 1) * sizeof(*ProcGlobal->vacuumFlags)); (arrayP->numProcs - index - 1) * sizeof(*ProcGlobal->vacuumFlags));
@ -597,8 +604,6 @@ ProcArrayRemove(PGPROC *proc, TransactionId latestXid)
void void
ProcArrayEndTransaction(PGPROC *proc, TransactionId latestXid) ProcArrayEndTransaction(PGPROC *proc, TransactionId latestXid)
{ {
PGXACT *pgxact = &allPgXact[proc->pgprocno];
if (TransactionIdIsValid(latestXid)) if (TransactionIdIsValid(latestXid))
{ {
/* /*
@ -616,7 +621,7 @@ ProcArrayEndTransaction(PGPROC *proc, TransactionId latestXid)
*/ */
if (LWLockConditionalAcquire(ProcArrayLock, LW_EXCLUSIVE)) if (LWLockConditionalAcquire(ProcArrayLock, LW_EXCLUSIVE))
{ {
ProcArrayEndTransactionInternal(proc, pgxact, latestXid); ProcArrayEndTransactionInternal(proc, latestXid);
LWLockRelease(ProcArrayLock); LWLockRelease(ProcArrayLock);
} }
else else
@ -630,15 +635,14 @@ ProcArrayEndTransaction(PGPROC *proc, TransactionId latestXid)
* estimate of global xmin, but that's OK. * estimate of global xmin, but that's OK.
*/ */
Assert(!TransactionIdIsValid(proc->xid)); Assert(!TransactionIdIsValid(proc->xid));
Assert(proc->subxidStatus.count == 0);
Assert(!proc->subxidStatus.overflowed);
proc->lxid = InvalidLocalTransactionId; proc->lxid = InvalidLocalTransactionId;
proc->xmin = InvalidTransactionId; proc->xmin = InvalidTransactionId;
proc->delayChkpt = false; /* be sure this is cleared in abort */ proc->delayChkpt = false; /* be sure this is cleared in abort */
proc->recoveryConflictPending = false; proc->recoveryConflictPending = false;
Assert(pgxact->nxids == 0);
Assert(pgxact->overflowed == false);
/* must be cleared with xid/xmin: */ /* must be cleared with xid/xmin: */
/* avoid unnecessarily dirtying shared cachelines */ /* avoid unnecessarily dirtying shared cachelines */
if (proc->vacuumFlags & PROC_VACUUM_STATE_MASK) if (proc->vacuumFlags & PROC_VACUUM_STATE_MASK)
@ -659,8 +663,7 @@ ProcArrayEndTransaction(PGPROC *proc, TransactionId latestXid)
* We don't do any locking here; caller must handle that. * We don't do any locking here; caller must handle that.
*/ */
static inline void static inline void
ProcArrayEndTransactionInternal(PGPROC *proc, PGXACT *pgxact, ProcArrayEndTransactionInternal(PGPROC *proc, TransactionId latestXid)
TransactionId latestXid)
{ {
size_t pgxactoff = proc->pgxactoff; size_t pgxactoff = proc->pgxactoff;
@ -683,8 +686,15 @@ ProcArrayEndTransactionInternal(PGPROC *proc, PGXACT *pgxact,
} }
/* Clear the subtransaction-XID cache too while holding the lock */ /* Clear the subtransaction-XID cache too while holding the lock */
pgxact->nxids = 0; Assert(ProcGlobal->subxidStates[pgxactoff].count == proc->subxidStatus.count &&
pgxact->overflowed = false; ProcGlobal->subxidStates[pgxactoff].overflowed == proc->subxidStatus.overflowed);
if (proc->subxidStatus.count > 0 || proc->subxidStatus.overflowed)
{
ProcGlobal->subxidStates[pgxactoff].count = 0;
ProcGlobal->subxidStates[pgxactoff].overflowed = false;
proc->subxidStatus.count = 0;
proc->subxidStatus.overflowed = false;
}
/* Also advance global latestCompletedXid while holding the lock */ /* Also advance global latestCompletedXid while holding the lock */
MaintainLatestCompletedXid(latestXid); MaintainLatestCompletedXid(latestXid);
@ -774,9 +784,8 @@ ProcArrayGroupClearXid(PGPROC *proc, TransactionId latestXid)
while (nextidx != INVALID_PGPROCNO) while (nextidx != INVALID_PGPROCNO)
{ {
PGPROC *proc = &allProcs[nextidx]; PGPROC *proc = &allProcs[nextidx];
PGXACT *pgxact = &allPgXact[nextidx];
ProcArrayEndTransactionInternal(proc, pgxact, proc->procArrayGroupMemberXid); ProcArrayEndTransactionInternal(proc, proc->procArrayGroupMemberXid);
/* Move to next proc in list. */ /* Move to next proc in list. */
nextidx = pg_atomic_read_u32(&proc->procArrayGroupNext); nextidx = pg_atomic_read_u32(&proc->procArrayGroupNext);
@ -820,7 +829,6 @@ ProcArrayGroupClearXid(PGPROC *proc, TransactionId latestXid)
void void
ProcArrayClearTransaction(PGPROC *proc) ProcArrayClearTransaction(PGPROC *proc)
{ {
PGXACT *pgxact = &allPgXact[proc->pgprocno];
size_t pgxactoff; size_t pgxactoff;
/* /*
@ -845,8 +853,15 @@ ProcArrayClearTransaction(PGPROC *proc)
Assert(!proc->delayChkpt); Assert(!proc->delayChkpt);
/* Clear the subtransaction-XID cache too */ /* Clear the subtransaction-XID cache too */
pgxact->nxids = 0; Assert(ProcGlobal->subxidStates[pgxactoff].count == proc->subxidStatus.count &&
pgxact->overflowed = false; ProcGlobal->subxidStates[pgxactoff].overflowed == proc->subxidStatus.overflowed);
if (proc->subxidStatus.count > 0 || proc->subxidStatus.overflowed)
{
ProcGlobal->subxidStates[pgxactoff].count = 0;
ProcGlobal->subxidStates[pgxactoff].overflowed = false;
proc->subxidStatus.count = 0;
proc->subxidStatus.overflowed = false;
}
LWLockRelease(ProcArrayLock); LWLockRelease(ProcArrayLock);
} }
@ -1267,6 +1282,7 @@ TransactionIdIsInProgress(TransactionId xid)
{ {
static TransactionId *xids = NULL; static TransactionId *xids = NULL;
static TransactionId *other_xids; static TransactionId *other_xids;
XidCacheStatus *other_subxidstates;
int nxids = 0; int nxids = 0;
ProcArrayStruct *arrayP = procArray; ProcArrayStruct *arrayP = procArray;
TransactionId topxid; TransactionId topxid;
@ -1329,6 +1345,7 @@ TransactionIdIsInProgress(TransactionId xid)
} }
other_xids = ProcGlobal->xids; other_xids = ProcGlobal->xids;
other_subxidstates = ProcGlobal->subxidStates;
LWLockAcquire(ProcArrayLock, LW_SHARED); LWLockAcquire(ProcArrayLock, LW_SHARED);
@ -1351,7 +1368,6 @@ TransactionIdIsInProgress(TransactionId xid)
for (size_t pgxactoff = 0; pgxactoff < numProcs; pgxactoff++) for (size_t pgxactoff = 0; pgxactoff < numProcs; pgxactoff++)
{ {
int pgprocno; int pgprocno;
PGXACT *pgxact;
PGPROC *proc; PGPROC *proc;
TransactionId pxid; TransactionId pxid;
int pxids; int pxids;
@ -1386,9 +1402,7 @@ TransactionIdIsInProgress(TransactionId xid)
/* /*
* Step 2: check the cached child-Xids arrays * Step 2: check the cached child-Xids arrays
*/ */
pgprocno = arrayP->pgprocnos[pgxactoff]; pxids = other_subxidstates[pgxactoff].count;
pgxact = &allPgXact[pgprocno];
pxids = pgxact->nxids;
pg_read_barrier(); /* pairs with barrier in GetNewTransactionId() */ pg_read_barrier(); /* pairs with barrier in GetNewTransactionId() */
pgprocno = arrayP->pgprocnos[pgxactoff]; pgprocno = arrayP->pgprocnos[pgxactoff];
proc = &allProcs[pgprocno]; proc = &allProcs[pgprocno];
@ -1412,7 +1426,7 @@ TransactionIdIsInProgress(TransactionId xid)
* we hold ProcArrayLock. So we can't miss an Xid that we need to * we hold ProcArrayLock. So we can't miss an Xid that we need to
* worry about.) * worry about.)
*/ */
if (pgxact->overflowed) if (other_subxidstates[pgxactoff].overflowed)
xids[nxids++] = pxid; xids[nxids++] = pxid;
} }
@ -2019,6 +2033,7 @@ GetSnapshotData(Snapshot snapshot)
size_t numProcs = arrayP->numProcs; size_t numProcs = arrayP->numProcs;
TransactionId *xip = snapshot->xip; TransactionId *xip = snapshot->xip;
int *pgprocnos = arrayP->pgprocnos; int *pgprocnos = arrayP->pgprocnos;
XidCacheStatus *subxidStates = ProcGlobal->subxidStates;
uint8 *allVacuumFlags = ProcGlobal->vacuumFlags; uint8 *allVacuumFlags = ProcGlobal->vacuumFlags;
/* /*
@ -2095,17 +2110,16 @@ GetSnapshotData(Snapshot snapshot)
*/ */
if (!suboverflowed) if (!suboverflowed)
{ {
int pgprocno = pgprocnos[pgxactoff];
PGXACT *pgxact = &allPgXact[pgprocno];
if (pgxact->overflowed) if (subxidStates[pgxactoff].overflowed)
suboverflowed = true; suboverflowed = true;
else else
{ {
int nsubxids = pgxact->nxids; int nsubxids = subxidStates[pgxactoff].count;
if (nsubxids > 0) if (nsubxids > 0)
{ {
int pgprocno = pgprocnos[pgxactoff];
PGPROC *proc = &allProcs[pgprocno]; PGPROC *proc = &allProcs[pgprocno];
pg_read_barrier(); /* pairs with GetNewTransactionId */ pg_read_barrier(); /* pairs with GetNewTransactionId */
@ -2498,8 +2512,6 @@ GetRunningTransactionData(void)
*/ */
for (index = 0; index < arrayP->numProcs; index++) for (index = 0; index < arrayP->numProcs; index++)
{ {
int pgprocno = arrayP->pgprocnos[index];
PGXACT *pgxact = &allPgXact[pgprocno];
TransactionId xid; TransactionId xid;
/* Fetch xid just once - see GetNewTransactionId */ /* Fetch xid just once - see GetNewTransactionId */
@ -2520,7 +2532,7 @@ GetRunningTransactionData(void)
if (TransactionIdPrecedes(xid, oldestRunningXid)) if (TransactionIdPrecedes(xid, oldestRunningXid))
oldestRunningXid = xid; oldestRunningXid = xid;
if (pgxact->overflowed) if (ProcGlobal->subxidStates[index].overflowed)
suboverflowed = true; suboverflowed = true;
/* /*
@ -2540,27 +2552,28 @@ GetRunningTransactionData(void)
*/ */
if (!suboverflowed) if (!suboverflowed)
{ {
XidCacheStatus *other_subxidstates = ProcGlobal->subxidStates;
for (index = 0; index < arrayP->numProcs; index++) for (index = 0; index < arrayP->numProcs; index++)
{ {
int pgprocno = arrayP->pgprocnos[index]; int pgprocno = arrayP->pgprocnos[index];
PGPROC *proc = &allProcs[pgprocno]; PGPROC *proc = &allProcs[pgprocno];
PGXACT *pgxact = &allPgXact[pgprocno]; int nsubxids;
int nxids;
/* /*
* Save subtransaction XIDs. Other backends can't add or remove * Save subtransaction XIDs. Other backends can't add or remove
* entries while we're holding XidGenLock. * entries while we're holding XidGenLock.
*/ */
nxids = pgxact->nxids; nsubxids = other_subxidstates[index].count;
if (nxids > 0) if (nsubxids > 0)
{ {
/* barrier not really required, as XidGenLock is held, but ... */ /* barrier not really required, as XidGenLock is held, but ... */
pg_read_barrier(); /* pairs with GetNewTransactionId */ pg_read_barrier(); /* pairs with GetNewTransactionId */
memcpy(&xids[count], (void *) proc->subxids.xids, memcpy(&xids[count], (void *) proc->subxids.xids,
nxids * sizeof(TransactionId)); nsubxids * sizeof(TransactionId));
count += nxids; count += nsubxids;
subcount += nxids; subcount += nsubxids;
/* /*
* Top-level XID of a transaction is always less than any of * Top-level XID of a transaction is always less than any of
@ -3627,14 +3640,6 @@ ProcArrayGetReplicationSlotXmin(TransactionId *xmin,
LWLockRelease(ProcArrayLock); LWLockRelease(ProcArrayLock);
} }
#define XidCacheRemove(i) \
do { \
MyProc->subxids.xids[i] = MyProc->subxids.xids[MyPgXact->nxids - 1]; \
pg_write_barrier(); \
MyPgXact->nxids--; \
} while (0)
/* /*
* XidCacheRemoveRunningXids * XidCacheRemoveRunningXids
* *
@ -3650,6 +3655,7 @@ XidCacheRemoveRunningXids(TransactionId xid,
{ {
int i, int i,
j; j;
XidCacheStatus *mysubxidstat;
Assert(TransactionIdIsValid(xid)); Assert(TransactionIdIsValid(xid));
@ -3667,6 +3673,8 @@ XidCacheRemoveRunningXids(TransactionId xid,
*/ */
LWLockAcquire(ProcArrayLock, LW_EXCLUSIVE); LWLockAcquire(ProcArrayLock, LW_EXCLUSIVE);
mysubxidstat = &ProcGlobal->subxidStates[MyProc->pgxactoff];
/* /*
* Under normal circumstances xid and xids[] will be in increasing order, * Under normal circumstances xid and xids[] will be in increasing order,
* as will be the entries in subxids. Scan backwards to avoid O(N^2) * as will be the entries in subxids. Scan backwards to avoid O(N^2)
@ -3676,11 +3684,14 @@ XidCacheRemoveRunningXids(TransactionId xid,
{ {
TransactionId anxid = xids[i]; TransactionId anxid = xids[i];
for (j = MyPgXact->nxids - 1; j >= 0; j--) for (j = MyProc->subxidStatus.count - 1; j >= 0; j--)
{ {
if (TransactionIdEquals(MyProc->subxids.xids[j], anxid)) if (TransactionIdEquals(MyProc->subxids.xids[j], anxid))
{ {
XidCacheRemove(j); MyProc->subxids.xids[j] = MyProc->subxids.xids[MyProc->subxidStatus.count - 1];
pg_write_barrier();
mysubxidstat->count--;
MyProc->subxidStatus.count--;
break; break;
} }
} }
@ -3692,20 +3703,23 @@ XidCacheRemoveRunningXids(TransactionId xid,
* error during AbortSubTransaction. So instead of Assert, emit a * error during AbortSubTransaction. So instead of Assert, emit a
* debug warning. * debug warning.
*/ */
if (j < 0 && !MyPgXact->overflowed) if (j < 0 && !MyProc->subxidStatus.overflowed)
elog(WARNING, "did not find subXID %u in MyProc", anxid); elog(WARNING, "did not find subXID %u in MyProc", anxid);
} }
for (j = MyPgXact->nxids - 1; j >= 0; j--) for (j = MyProc->subxidStatus.count - 1; j >= 0; j--)
{ {
if (TransactionIdEquals(MyProc->subxids.xids[j], xid)) if (TransactionIdEquals(MyProc->subxids.xids[j], xid))
{ {
XidCacheRemove(j); MyProc->subxids.xids[j] = MyProc->subxids.xids[MyProc->subxidStatus.count - 1];
pg_write_barrier();
mysubxidstat->count--;
MyProc->subxidStatus.count--;
break; break;
} }
} }
/* Ordinarily we should have found it, unless the cache has overflowed */ /* Ordinarily we should have found it, unless the cache has overflowed */
if (j < 0 && !MyPgXact->overflowed) if (j < 0 && !MyProc->subxidStatus.overflowed)
elog(WARNING, "did not find subXID %u in MyProc", xid); elog(WARNING, "did not find subXID %u in MyProc", xid);
/* Also advance global latestCompletedXid while holding the lock */ /* Also advance global latestCompletedXid while holding the lock */

View File

@ -63,9 +63,8 @@ int LockTimeout = 0;
int IdleInTransactionSessionTimeout = 0; int IdleInTransactionSessionTimeout = 0;
bool log_lock_waits = false; bool log_lock_waits = false;
/* Pointer to this process's PGPROC and PGXACT structs, if any */ /* Pointer to this process's PGPROC struct, if any */
PGPROC *MyProc = NULL; PGPROC *MyProc = NULL;
PGXACT *MyPgXact = NULL;
/* /*
* This spinlock protects the freelist of recycled PGPROC structures. * This spinlock protects the freelist of recycled PGPROC structures.
@ -110,10 +109,8 @@ ProcGlobalShmemSize(void)
size = add_size(size, mul_size(TotalProcs, sizeof(PGPROC))); size = add_size(size, mul_size(TotalProcs, sizeof(PGPROC)));
size = add_size(size, sizeof(slock_t)); size = add_size(size, sizeof(slock_t));
size = add_size(size, mul_size(MaxBackends, sizeof(PGXACT)));
size = add_size(size, mul_size(NUM_AUXILIARY_PROCS, sizeof(PGXACT)));
size = add_size(size, mul_size(max_prepared_xacts, sizeof(PGXACT)));
size = add_size(size, mul_size(TotalProcs, sizeof(*ProcGlobal->xids))); size = add_size(size, mul_size(TotalProcs, sizeof(*ProcGlobal->xids)));
size = add_size(size, mul_size(TotalProcs, sizeof(*ProcGlobal->subxidStates)));
size = add_size(size, mul_size(TotalProcs, sizeof(*ProcGlobal->vacuumFlags))); size = add_size(size, mul_size(TotalProcs, sizeof(*ProcGlobal->vacuumFlags)));
return size; return size;
@ -161,7 +158,6 @@ void
InitProcGlobal(void) InitProcGlobal(void)
{ {
PGPROC *procs; PGPROC *procs;
PGXACT *pgxacts;
int i, int i,
j; j;
bool found; bool found;
@ -202,18 +198,6 @@ InitProcGlobal(void)
/* XXX allProcCount isn't really all of them; it excludes prepared xacts */ /* XXX allProcCount isn't really all of them; it excludes prepared xacts */
ProcGlobal->allProcCount = MaxBackends + NUM_AUXILIARY_PROCS; ProcGlobal->allProcCount = MaxBackends + NUM_AUXILIARY_PROCS;
/*
* Also allocate a separate array of PGXACT structures. This is separate
* from the main PGPROC array so that the most heavily accessed data is
* stored contiguously in memory in as few cache lines as possible. This
* provides significant performance benefits, especially on a
* multiprocessor system. There is one PGXACT structure for every PGPROC
* structure.
*/
pgxacts = (PGXACT *) ShmemAlloc(TotalProcs * sizeof(PGXACT));
MemSet(pgxacts, 0, TotalProcs * sizeof(PGXACT));
ProcGlobal->allPgXact = pgxacts;
/* /*
* Allocate arrays mirroring PGPROC fields in a dense manner. See * Allocate arrays mirroring PGPROC fields in a dense manner. See
* PROC_HDR. * PROC_HDR.
@ -224,6 +208,8 @@ InitProcGlobal(void)
ProcGlobal->xids = ProcGlobal->xids =
(TransactionId *) ShmemAlloc(TotalProcs * sizeof(*ProcGlobal->xids)); (TransactionId *) ShmemAlloc(TotalProcs * sizeof(*ProcGlobal->xids));
MemSet(ProcGlobal->xids, 0, TotalProcs * sizeof(*ProcGlobal->xids)); MemSet(ProcGlobal->xids, 0, TotalProcs * sizeof(*ProcGlobal->xids));
ProcGlobal->subxidStates = (XidCacheStatus *) ShmemAlloc(TotalProcs * sizeof(*ProcGlobal->subxidStates));
MemSet(ProcGlobal->subxidStates, 0, TotalProcs * sizeof(*ProcGlobal->subxidStates));
ProcGlobal->vacuumFlags = (uint8 *) ShmemAlloc(TotalProcs * sizeof(*ProcGlobal->vacuumFlags)); ProcGlobal->vacuumFlags = (uint8 *) ShmemAlloc(TotalProcs * sizeof(*ProcGlobal->vacuumFlags));
MemSet(ProcGlobal->vacuumFlags, 0, TotalProcs * sizeof(*ProcGlobal->vacuumFlags)); MemSet(ProcGlobal->vacuumFlags, 0, TotalProcs * sizeof(*ProcGlobal->vacuumFlags));
@ -372,7 +358,6 @@ InitProcess(void)
(errcode(ERRCODE_TOO_MANY_CONNECTIONS), (errcode(ERRCODE_TOO_MANY_CONNECTIONS),
errmsg("sorry, too many clients already"))); errmsg("sorry, too many clients already")));
} }
MyPgXact = &ProcGlobal->allPgXact[MyProc->pgprocno];
/* /*
* Cross-check that the PGPROC is of the type we expect; if this were not * Cross-check that the PGPROC is of the type we expect; if this were not
@ -569,7 +554,6 @@ InitAuxiliaryProcess(void)
((volatile PGPROC *) auxproc)->pid = MyProcPid; ((volatile PGPROC *) auxproc)->pid = MyProcPid;
MyProc = auxproc; MyProc = auxproc;
MyPgXact = &ProcGlobal->allPgXact[auxproc->pgprocno];
SpinLockRelease(ProcStructLock); SpinLockRelease(ProcStructLock);

View File

@ -35,6 +35,14 @@
*/ */
#define PGPROC_MAX_CACHED_SUBXIDS 64 /* XXX guessed-at value */ #define PGPROC_MAX_CACHED_SUBXIDS 64 /* XXX guessed-at value */
typedef struct XidCacheStatus
{
/* number of cached subxids, never more than PGPROC_MAX_CACHED_SUBXIDS */
uint8 count;
/* has PGPROC->subxids overflowed */
bool overflowed;
} XidCacheStatus;
struct XidCache struct XidCache
{ {
TransactionId xids[PGPROC_MAX_CACHED_SUBXIDS]; TransactionId xids[PGPROC_MAX_CACHED_SUBXIDS];
@ -187,6 +195,8 @@ struct PGPROC
*/ */
SHM_QUEUE myProcLocks[NUM_LOCK_PARTITIONS]; SHM_QUEUE myProcLocks[NUM_LOCK_PARTITIONS];
XidCacheStatus subxidStatus; /* mirrored with
* ProcGlobal->subxidStates[i] */
struct XidCache subxids; /* cache for subtransaction XIDs */ struct XidCache subxids; /* cache for subtransaction XIDs */
/* Support for group XID clearing. */ /* Support for group XID clearing. */
@ -235,22 +245,6 @@ struct PGPROC
extern PGDLLIMPORT PGPROC *MyProc; extern PGDLLIMPORT PGPROC *MyProc;
extern PGDLLIMPORT struct PGXACT *MyPgXact;
/*
* Prior to PostgreSQL 9.2, the fields below were stored as part of the
* PGPROC. However, benchmarking revealed that packing these particular
* members into a separate array as tightly as possible sped up GetSnapshotData
* considerably on systems with many CPU cores, by reducing the number of
* cache lines needing to be fetched. Thus, think very carefully before adding
* anything else here.
*/
typedef struct PGXACT
{
bool overflowed;
uint8 nxids;
} PGXACT;
/* /*
* There is one ProcGlobal struct for the whole database cluster. * There is one ProcGlobal struct for the whole database cluster.
@ -310,12 +304,16 @@ typedef struct PROC_HDR
{ {
/* Array of PGPROC structures (not including dummies for prepared txns) */ /* Array of PGPROC structures (not including dummies for prepared txns) */
PGPROC *allProcs; PGPROC *allProcs;
/* Array of PGXACT structures (not including dummies for prepared txns) */
PGXACT *allPgXact;
/* Array mirroring PGPROC.xid for each PGPROC currently in the procarray */ /* Array mirroring PGPROC.xid for each PGPROC currently in the procarray */
TransactionId *xids; TransactionId *xids;
/*
* Array mirroring PGPROC.subxidStatus for each PGPROC currently in the
* procarray.
*/
XidCacheStatus *subxidStates;
/* /*
* Array mirroring PGPROC.vacuumFlags for each PGPROC currently in the * Array mirroring PGPROC.vacuumFlags for each PGPROC currently in the
* procarray. * procarray.

View File

@ -1536,7 +1536,6 @@ PGSetenvStatusType
PGShmemHeader PGShmemHeader
PGTransactionStatusType PGTransactionStatusType
PGVerbosity PGVerbosity
PGXACT
PG_Locale_Strategy PG_Locale_Strategy
PG_Lock_Status PG_Lock_Status
PG_init_t PG_init_t