From d445131577310f5332aaa2129dbf79b70932d086 Mon Sep 17 00:00:00 2001 From: Michail Nikolaev Date: Sun, 21 Nov 2021 21:37:29 +0300 Subject: [PATCH v3 2/2] know assignment xid next --- src/backend/storage/ipc/procarray.c | 60 ++++++++++++++++++++++++----- 1 file changed, 50 insertions(+), 10 deletions(-) diff --git a/src/backend/storage/ipc/procarray.c b/src/backend/storage/ipc/procarray.c index da0c4eaa00..61753e8c6e 100644 --- a/src/backend/storage/ipc/procarray.c +++ b/src/backend/storage/ipc/procarray.c @@ -265,6 +265,7 @@ static PGPROC *allProcs; */ static TransactionId *KnownAssignedXids; static bool *KnownAssignedXidsValid; +static int32 *KnownAssignedXidsNext; static TransactionId latestObservedXid = InvalidTransactionId; /* @@ -443,6 +444,12 @@ CreateSharedProcArray(void) ShmemInitStruct("KnownAssignedXidsValid", mul_size(sizeof(bool), TOTAL_MAX_CACHED_SUBXIDS), &found); + KnownAssignedXidsNext = (int32 *) + ShmemInitStruct("KnownAssignedXidsNext", + mul_size(sizeof(int32), TOTAL_MAX_CACHED_SUBXIDS), + &found); + for (int i = 0; i < TOTAL_MAX_CACHED_SUBXIDS; i++) + KnownAssignedXidsNext[i] = 1; } } @@ -4527,7 +4534,13 @@ ExpireOldKnownAssignedTransactionIds(TransactionId xid) * XID entry itself. This preserves the property that the XID entries are * sorted, so we can do binary searches easily. Periodically we compress * out the unused entries; that's much cheaper than having to compress the - * array immediately on every deletion. + * array immediately on every deletion. Also, we lazily maintain an offset + * in KnownAssignedXidsNext[] array to skip known to be invalid xids. It + * helps to skip the gaps; it could significantly increase performance in + * the case of long transactions on the primary. KnownAssignedXidsNext[] is + * updating while taking the snapshot. In general case KnownAssignedXidsNext + * contains not an offset to the next valid xid but a number which tends to + * the offset to next valid xid. * * The actually valid items in KnownAssignedXids[] and KnownAssignedXidsValid[] * are those with indexes tail <= i < head; items outside this subscript range @@ -4553,7 +4566,10 @@ ExpireOldKnownAssignedTransactionIds(TransactionId xid) * We handle this by using a memory barrier to protect writes of the * head pointer. * The memory barrier is taken before write the head pointer unless - * the caller holds ProcArrayLock exclusively. + * the caller holds ProcArrayLock exclusively. Access to KnownAssignedXidsNext + * is not especially protected by any lock because it just some kind of hint + * to reduce the scan cost, but at least shared ProcArrayLock is held anyway - + * it is required to access KnownAssignedXids. * * Algorithmic analysis: * @@ -4564,7 +4580,7 @@ ExpireOldKnownAssignedTransactionIds(TransactionId xid) * must happen) * * Compressing the array is O(S) and requires exclusive lock * * Removing an XID is O(logS) and requires exclusive lock - * * Taking a snapshot is O(S) and requires shared lock + * * Taking a snapshot is O(S), O(N) next call; requires shared lock * * Checking for an XID is O(logS) and requires shared lock * * In comparison, using a hash table for KnownAssignedXids would mean that @@ -4623,12 +4639,13 @@ KnownAssignedXidsCompress(bool force) * re-aligning data to 0th element. */ compress_index = 0; - for (i = tail; i < head; i++) + for (i = tail; i < head; i += KnownAssignedXidsNext[i]) { if (KnownAssignedXidsValid[i]) { KnownAssignedXids[compress_index] = KnownAssignedXids[i]; KnownAssignedXidsValid[compress_index] = true; + KnownAssignedXidsNext[compress_index] = 1; compress_index++; } } @@ -4731,6 +4748,7 @@ KnownAssignedXidsAdd(TransactionId from_xid, TransactionId to_xid, { KnownAssignedXids[head] = next_xid; KnownAssignedXidsValid[head] = true; + KnownAssignedXidsNext[head] = 1; TransactionIdAdvance(next_xid); head++; } @@ -4933,7 +4951,7 @@ KnownAssignedXidsRemovePreceding(TransactionId removeXid) tail = pArray->tailKnownAssignedXids; head = pArray->headKnownAssignedXids; - for (i = tail; i < head; i++) + for (i = tail; i < head; i += KnownAssignedXidsNext[i]) { if (KnownAssignedXidsValid[i]) { @@ -4956,7 +4974,7 @@ KnownAssignedXidsRemovePreceding(TransactionId removeXid) /* * Advance the tail pointer if we've marked the tail item invalid. */ - for (i = tail; i < head; i++) + for (i = tail; i < head; i += KnownAssignedXidsNext[i]) { if (KnownAssignedXidsValid[i]) break; @@ -5006,7 +5024,9 @@ KnownAssignedXidsGetAndSetXmin(TransactionId *xarray, TransactionId *xmin, int count = 0; int head, tail; - int i; + int i, + prev, + prevOffset; /* * Fetch head just once, since it may change while we loop. We can stop @@ -5017,8 +5037,10 @@ KnownAssignedXidsGetAndSetXmin(TransactionId *xarray, TransactionId *xmin, */ tail = procArray->tailKnownAssignedXids; head = procArray->headKnownAssignedXids; + prev = tail; + prevOffset = KnownAssignedXidsNext[prev]; - for (i = tail; i < head; i++) + for (i = tail; i < head; i += KnownAssignedXidsNext[i]) { /* Skip any gaps in the array */ if (KnownAssignedXidsValid[i]) @@ -5043,6 +5065,24 @@ KnownAssignedXidsGetAndSetXmin(TransactionId *xarray, TransactionId *xmin, /* Add knownXid into output array */ xarray[count++] = knownXid; + + if (prev != i) + { + int32 n = i - prev; + /** + * Do not touch the cache if value unchanged. This way we + * could avoid additional cache miss. + */ + if (n != prevOffset) + KnownAssignedXidsNext[prev] = n; + /** + * Remember this xid as previous valid. Also, manually store + * prevOffset from current fetched value to avoid additional + * atomic read. + */ + prev = i; + prevOffset = KnownAssignedXidsNext[i]; + } } } @@ -5066,7 +5106,7 @@ KnownAssignedXidsGetOldestXmin(void) tail = procArray->tailKnownAssignedXids; head = procArray->headKnownAssignedXids; - for (i = tail; i < head; i++) + for (i = tail; i < head; i += KnownAssignedXidsNext[i]) { /* Skip any gaps in the array */ if (KnownAssignedXidsValid[i]) @@ -5101,7 +5141,7 @@ KnownAssignedXidsDisplay(int trace_level) initStringInfo(&buf); - for (i = tail; i < head; i++) + for (i = tail; i < head; i += KnownAssignedXidsNext[i]) { if (KnownAssignedXidsValid[i]) { -- 2.25.1