From cea3142af73c434d11206dd39a7edaa8718269b4 Mon Sep 17 00:00:00 2001 From: Thomas Munro Date: Wed, 7 Apr 2021 16:39:09 +1200 Subject: [PATCH v15 3/3] Use hybrid random/SLRU replacement for SLRUs. The previous algorithm would search the entire SLRU to find the least recently used page. That doesn't seem like a good idea now that the user can set SLRUs to large sizes. Instead, apply the existing algorithm to a small number of randomly chosen buffers, for a cheap hybrid of SLRU and random replacement that doesn't require a complete rewrite of slru.c's locking. XXX experiment --- src/backend/access/transam/slru.c | 22 +++++++++++++++++----- 1 file changed, 17 insertions(+), 5 deletions(-) diff --git a/src/backend/access/transam/slru.c b/src/backend/access/transam/slru.c index 82c61c475b..95e404e856 100644 --- a/src/backend/access/transam/slru.c +++ b/src/backend/access/transam/slru.c @@ -71,6 +71,14 @@ */ #define MAX_WRITEALL_BUFFERS 16 +/* + * We need a fast way to choose a buffer to replace, because we hold a coarse + * grained exclusive lock while searching. As an improvement over simple + * random replacement that still has some LRU tendancies, we'll compare the + * recency of a smallish number of randomly selected buffers. + */ +#define MAX_RANDOM_SEARCH 8 + typedef struct SlruWriteAllData { int num_files; /* # files actually open */ @@ -1071,6 +1079,7 @@ SlruSelectLRUPage(SlruCtl ctl, int pageno) int bestinvalidslot = 0; /* keep compiler quiet */ int best_invalid_delta = -1; int best_invalid_page_number = 0; /* keep compiler quiet */ + int candidates = Min(shared->num_slots, MAX_RANDOM_SEARCH); /* See if page already has a buffer assigned */ slotno = SlruMappingFind(ctl, pageno); @@ -1082,9 +1091,10 @@ SlruSelectLRUPage(SlruCtl ctl, int pageno) } /* - * If we find any EMPTY slot, just select that one. Else choose a - * victim page to replace. We normally take the least recently used - * valid page, but we will never take the slot containing + * If we find any EMPTY slot, just select that one. Else choose a + * victim page to replace. We take the least recently used of a small + * number of randomly chosen pages, to avoid having to scan a + * potentially large number of pages. We skip the slot containing * latest_page_number, even if it appears least recently used. We * will select a slot that is already I/O busy only if there is no * other choice: a read-busy slot will not be least recently used once @@ -1109,11 +1119,13 @@ SlruSelectLRUPage(SlruCtl ctl, int pageno) * multiple pages with the same lru_count. */ cur_count = (shared->cur_lru_count)++; - for (slotno = 0; slotno < shared->num_slots; slotno++) + for (int i = 0; i < candidates; ++i) { int this_delta; int this_page_number; +retry: + slotno = (random() & LONG_MAX) % shared->num_slots; if (shared->page_status[slotno] == SLRU_PAGE_EMPTY) return slotno; this_delta = cur_count - shared->page_lru_count[slotno]; @@ -1131,7 +1143,7 @@ SlruSelectLRUPage(SlruCtl ctl, int pageno) } this_page_number = shared->page_number[slotno]; if (this_page_number == shared->latest_page_number) - continue; + goto retry; if (shared->page_status[slotno] == SLRU_PAGE_VALID) { if (this_delta > best_valid_delta || -- 2.27.0