From 7f584b2296512181d86a82aa6ebb7cb6794a9c4a Mon Sep 17 00:00:00 2001
From: Andrey Borodin <x4m@flight.local>
Date: Sun, 26 Dec 2021 15:03:30 +0500
Subject: [PATCH v20 3/3] Pack SLRU page_number, page_status and page_dirty
 toogether

This allows to test only one cacheline during successfull
SlruSelectLRUPage().
---
 src/backend/access/transam/clog.c      |  12 +-
 src/backend/access/transam/commit_ts.c |   6 +-
 src/backend/access/transam/multixact.c |  16 +--
 src/backend/access/transam/slru.c      | 150 ++++++++++++-------------
 src/backend/access/transam/subtrans.c  |   4 +-
 src/backend/commands/async.c           |   2 +-
 src/backend/storage/lmgr/predicate.c   |   2 +-
 src/include/access/slru.h              |  14 ++-
 8 files changed, 104 insertions(+), 102 deletions(-)

diff --git a/src/backend/access/transam/clog.c b/src/backend/access/transam/clog.c
index 23922abc60..afb6c4ea57 100644
--- a/src/backend/access/transam/clog.c
+++ b/src/backend/access/transam/clog.c
@@ -375,7 +375,7 @@ TransactionIdSetPageStatusInternal(TransactionId xid, int nsubxids,
 		{
 			for (i = 0; i < nsubxids; i++)
 			{
-				Assert(XactCtl->shared->page_number[slotno] == TransactionIdToPage(subxids[i]));
+				Assert(XactCtl->shared->page_entries[slotno].page_number == TransactionIdToPage(subxids[i]));
 				TransactionIdSetStatusBit(subxids[i],
 										  TRANSACTION_STATUS_SUB_COMMITTED,
 										  lsn, slotno);
@@ -389,11 +389,11 @@ TransactionIdSetPageStatusInternal(TransactionId xid, int nsubxids,
 	/* Set the subtransactions */
 	for (i = 0; i < nsubxids; i++)
 	{
-		Assert(XactCtl->shared->page_number[slotno] == TransactionIdToPage(subxids[i]));
+		Assert(XactCtl->shared->page_entries[slotno].page_number == TransactionIdToPage(subxids[i]));
 		TransactionIdSetStatusBit(subxids[i], status, lsn, slotno);
 	}
 
-	XactCtl->shared->page_dirty[slotno] = true;
+	XactCtl->shared->page_entries[slotno].page_dirty = true;
 }
 
 /*
@@ -713,7 +713,7 @@ BootStrapCLOG(void)
 
 	/* Make sure it's written out */
 	SimpleLruWritePage(XactCtl, slotno);
-	Assert(!XactCtl->shared->page_dirty[slotno]);
+	Assert(!XactCtl->shared->page_entries[slotno].page_dirty);
 
 	LWLockRelease(XactSLRULock);
 }
@@ -798,7 +798,7 @@ TrimCLOG(void)
 		/* Zero the rest of the page */
 		MemSet(byteptr + 1, 0, BLCKSZ - byteno - 1);
 
-		XactCtl->shared->page_dirty[slotno] = true;
+		XactCtl->shared->page_entries[slotno].page_dirty = true;
 	}
 
 	LWLockRelease(XactSLRULock);
@@ -994,7 +994,7 @@ clog_redo(XLogReaderState *record)
 
 		slotno = ZeroCLOGPage(pageno, false);
 		SimpleLruWritePage(XactCtl, slotno);
-		Assert(!XactCtl->shared->page_dirty[slotno]);
+		Assert(!XactCtl->shared->page_entries[slotno].page_dirty);
 
 		LWLockRelease(XactSLRULock);
 	}
diff --git a/src/backend/access/transam/commit_ts.c b/src/backend/access/transam/commit_ts.c
index 1247691e79..72bd116eef 100644
--- a/src/backend/access/transam/commit_ts.c
+++ b/src/backend/access/transam/commit_ts.c
@@ -227,7 +227,7 @@ SetXidCommitTsInPage(TransactionId xid, int nsubxids,
 	for (i = 0; i < nsubxids; i++)
 		TransactionIdSetCommitTs(subxids[i], ts, nodeid, slotno);
 
-	CommitTsCtl->shared->page_dirty[slotno] = true;
+	CommitTsCtl->shared->page_entries[slotno].page_dirty = true;
 
 	LWLockRelease(CommitTsSLRULock);
 }
@@ -735,7 +735,7 @@ ActivateCommitTs(void)
 		LWLockAcquire(CommitTsSLRULock, LW_EXCLUSIVE);
 		slotno = ZeroCommitTsPage(pageno, false);
 		SimpleLruWritePage(CommitTsCtl, slotno);
-		Assert(!CommitTsCtl->shared->page_dirty[slotno]);
+		Assert(!CommitTsCtl->shared->page_entries[slotno].page_dirty);
 		LWLockRelease(CommitTsSLRULock);
 	}
 
@@ -1005,7 +1005,7 @@ commit_ts_redo(XLogReaderState *record)
 
 		slotno = ZeroCommitTsPage(pageno, false);
 		SimpleLruWritePage(CommitTsCtl, slotno);
-		Assert(!CommitTsCtl->shared->page_dirty[slotno]);
+		Assert(!CommitTsCtl->shared->page_entries[slotno].page_dirty);
 
 		LWLockRelease(CommitTsSLRULock);
 	}
diff --git a/src/backend/access/transam/multixact.c b/src/backend/access/transam/multixact.c
index 3c86bd4517..276584d45e 100644
--- a/src/backend/access/transam/multixact.c
+++ b/src/backend/access/transam/multixact.c
@@ -887,7 +887,7 @@ RecordNewMultiXact(MultiXactId multi, MultiXactOffset offset,
 
 	*offptr = offset;
 
-	MultiXactOffsetCtl->shared->page_dirty[slotno] = true;
+	MultiXactOffsetCtl->shared->page_entries[slotno].page_dirty = true;
 
 	/* Exchange our lock */
 	LWLockRelease(MultiXactOffsetSLRULock);
@@ -931,7 +931,7 @@ RecordNewMultiXact(MultiXactId multi, MultiXactOffset offset,
 		flagsval |= (members[i].status << bshift);
 		*flagsptr = flagsval;
 
-		MultiXactMemberCtl->shared->page_dirty[slotno] = true;
+		MultiXactMemberCtl->shared->page_entries[slotno].page_dirty = true;
 	}
 
 	LWLockRelease(MultiXactMemberSLRULock);
@@ -1902,7 +1902,7 @@ BootStrapMultiXact(void)
 
 	/* Make sure it's written out */
 	SimpleLruWritePage(MultiXactOffsetCtl, slotno);
-	Assert(!MultiXactOffsetCtl->shared->page_dirty[slotno]);
+	Assert(!MultiXactOffsetCtl->shared->page_entries[slotno].page_dirty);
 
 	LWLockRelease(MultiXactOffsetSLRULock);
 
@@ -1913,7 +1913,7 @@ BootStrapMultiXact(void)
 
 	/* Make sure it's written out */
 	SimpleLruWritePage(MultiXactMemberCtl, slotno);
-	Assert(!MultiXactMemberCtl->shared->page_dirty[slotno]);
+	Assert(!MultiXactMemberCtl->shared->page_entries[slotno].page_dirty);
 
 	LWLockRelease(MultiXactMemberSLRULock);
 }
@@ -2074,7 +2074,7 @@ TrimMultiXact(void)
 
 		MemSet(offptr, 0, BLCKSZ - (entryno * sizeof(MultiXactOffset)));
 
-		MultiXactOffsetCtl->shared->page_dirty[slotno] = true;
+		MultiXactOffsetCtl->shared->page_entries[slotno].page_dirty = true;
 	}
 
 	LWLockRelease(MultiXactOffsetSLRULock);
@@ -2112,7 +2112,7 @@ TrimMultiXact(void)
 		 * writing.
 		 */
 
-		MultiXactMemberCtl->shared->page_dirty[slotno] = true;
+		MultiXactMemberCtl->shared->page_entries[slotno].page_dirty = true;
 	}
 
 	LWLockRelease(MultiXactMemberSLRULock);
@@ -3251,7 +3251,7 @@ multixact_redo(XLogReaderState *record)
 
 		slotno = ZeroMultiXactOffsetPage(pageno, false);
 		SimpleLruWritePage(MultiXactOffsetCtl, slotno);
-		Assert(!MultiXactOffsetCtl->shared->page_dirty[slotno]);
+		Assert(!MultiXactOffsetCtl->shared->page_entries[slotno].page_dirty);
 
 		LWLockRelease(MultiXactOffsetSLRULock);
 	}
@@ -3266,7 +3266,7 @@ multixact_redo(XLogReaderState *record)
 
 		slotno = ZeroMultiXactMemberPage(pageno, false);
 		SimpleLruWritePage(MultiXactMemberCtl, slotno);
-		Assert(!MultiXactMemberCtl->shared->page_dirty[slotno]);
+		Assert(!MultiXactMemberCtl->shared->page_entries[slotno].page_dirty);
 
 		LWLockRelease(MultiXactMemberSLRULock);
 	}
diff --git a/src/backend/access/transam/slru.c b/src/backend/access/transam/slru.c
index f34a2e3fdd..6a43381f81 100644
--- a/src/backend/access/transam/slru.c
+++ b/src/backend/access/transam/slru.c
@@ -157,8 +157,8 @@ static void SlruInternalDeleteSegment(SlruCtl ctl, int segno);
  */
 static void SlruAdjustNSlots(int* nslots, int* banksize, int* bankoffset)
 {
-	*banksize = *nslots;
 	int nbanks = 1;
+	*banksize = *nslots;
 	*bankoffset = 0;
 	while (*banksize > 15)
 	{
@@ -181,14 +181,13 @@ SimpleLruShmemSize(int nslots, int nlsns)
 {
 	Size		sz;
 	int bankoffset, banksize;
+	Assert(sizeof(SlruPageEntry)%8 == 0);
 	SlruAdjustNSlots(&nslots, &banksize, &bankoffset);
 
 	/* we assume nslots isn't so large as to risk overflow */
 	sz = MAXALIGN(sizeof(SlruSharedData));
 	sz += MAXALIGN(nslots * sizeof(char *));	/* page_buffer[] */
-	sz += MAXALIGN(nslots * sizeof(SlruPageStatus));	/* page_status[] */
-	sz += MAXALIGN(nslots * sizeof(bool));	/* page_dirty[] */
-	sz += MAXALIGN(nslots * sizeof(int));	/* page_number[] */
+	sz += MAXALIGN(nslots * sizeof(SlruPageEntry));	/* page_entries[] */
 	sz += MAXALIGN(nslots * sizeof(int));	/* page_lru_count[] */
 	sz += MAXALIGN(nslots * sizeof(LWLockPadded));	/* buffer_locks[] */
 
@@ -248,16 +247,13 @@ SimpleLruInit(SlruCtl ctl, const char *name, int nslots, int nlsns,
 
 		shared->slru_stats_idx = pgstat_slru_index(name);
 
+		Assert(sizeof(SlruPageEntry) == 8);
 		ptr = (char *) shared;
 		offset = MAXALIGN(sizeof(SlruSharedData));
 		shared->page_buffer = (char **) (ptr + offset);
 		offset += MAXALIGN(nslots * sizeof(char *));
-		shared->page_status = (SlruPageStatus *) (ptr + offset);
-		offset += MAXALIGN(nslots * sizeof(SlruPageStatus));
-		shared->page_dirty = (bool *) (ptr + offset);
-		offset += MAXALIGN(nslots * sizeof(bool));
-		shared->page_number = (int *) (ptr + offset);
-		offset += MAXALIGN(nslots * sizeof(int));
+		shared->page_entries = (SlruPageEntry *) (ptr + offset);
+		offset += MAXALIGN(nslots * sizeof(SlruPageEntry));
 		shared->page_lru_count = (int *) (ptr + offset);
 		offset += MAXALIGN(nslots * sizeof(int));
 
@@ -278,8 +274,8 @@ SimpleLruInit(SlruCtl ctl, const char *name, int nslots, int nlsns,
 							 tranche_id);
 
 			shared->page_buffer[slotno] = ptr;
-			shared->page_status[slotno] = SLRU_PAGE_EMPTY;
-			shared->page_dirty[slotno] = false;
+			shared->page_entries[slotno].page_status = SLRU_PAGE_EMPTY;
+			shared->page_entries[slotno].page_dirty = false;
 			shared->page_lru_count[slotno] = 0;
 			ptr += BLCKSZ;
 		}
@@ -315,15 +311,15 @@ SimpleLruZeroPage(SlruCtl ctl, int pageno)
 
 	/* Find a suitable buffer slot for the page */
 	slotno = SlruSelectLRUPage(ctl, pageno);
-	Assert(shared->page_status[slotno] == SLRU_PAGE_EMPTY ||
-		   (shared->page_status[slotno] == SLRU_PAGE_VALID &&
-			!shared->page_dirty[slotno]) ||
-		   shared->page_number[slotno] == pageno);
+	Assert(shared->page_entries[slotno].page_status == SLRU_PAGE_EMPTY ||
+		   (shared->page_entries[slotno].page_status == SLRU_PAGE_VALID &&
+			!shared->page_entries[slotno].page_dirty) ||
+		   shared->page_entries[slotno].page_number == pageno);
 
 	/* Mark the slot as containing this page */
-	shared->page_number[slotno] = pageno;
-	shared->page_status[slotno] = SLRU_PAGE_VALID;
-	shared->page_dirty[slotno] = true;
+	shared->page_entries[slotno].page_number = pageno;
+	shared->page_entries[slotno].page_status = SLRU_PAGE_VALID;
+	shared->page_entries[slotno].page_dirty = true;
 	SlruRecentlyUsed(shared, slotno);
 
 	/* Set the buffer to zeroes */
@@ -387,18 +383,18 @@ SimpleLruWaitIO(SlruCtl ctl, int slotno)
 	 * cheaply test for failure by seeing if the buffer lock is still held (we
 	 * assume that transaction abort would release the lock).
 	 */
-	if (shared->page_status[slotno] == SLRU_PAGE_READ_IN_PROGRESS ||
-		shared->page_status[slotno] == SLRU_PAGE_WRITE_IN_PROGRESS)
+	if (shared->page_entries[slotno].page_status == SLRU_PAGE_READ_IN_PROGRESS ||
+		shared->page_entries[slotno].page_status == SLRU_PAGE_WRITE_IN_PROGRESS)
 	{
 		if (LWLockConditionalAcquire(&shared->buffer_locks[slotno].lock, LW_SHARED))
 		{
 			/* indeed, the I/O must have failed */
-			if (shared->page_status[slotno] == SLRU_PAGE_READ_IN_PROGRESS)
-				shared->page_status[slotno] = SLRU_PAGE_EMPTY;
+			if (shared->page_entries[slotno].page_status == SLRU_PAGE_READ_IN_PROGRESS)
+				shared->page_entries[slotno].page_status = SLRU_PAGE_EMPTY;
 			else				/* write_in_progress */
 			{
-				shared->page_status[slotno] = SLRU_PAGE_VALID;
-				shared->page_dirty[slotno] = true;
+				shared->page_entries[slotno].page_status = SLRU_PAGE_VALID;
+				shared->page_entries[slotno].page_dirty = true;
 			}
 			LWLockRelease(&shared->buffer_locks[slotno].lock);
 		}
@@ -438,15 +434,15 @@ SimpleLruReadPage(SlruCtl ctl, int pageno, bool write_ok,
 		slotno = SlruSelectLRUPage(ctl, pageno);
 
 		/* Did we find the page in memory? */
-		if (shared->page_number[slotno] == pageno &&
-			shared->page_status[slotno] != SLRU_PAGE_EMPTY)
+		if (shared->page_entries[slotno].page_number == pageno &&
+			shared->page_entries[slotno].page_status != SLRU_PAGE_EMPTY)
 		{
 			/*
 			 * If page is still being read in, we must wait for I/O.  Likewise
 			 * if the page is being written and the caller said that's not OK.
 			 */
-			if (shared->page_status[slotno] == SLRU_PAGE_READ_IN_PROGRESS ||
-				(shared->page_status[slotno] == SLRU_PAGE_WRITE_IN_PROGRESS &&
+			if (shared->page_entries[slotno].page_status == SLRU_PAGE_READ_IN_PROGRESS ||
+				(shared->page_entries[slotno].page_status == SLRU_PAGE_WRITE_IN_PROGRESS &&
 				 !write_ok))
 			{
 				SimpleLruWaitIO(ctl, slotno);
@@ -463,14 +459,14 @@ SimpleLruReadPage(SlruCtl ctl, int pageno, bool write_ok,
 		}
 
 		/* We found no match; assert we selected a freeable slot */
-		Assert(shared->page_status[slotno] == SLRU_PAGE_EMPTY ||
-			   (shared->page_status[slotno] == SLRU_PAGE_VALID &&
-				!shared->page_dirty[slotno]));
+		Assert(shared->page_entries[slotno].page_status == SLRU_PAGE_EMPTY ||
+			   (shared->page_entries[slotno].page_status == SLRU_PAGE_VALID &&
+				!shared->page_entries[slotno].page_dirty));
 
 		/* Mark the slot read-busy */
-		shared->page_number[slotno] = pageno;
-		shared->page_status[slotno] = SLRU_PAGE_READ_IN_PROGRESS;
-		shared->page_dirty[slotno] = false;
+		shared->page_entries[slotno].page_number = pageno;
+		shared->page_entries[slotno].page_status = SLRU_PAGE_READ_IN_PROGRESS;
+		shared->page_entries[slotno].page_dirty = false;
 
 		/* Acquire per-buffer lock (cannot deadlock, see notes at top) */
 		LWLockAcquire(&shared->buffer_locks[slotno].lock, LW_EXCLUSIVE);
@@ -487,11 +483,11 @@ SimpleLruReadPage(SlruCtl ctl, int pageno, bool write_ok,
 		/* Re-acquire control lock and update page state */
 		LWLockAcquire(shared->ControlLock, LW_EXCLUSIVE);
 
-		Assert(shared->page_number[slotno] == pageno &&
-			   shared->page_status[slotno] == SLRU_PAGE_READ_IN_PROGRESS &&
-			   !shared->page_dirty[slotno]);
+		Assert(shared->page_entries[slotno].page_number == pageno &&
+			   shared->page_entries[slotno].page_status == SLRU_PAGE_READ_IN_PROGRESS &&
+			   !shared->page_entries[slotno].page_dirty);
 
-		shared->page_status[slotno] = ok ? SLRU_PAGE_VALID : SLRU_PAGE_EMPTY;
+		shared->page_entries[slotno].page_status = ok ? SLRU_PAGE_VALID : SLRU_PAGE_EMPTY;
 
 		LWLockRelease(&shared->buffer_locks[slotno].lock);
 
@@ -527,18 +523,18 @@ SimpleLruReadPage_ReadOnly(SlruCtl ctl, int pageno, TransactionId xid)
 {
 	SlruShared	shared = ctl->shared;
 	int			slotno;
+	int			bankstart = (pageno & shared->bank_mask) * shared->bank_size;
+	int			bankend = bankstart + shared->bank_size;
 
 	/* Try to find the page while holding only shared lock */
 	LWLockAcquire(shared->ControlLock, LW_SHARED);
 
 	/* See if page is already in a buffer */
-	int bankstart = (pageno & shared->bank_mask) * shared->bank_size;
-	int bankend = bankstart + shared->bank_size;
 	for (slotno = bankstart; slotno < bankend; slotno++)
 	{
-		if (shared->page_number[slotno] == pageno &&
-			shared->page_status[slotno] != SLRU_PAGE_EMPTY &&
-			shared->page_status[slotno] != SLRU_PAGE_READ_IN_PROGRESS)
+		if (shared->page_entries[slotno].page_number == pageno &&
+			shared->page_entries[slotno].page_status != SLRU_PAGE_EMPTY &&
+			shared->page_entries[slotno].page_status != SLRU_PAGE_READ_IN_PROGRESS)
 		{
 			/* See comments for SlruRecentlyUsed macro */
 			SlruRecentlyUsed(shared, slotno);
@@ -572,12 +568,12 @@ static void
 SlruInternalWritePage(SlruCtl ctl, int slotno, SlruWriteAll fdata)
 {
 	SlruShared	shared = ctl->shared;
-	int			pageno = shared->page_number[slotno];
+	int			pageno = shared->page_entries[slotno].page_number;
 	bool		ok;
 
 	/* If a write is in progress, wait for it to finish */
-	while (shared->page_status[slotno] == SLRU_PAGE_WRITE_IN_PROGRESS &&
-		   shared->page_number[slotno] == pageno)
+	while (shared->page_entries[slotno].page_status == SLRU_PAGE_WRITE_IN_PROGRESS &&
+		   shared->page_entries[slotno].page_number == pageno)
 	{
 		SimpleLruWaitIO(ctl, slotno);
 	}
@@ -586,17 +582,17 @@ SlruInternalWritePage(SlruCtl ctl, int slotno, SlruWriteAll fdata)
 	 * Do nothing if page is not dirty, or if buffer no longer contains the
 	 * same page we were called for.
 	 */
-	if (!shared->page_dirty[slotno] ||
-		shared->page_status[slotno] != SLRU_PAGE_VALID ||
-		shared->page_number[slotno] != pageno)
+	if (!shared->page_entries[slotno].page_dirty ||
+		shared->page_entries[slotno].page_status != SLRU_PAGE_VALID ||
+		shared->page_entries[slotno].page_number != pageno)
 		return;
 
 	/*
 	 * Mark the slot write-busy, and clear the dirtybit.  After this point, a
 	 * transaction status update on this page will mark it dirty again.
 	 */
-	shared->page_status[slotno] = SLRU_PAGE_WRITE_IN_PROGRESS;
-	shared->page_dirty[slotno] = false;
+	shared->page_entries[slotno].page_status = SLRU_PAGE_WRITE_IN_PROGRESS;
+	shared->page_entries[slotno].page_dirty = false;
 
 	/* Acquire per-buffer lock (cannot deadlock, see notes at top) */
 	LWLockAcquire(&shared->buffer_locks[slotno].lock, LW_EXCLUSIVE);
@@ -619,14 +615,14 @@ SlruInternalWritePage(SlruCtl ctl, int slotno, SlruWriteAll fdata)
 	/* Re-acquire control lock and update page state */
 	LWLockAcquire(shared->ControlLock, LW_EXCLUSIVE);
 
-	Assert(shared->page_number[slotno] == pageno &&
-		   shared->page_status[slotno] == SLRU_PAGE_WRITE_IN_PROGRESS);
+	Assert(shared->page_entries[slotno].page_number == pageno &&
+		   shared->page_entries[slotno].page_status == SLRU_PAGE_WRITE_IN_PROGRESS);
 
 	/* If we failed to write, mark the page dirty again */
 	if (!ok)
-		shared->page_dirty[slotno] = true;
+		shared->page_entries[slotno].page_dirty = true;
 
-	shared->page_status[slotno] = SLRU_PAGE_VALID;
+	shared->page_entries[slotno].page_status = SLRU_PAGE_VALID;
 
 	LWLockRelease(&shared->buffer_locks[slotno].lock);
 
@@ -1067,8 +1063,8 @@ SlruSelectLRUPage(SlruCtl ctl, int pageno)
 		int bankend = bankstart + shared->bank_size;
 		for (slotno = bankstart; slotno < bankend; slotno++)
 		{
-			if (shared->page_number[slotno] == pageno &&
-				shared->page_status[slotno] != SLRU_PAGE_EMPTY)
+			if (shared->page_entries[slotno].page_number == pageno &&
+				shared->page_entries[slotno].page_status != SLRU_PAGE_EMPTY)
 				return slotno;
 		}
 
@@ -1105,7 +1101,7 @@ SlruSelectLRUPage(SlruCtl ctl, int pageno)
 			int			this_delta;
 			int			this_page_number;
 
-			if (shared->page_status[slotno] == SLRU_PAGE_EMPTY)
+			if (shared->page_entries[slotno].page_status == SLRU_PAGE_EMPTY)
 				return slotno;
 			this_delta = cur_count - shared->page_lru_count[slotno];
 			if (this_delta < 0)
@@ -1120,10 +1116,10 @@ SlruSelectLRUPage(SlruCtl ctl, int pageno)
 				shared->page_lru_count[slotno] = cur_count;
 				this_delta = 0;
 			}
-			this_page_number = shared->page_number[slotno];
+			this_page_number = shared->page_entries[slotno].page_number;
 			if (this_page_number == shared->latest_page_number)
 				continue;
-			if (shared->page_status[slotno] == SLRU_PAGE_VALID)
+			if (shared->page_entries[slotno].page_status == SLRU_PAGE_VALID)
 			{
 				if (this_delta > best_valid_delta ||
 					(this_delta == best_valid_delta &&
@@ -1165,7 +1161,7 @@ SlruSelectLRUPage(SlruCtl ctl, int pageno)
 		/*
 		 * If the selected page is clean, we're set.
 		 */
-		if (!shared->page_dirty[bestvalidslot])
+		if (!shared->page_entries[bestvalidslot].page_dirty)
 			return bestvalidslot;
 
 		/*
@@ -1217,9 +1213,9 @@ SimpleLruWriteAll(SlruCtl ctl, bool allow_redirtied)
 		 * already.  That's okay.
 		 */
 		Assert(allow_redirtied ||
-			   shared->page_status[slotno] == SLRU_PAGE_EMPTY ||
-			   (shared->page_status[slotno] == SLRU_PAGE_VALID &&
-				!shared->page_dirty[slotno]));
+			   shared->page_entries[slotno].page_status == SLRU_PAGE_EMPTY ||
+			   (shared->page_entries[slotno].page_status == SLRU_PAGE_VALID &&
+				!shared->page_entries[slotno].page_dirty));
 	}
 
 	LWLockRelease(shared->ControlLock);
@@ -1291,18 +1287,18 @@ restart:;
 
 	for (slotno = 0; slotno < shared->num_slots; slotno++)
 	{
-		if (shared->page_status[slotno] == SLRU_PAGE_EMPTY)
+		if (shared->page_entries[slotno].page_status == SLRU_PAGE_EMPTY)
 			continue;
-		if (!ctl->PagePrecedes(shared->page_number[slotno], cutoffPage))
+		if (!ctl->PagePrecedes(shared->page_entries[slotno].page_number, cutoffPage))
 			continue;
 
 		/*
 		 * If page is clean, just change state to EMPTY (expected case).
 		 */
-		if (shared->page_status[slotno] == SLRU_PAGE_VALID &&
-			!shared->page_dirty[slotno])
+		if (shared->page_entries[slotno].page_status == SLRU_PAGE_VALID &&
+			!shared->page_entries[slotno].page_dirty)
 		{
-			shared->page_status[slotno] = SLRU_PAGE_EMPTY;
+			shared->page_entries[slotno].page_status = SLRU_PAGE_EMPTY;
 			continue;
 		}
 
@@ -1316,7 +1312,7 @@ restart:;
 		 * won't have cause to read its data again.  For now, keep the logic
 		 * the same as it was.)
 		 */
-		if (shared->page_status[slotno] == SLRU_PAGE_VALID)
+		if (shared->page_entries[slotno].page_status == SLRU_PAGE_VALID)
 			SlruInternalWritePage(ctl, slotno, NULL);
 		else
 			SimpleLruWaitIO(ctl, slotno);
@@ -1371,9 +1367,9 @@ restart:
 	did_write = false;
 	for (slotno = 0; slotno < shared->num_slots; slotno++)
 	{
-		int			pagesegno = shared->page_number[slotno] / SLRU_PAGES_PER_SEGMENT;
+		int			pagesegno = shared->page_entries[slotno].page_number / SLRU_PAGES_PER_SEGMENT;
 
-		if (shared->page_status[slotno] == SLRU_PAGE_EMPTY)
+		if (shared->page_entries[slotno].page_status == SLRU_PAGE_EMPTY)
 			continue;
 
 		/* not the segment we're looking for */
@@ -1381,15 +1377,15 @@ restart:
 			continue;
 
 		/* If page is clean, just change state to EMPTY (expected case). */
-		if (shared->page_status[slotno] == SLRU_PAGE_VALID &&
-			!shared->page_dirty[slotno])
+		if (shared->page_entries[slotno].page_status == SLRU_PAGE_VALID &&
+			!shared->page_entries[slotno].page_dirty)
 		{
-			shared->page_status[slotno] = SLRU_PAGE_EMPTY;
+			shared->page_entries[slotno].page_status = SLRU_PAGE_EMPTY;
 			continue;
 		}
 
 		/* Same logic as SimpleLruTruncate() */
-		if (shared->page_status[slotno] == SLRU_PAGE_VALID)
+		if (shared->page_entries[slotno].page_status == SLRU_PAGE_VALID)
 			SlruInternalWritePage(ctl, slotno, NULL);
 		else
 			SimpleLruWaitIO(ctl, slotno);
diff --git a/src/backend/access/transam/subtrans.c b/src/backend/access/transam/subtrans.c
index 862b0eab96..4cf34ef733 100644
--- a/src/backend/access/transam/subtrans.c
+++ b/src/backend/access/transam/subtrans.c
@@ -97,7 +97,7 @@ SubTransSetParent(TransactionId xid, TransactionId parent)
 	{
 		Assert(*ptr == InvalidTransactionId);
 		*ptr = parent;
-		SubTransCtl->shared->page_dirty[slotno] = true;
+		SubTransCtl->shared->page_entries[slotno].page_dirty = true;
 	}
 
 	LWLockRelease(SubtransSLRULock);
@@ -220,7 +220,7 @@ BootStrapSUBTRANS(void)
 
 	/* Make sure it's written out */
 	SimpleLruWritePage(SubTransCtl, slotno);
-	Assert(!SubTransCtl->shared->page_dirty[slotno]);
+	Assert(!SubTransCtl->shared->page_entries[slotno].page_dirty);
 
 	LWLockRelease(SubtransSLRULock);
 }
diff --git a/src/backend/commands/async.c b/src/backend/commands/async.c
index 6b98060330..2d220e4819 100644
--- a/src/backend/commands/async.c
+++ b/src/backend/commands/async.c
@@ -1445,7 +1445,7 @@ asyncQueueAddEntries(ListCell *nextNotify)
 								   InvalidTransactionId);
 
 	/* Note we mark the page dirty before writing in it */
-	NotifyCtl->shared->page_dirty[slotno] = true;
+	NotifyCtl->shared->page_entries[slotno].page_dirty = true;
 
 	while (nextNotify != NULL)
 	{
diff --git a/src/backend/storage/lmgr/predicate.c b/src/backend/storage/lmgr/predicate.c
index 1534d4ea48..957cbe82f7 100644
--- a/src/backend/storage/lmgr/predicate.c
+++ b/src/backend/storage/lmgr/predicate.c
@@ -963,7 +963,7 @@ SerialAdd(TransactionId xid, SerCommitSeqNo minConflictCommitSeqNo)
 		slotno = SimpleLruReadPage(SerialSlruCtl, targetPage, true, xid);
 
 	SerialValue(slotno, xid) = minConflictCommitSeqNo;
-	SerialSlruCtl->shared->page_dirty[slotno] = true;
+	SerialSlruCtl->shared->page_entries[slotno].page_dirty = true;
 
 	LWLockRelease(SerialSLRULock);
 }
diff --git a/src/include/access/slru.h b/src/include/access/slru.h
index cc9cc10d27..4a1fe1a1ae 100644
--- a/src/include/access/slru.h
+++ b/src/include/access/slru.h
@@ -44,7 +44,7 @@
  * in the latter case it implies that the page has been re-dirtied since
  * the write started.
  */
-typedef enum
+typedef enum SlruPageStatus
 {
 	SLRU_PAGE_EMPTY,			/* buffer is not in use */
 	SLRU_PAGE_READ_IN_PROGRESS, /* page is being read in */
@@ -52,6 +52,14 @@ typedef enum
 	SLRU_PAGE_WRITE_IN_PROGRESS /* page is being written out */
 } SlruPageStatus;
 
+typedef struct SlruPageEntry
+{
+	int32	page_number;
+	char	page_status;
+	bool	page_dirty;
+	uint16	padding;
+} SlruPageEntry;
+
 /*
  * Shared-memory state
  */
@@ -69,9 +77,7 @@ typedef struct SlruSharedData
 	 * when status is EMPTY, as is page_lru_count.
 	 */
 	char	  **page_buffer;
-	SlruPageStatus *page_status;
-	bool	   *page_dirty;
-	int		   *page_number;
+	SlruPageEntry *page_entries;
 	int		   *page_lru_count;
 	LWLockPadded *buffer_locks;
 
-- 
2.24.3 (Apple Git-128)

