From cb46346ee896b4ea7778d0e0562e1a250e771bb6 Mon Sep 17 00:00:00 2001 From: Dilip Kumar Date: Tue, 31 Oct 2023 10:26:45 +0530 Subject: [PATCH v4 2/5] Add a buffer mapping table for SLRUs. Instead of doing a linear search for the buffer holding a given page number, use a hash table. This will allow us to increase the size of these caches. Patch By: Thomas Munro and some adjustment by Dilip Kumar Reviewed-by: Andrey M. Borodin and Dilip Kumar --- src/backend/access/transam/slru.c | 140 +++++++++++++++++++++++++----- src/include/access/slru.h | 4 + src/tools/pgindent/typedefs.list | 1 + 3 files changed, 123 insertions(+), 22 deletions(-) diff --git a/src/backend/access/transam/slru.c b/src/backend/access/transam/slru.c index 9ed24e1185..ac23076def 100644 --- a/src/backend/access/transam/slru.c +++ b/src/backend/access/transam/slru.c @@ -59,6 +59,7 @@ #include "pgstat.h" #include "storage/fd.h" #include "storage/shmem.h" +#include "utils/hsearch.h" #define SlruFileName(ctl, path, seg) \ snprintf(path, MAXPGPATH, "%s/%04X", (ctl)->Dir, seg) @@ -80,6 +81,15 @@ typedef struct SlruWriteAllData typedef struct SlruWriteAllData *SlruWriteAll; +/* + * hash table entry for mapping from pageno to the slotno in SLRU buffer pool. + */ +typedef struct SlruMappingTableEntry +{ + int pageno; + int slotno; +} SlruMappingTableEntry; + /* * Populate a file tag describing a segment file. We only use the segment * number, since we can derive everything else we need by having separate @@ -147,13 +157,15 @@ static int SlruSelectLRUPage(SlruCtl ctl, int pageno); static bool SlruScanDirCbDeleteCutoff(SlruCtl ctl, char *filename, int segpage, void *data); static void SlruInternalDeleteSegment(SlruCtl ctl, int segno); +static void SlruMappingAdd(SlruCtl ctl, int pageno, int slotno); +static void SlruMappingRemove(SlruCtl ctl, int pageno); +static int SlruMappingFind(SlruCtl ctl, int pageno); /* - * Initialization of shared memory + * Helper function of SimpleLruShmemSize to compute the SlruSharedData size. */ - -Size -SimpleLruShmemSize(int nslots, int nlsns) +static Size +SimpleLruStructSize(int nslots, int nlsns) { Size sz; @@ -168,10 +180,19 @@ SimpleLruShmemSize(int nslots, int nlsns) if (nlsns > 0) sz += MAXALIGN(nslots * nlsns * sizeof(XLogRecPtr)); /* group_lsn[] */ - return BUFFERALIGN(sz) + BLCKSZ * nslots; } +/* + * Initialization of shared memory. + */ +Size +SimpleLruShmemSize(int nslots, int nlsns) +{ + return SimpleLruStructSize(nslots, nlsns) + + hash_estimate_size(nslots, sizeof(SlruMappingTableEntry)); +} + /* * Initialize, or attach to, a simple LRU cache in shared memory. * @@ -189,11 +210,14 @@ SimpleLruInit(SlruCtl ctl, const char *name, int nslots, int nlsns, LWLock *ctllock, const char *subdir, int tranche_id, SyncRequestHandler sync_handler) { + char mapping_table_name[SHMEM_INDEX_KEYSIZE]; + HASHCTL mapping_table_info; + HTAB *mapping_table; SlruShared shared; bool found; shared = (SlruShared) ShmemInitStruct(name, - SimpleLruShmemSize(nslots, nlsns), + SimpleLruStructSize(nslots, nlsns), &found); if (!IsUnderPostmaster) @@ -260,11 +284,21 @@ SimpleLruInit(SlruCtl ctl, const char *name, int nslots, int nlsns, else Assert(found); + /* Create or find the buffer mapping table. */ + memset(&mapping_table_info, 0, sizeof(mapping_table_info)); + mapping_table_info.keysize = sizeof(int); + mapping_table_info.entrysize = sizeof(SlruMappingTableEntry); + snprintf(mapping_table_name, sizeof(mapping_table_name), + "%s Lookup Table", name); + mapping_table = ShmemInitHash(mapping_table_name, nslots, nslots, + &mapping_table_info, HASH_ELEM | HASH_BLOBS); + /* * Initialize the unshared control struct, including directory path. We * assume caller set PagePrecedes. */ ctl->shared = shared; + ctl->mapping_table = mapping_table; ctl->sync_handler = sync_handler; strlcpy(ctl->Dir, subdir, sizeof(ctl->Dir)); } @@ -291,6 +325,9 @@ SimpleLruZeroPage(SlruCtl ctl, int pageno) shared->page_number[slotno] == pageno); /* Mark the slot as containing this page */ + if (shared->page_status[slotno] != SLRU_PAGE_EMPTY) + SlruMappingRemove(ctl, shared->page_number[slotno]); + SlruMappingAdd(ctl, pageno, slotno); shared->page_number[slotno] = pageno; shared->page_status[slotno] = SLRU_PAGE_VALID; shared->page_dirty[slotno] = true; @@ -364,7 +401,10 @@ SimpleLruWaitIO(SlruCtl ctl, int slotno) { /* indeed, the I/O must have failed */ if (shared->page_status[slotno] == SLRU_PAGE_READ_IN_PROGRESS) + { + SlruMappingRemove(ctl, shared->page_number[slotno]); shared->page_status[slotno] = SLRU_PAGE_EMPTY; + } else /* write_in_progress */ { shared->page_status[slotno] = SLRU_PAGE_VALID; @@ -438,6 +478,9 @@ SimpleLruReadPage(SlruCtl ctl, int pageno, bool write_ok, !shared->page_dirty[slotno])); /* Mark the slot read-busy */ + if (shared->page_status[slotno] != SLRU_PAGE_EMPTY) + SlruMappingRemove(ctl, shared->page_number[slotno]); + SlruMappingAdd(ctl, pageno, slotno); shared->page_number[slotno] = pageno; shared->page_status[slotno] = SLRU_PAGE_READ_IN_PROGRESS; shared->page_dirty[slotno] = false; @@ -461,7 +504,13 @@ SimpleLruReadPage(SlruCtl ctl, int pageno, bool write_ok, shared->page_status[slotno] == SLRU_PAGE_READ_IN_PROGRESS && !shared->page_dirty[slotno]); - shared->page_status[slotno] = ok ? SLRU_PAGE_VALID : SLRU_PAGE_EMPTY; + if (ok) + shared->page_status[slotno] = SLRU_PAGE_VALID; + else + { + SlruMappingRemove(ctl, pageno); + shared->page_status[slotno] = SLRU_PAGE_EMPTY; + } LWLockRelease(&shared->buffer_locks[slotno].lock); @@ -502,20 +551,20 @@ SimpleLruReadPage_ReadOnly(SlruCtl ctl, int pageno, TransactionId xid) LWLockAcquire(shared->ControlLock, LW_SHARED); /* See if page is already in a buffer */ - for (slotno = 0; slotno < shared->num_slots; slotno++) + slotno = SlruMappingFind(ctl, pageno); + if (slotno >= 0 && + shared->page_status[slotno] != SLRU_PAGE_READ_IN_PROGRESS) { - if (shared->page_number[slotno] == pageno && - shared->page_status[slotno] != SLRU_PAGE_EMPTY && - shared->page_status[slotno] != SLRU_PAGE_READ_IN_PROGRESS) - { - /* See comments for SlruRecentlyUsed macro */ - SlruRecentlyUsed(shared, slotno); + Assert(shared->page_status[slotno] != SLRU_PAGE_EMPTY); + Assert(shared->page_number[slotno] == pageno); - /* update the stats counter of pages found in the SLRU */ - pgstat_count_slru_page_hit(shared->slru_stats_idx); + /* See comments for SlruRecentlyUsed macro */ + SlruRecentlyUsed(shared, slotno); - return slotno; - } + /* update the stats counter of pages found in the SLRU */ + pgstat_count_slru_page_hit(shared->slru_stats_idx); + + return slotno; } /* No luck, so switch to normal exclusive lock and do regular read */ @@ -1031,11 +1080,12 @@ SlruSelectLRUPage(SlruCtl ctl, int pageno) int best_invalid_page_number = 0; /* keep compiler quiet */ /* See if page already has a buffer assigned */ - for (slotno = 0; slotno < shared->num_slots; slotno++) + slotno = SlruMappingFind(ctl, pageno); + if (slotno >= 0) { - if (shared->page_number[slotno] == pageno && - shared->page_status[slotno] != SLRU_PAGE_EMPTY) - return slotno; + Assert(shared->page_number[slotno] == pageno); + Assert(shared->page_status[slotno] != SLRU_PAGE_EMPTY); + return slotno; } /* @@ -1268,6 +1318,7 @@ restart: if (shared->page_status[slotno] == SLRU_PAGE_VALID && !shared->page_dirty[slotno]) { + SlruMappingRemove(ctl, shared->page_number[slotno]); shared->page_status[slotno] = SLRU_PAGE_EMPTY; continue; } @@ -1350,6 +1401,7 @@ restart: if (shared->page_status[slotno] == SLRU_PAGE_VALID && !shared->page_dirty[slotno]) { + SlruMappingRemove(ctl, shared->page_number[slotno]); shared->page_status[slotno] = SLRU_PAGE_EMPTY; continue; } @@ -1613,3 +1665,47 @@ SlruSyncFileTag(SlruCtl ctl, const FileTag *ftag, char *path) errno = save_errno; return result; } + +/* + * Lookup the given pageno entry; return buffer slotno, or -1 if not found. + */ +static int +SlruMappingFind(SlruCtl ctl, int pageno) +{ + SlruMappingTableEntry *mapping; + + mapping = hash_search(ctl->mapping_table, &pageno, HASH_FIND, NULL); + if (mapping) + return mapping->slotno; + + return -1; +} + +/* + * Insert a hashtable entry for given pageno and buffer slotno, unless an entry + * already exists for that pageno. + */ +static void +SlruMappingAdd(SlruCtl ctl, int pageno, int slotno) +{ + SlruMappingTableEntry *mapping; + bool found PG_USED_FOR_ASSERTS_ONLY; + + mapping = hash_search(ctl->mapping_table, &pageno, HASH_ENTER, &found); + mapping->slotno = slotno; + + Assert(!found); +} + +/* + * Delete the hashtable entry for given tag (which must exist). + */ +static void +SlruMappingRemove(SlruCtl ctl, int pageno) +{ + bool found PG_USED_FOR_ASSERTS_ONLY; + + hash_search(ctl->mapping_table, &pageno, HASH_REMOVE, &found); + + Assert(found); +} diff --git a/src/include/access/slru.h b/src/include/access/slru.h index c0d37e3eb3..9cd0899f1d 100644 --- a/src/include/access/slru.h +++ b/src/include/access/slru.h @@ -16,6 +16,7 @@ #include "access/xlogdefs.h" #include "storage/lwlock.h" #include "storage/sync.h" +#include "utils/hsearch.h" /* * To avoid overflowing internal arithmetic and the size_t data type, the @@ -116,6 +117,9 @@ typedef struct SlruCtlData { SlruShared shared; + /* Buffer mapping hash table over slru buffer pool */ + HTAB *mapping_table; + /* * Which sync handler function to use when handing sync requests over to * the checkpointer. SYNC_HANDLER_NONE to disable fsync (eg pg_notify). diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list index 87c1aee379..ec8957f12a 100644 --- a/src/tools/pgindent/typedefs.list +++ b/src/tools/pgindent/typedefs.list @@ -2568,6 +2568,7 @@ SlotNumber SlruCtl SlruCtlData SlruErrorCause +SlruMappingTableEntry SlruPageStatus SlruScanCallback SlruShared -- 2.39.2 (Apple Git-143)