1
0
mirror of https://github.com/postgres/postgres.git synced 2025-11-09 06:21:09 +03:00

Use atomic access for SlruShared->latest_page_number

The new concurrency model proposed for slru.c to improve performance
does not include any single lock that would coordinate processes
doing concurrent reads/writes on SlruShared->latest_page_number.
We can instead use atomic reads and writes for that variable.

Author: Dilip Kumar <dilipbalaut@gmail.com>
Reviewed-by: Andrey M. Borodin <x4mmm@yandex-team.ru>
Discussion: https://postgr.es/m/CAFiTN-vzDvNz=ExGXz6gdyjtzGixKSqs0mKHMmaQ8sOSEFZ33A@mail.gmail.com
This commit is contained in:
Alvaro Herrera
2024-02-06 10:54:10 +01:00
parent b83033c3cf
commit d172b717c6
5 changed files with 53 additions and 32 deletions

View File

@@ -17,7 +17,8 @@
* per-buffer LWLocks that synchronize I/O for each buffer. The control lock
* must be held to examine or modify any shared state. A process that is
* reading in or writing out a page buffer does not hold the control lock,
* only the per-buffer lock for the buffer it is working on.
* only the per-buffer lock for the buffer it is working on. One exception
* is latest_page_number, which is read and written using atomic ops.
*
* "Holding the control lock" means exclusive lock in all cases except for
* SimpleLruReadPage_ReadOnly(); see comments for SlruRecentlyUsed() for
@@ -239,8 +240,7 @@ SimpleLruInit(SlruCtl ctl, const char *name, int nslots, int nlsns,
shared->lsn_groups_per_page = nlsns;
shared->cur_lru_count = 0;
/* shared->latest_page_number will be set later */
pg_atomic_write_u64(&shared->latest_page_number, 0);
shared->slru_stats_idx = pgstat_get_slru_index(name);
@@ -329,8 +329,15 @@ SimpleLruZeroPage(SlruCtl ctl, int64 pageno)
/* Set the LSNs for this new page to zero */
SimpleLruZeroLSNs(ctl, slotno);
/* Assume this page is now the latest active page */
shared->latest_page_number = pageno;
/*
* Assume this page is now the latest active page.
*
* Note that because both this routine and SlruSelectLRUPage run with
* ControlLock held, it is not possible for this to be zeroing a page that
* SlruSelectLRUPage is going to evict simultaneously. Therefore, there's
* no memory barrier here.
*/
pg_atomic_write_u64(&shared->latest_page_number, pageno);
/* update the stats counter of zeroed pages */
pgstat_count_slru_page_zeroed(shared->slru_stats_idx);
@@ -1113,9 +1120,17 @@ SlruSelectLRUPage(SlruCtl ctl, int64 pageno)
shared->page_lru_count[slotno] = cur_count;
this_delta = 0;
}
/*
* If this page is the one most recently zeroed, don't consider it
* an eviction candidate. See comments in SimpleLruZeroPage for an
* explanation about the lack of a memory barrier here.
*/
this_page_number = shared->page_number[slotno];
if (this_page_number == shared->latest_page_number)
if (this_page_number ==
pg_atomic_read_u64(&shared->latest_page_number))
continue;
if (shared->page_status[slotno] == SLRU_PAGE_VALID)
{
if (this_delta > best_valid_delta ||
@@ -1254,7 +1269,6 @@ void
SimpleLruTruncate(SlruCtl ctl, int64 cutoffPage)
{
SlruShared shared = ctl->shared;
int slotno;
/* update the stats counter of truncates */
pgstat_count_slru_truncate(shared->slru_stats_idx);
@@ -1270,10 +1284,13 @@ SimpleLruTruncate(SlruCtl ctl, int64 cutoffPage)
restart:
/*
* While we are holding the lock, make an important safety check: the
* current endpoint page must not be eligible for removal.
* An important safety check: the current endpoint page must not be
* eligible for removal. This check is just a backstop against wraparound
* bugs elsewhere in SLRU handling, so we don't care if we read a slightly
* outdated value; therefore we don't add a memory barrier.
*/
if (ctl->PagePrecedes(shared->latest_page_number, cutoffPage))
if (ctl->PagePrecedes(pg_atomic_read_u64(&shared->latest_page_number),
cutoffPage))
{
LWLockRelease(shared->ControlLock);
ereport(LOG,
@@ -1282,7 +1299,7 @@ restart:
return;
}
for (slotno = 0; slotno < shared->num_slots; slotno++)
for (int slotno = 0; slotno < shared->num_slots; slotno++)
{
if (shared->page_status[slotno] == SLRU_PAGE_EMPTY)
continue;