From 8e9190898eb0cd12afea72f7ad89c192d5943e60 Mon Sep 17 00:00:00 2001 From: Dilip Kumar Date: Wed, 24 Apr 2019 14:36:28 +0530 Subject: [PATCH] Add prefetch support for the undo log Add prefetching function for undo smgr and also provide mechanism to prefetch without relcache. Dilip Kumar --- src/backend/postmaster/pgstat.c | 3 + src/backend/storage/buffer/bufmgr.c | 101 ++++++++++++++++++---------- src/backend/storage/smgr/undofile.c | 13 +++- src/include/pgstat.h | 1 + src/include/storage/bufmgr.h | 4 ++ 5 files changed, 87 insertions(+), 35 deletions(-) diff --git a/src/backend/postmaster/pgstat.c b/src/backend/postmaster/pgstat.c index c742861dae..d8dc0cc547 100644 --- a/src/backend/postmaster/pgstat.c +++ b/src/backend/postmaster/pgstat.c @@ -4079,6 +4079,9 @@ pgstat_get_wait_io(WaitEventIO w) case WAIT_EVENT_UNDO_CHECKPOINT_SYNC: event_name = "UndoCheckpointSync"; break; + case WAIT_EVENT_UNDO_FILE_PREFETCH: + event_name = "UndoFilePrefetch"; + break; case WAIT_EVENT_UNDO_FILE_READ: event_name = "UndoFileRead"; break; diff --git a/src/backend/storage/buffer/bufmgr.c b/src/backend/storage/buffer/bufmgr.c index 2b1d60680e..5abf42efae 100644 --- a/src/backend/storage/buffer/bufmgr.c +++ b/src/backend/storage/buffer/bufmgr.c @@ -520,14 +520,57 @@ ComputeIoConcurrency(int io_concurrency, double *target) return (new_prefetch_pages >= 0.0 && new_prefetch_pages < (double) INT_MAX); } +#ifdef USE_PREFETCH /* - * PrefetchBuffer -- initiate asynchronous read of a block of a relation + * PrefetchBufferGuts -- Guts of prefetching a buffer. * * This is named by analogy to ReadBuffer but doesn't actually allocate a * buffer. Instead it tries to ensure that a future ReadBuffer for the given * block will not be delayed by the I/O. Prefetching is optional. * No-op if prefetching isn't compiled in. */ +static void +PrefetchBufferGuts(RelFileNode rnode, SMgrRelation smgr, ForkNumber forkNum, + BlockNumber blockNum) +{ + BufferTag newTag; /* identity of requested block */ + uint32 newHash; /* hash value for newTag */ + LWLock *newPartitionLock; /* buffer partition lock for it */ + int buf_id; + + /* create a tag so we can lookup the buffer */ + INIT_BUFFERTAG(newTag, rnode, forkNum, blockNum); + + /* determine its hash code and partition lock ID */ + newHash = BufTableHashCode(&newTag); + newPartitionLock = BufMappingPartitionLock(newHash); + + /* see if the block is in the buffer pool already */ + LWLockAcquire(newPartitionLock, LW_SHARED); + buf_id = BufTableLookup(&newTag, newHash); + LWLockRelease(newPartitionLock); + + /* If not in buffers, initiate prefetch */ + if (buf_id < 0) + smgrprefetch(smgr, forkNum, blockNum); + + /* + * If the block *is* in buffers, we do nothing. This is not really + * ideal: the block might be just about to be evicted, which would be + * stupid since we know we are going to need it soon. But the only + * easy answer is to bump the usage_count, which does not seem like a + * great solution: when the caller does ultimately touch the block, + * usage_count would get bumped again, resulting in too much + * favoritism for blocks that are involved in a prefetch sequence. A + * real fix would involve some additional per-buffer state, and it's + * not clear that there's enough of a problem to justify that. + */ +} +#endif /* USE_PREFETCH */ + +/* + * PrefetchBuffer -- initiate asynchronous read of a block of a relation + */ void PrefetchBuffer(Relation reln, ForkNumber forkNum, BlockNumber blockNum) { @@ -550,42 +593,32 @@ PrefetchBuffer(Relation reln, ForkNumber forkNum, BlockNumber blockNum) LocalPrefetchBuffer(reln->rd_smgr, forkNum, blockNum); } else - { - BufferTag newTag; /* identity of requested block */ - uint32 newHash; /* hash value for newTag */ - LWLock *newPartitionLock; /* buffer partition lock for it */ - int buf_id; - - /* create a tag so we can lookup the buffer */ - INIT_BUFFERTAG(newTag, reln->rd_smgr->smgr_rnode.node, - forkNum, blockNum); - - /* determine its hash code and partition lock ID */ - newHash = BufTableHashCode(&newTag); - newPartitionLock = BufMappingPartitionLock(newHash); - - /* see if the block is in the buffer pool already */ - LWLockAcquire(newPartitionLock, LW_SHARED); - buf_id = BufTableLookup(&newTag, newHash); - LWLockRelease(newPartitionLock); + PrefetchBufferGuts(reln->rd_smgr->smgr_rnode.node, reln->rd_smgr, + forkNum, blockNum); +#endif /* USE_PREFETCH */ +} - /* If not in buffers, initiate prefetch */ - if (buf_id < 0) - smgrprefetch(reln->rd_smgr, forkNum, blockNum); +/* + * PrefetchBufferWithoutRelcache -- like PrefetchBuffer but doesn't need a + * relcache entry for the relation. + */ +void +PrefetchBufferWithoutRelcache(RelFileNode rnode, ForkNumber forkNum, + BlockNumber blockNum, char relpersistence) +{ +#ifdef USE_PREFETCH + SMgrRelation smgr = smgropen(rnode, + relpersistence == RELPERSISTENCE_TEMP + ? MyBackendId : InvalidBackendId); - /* - * If the block *is* in buffers, we do nothing. This is not really - * ideal: the block might be just about to be evicted, which would be - * stupid since we know we are going to need it soon. But the only - * easy answer is to bump the usage_count, which does not seem like a - * great solution: when the caller does ultimately touch the block, - * usage_count would get bumped again, resulting in too much - * favoritism for blocks that are involved in a prefetch sequence. A - * real fix would involve some additional per-buffer state, and it's - * not clear that there's enough of a problem to justify that. - */ + if (relpersistence == RELPERSISTENCE_TEMP) + { + /* pass it off to localbuf.c */ + LocalPrefetchBuffer(smgr, forkNum, blockNum); } -#endif /* USE_PREFETCH */ + else + PrefetchBufferGuts(rnode, smgr, forkNum, blockNum); +#endif /* USE_PREFETCH */ } diff --git a/src/backend/storage/smgr/undofile.c b/src/backend/storage/smgr/undofile.c index 04d4514e35..3be0f5e922 100644 --- a/src/backend/storage/smgr/undofile.c +++ b/src/backend/storage/smgr/undofile.c @@ -119,7 +119,18 @@ undofile_extend(SMgrRelation reln, ForkNumber forknum, void undofile_prefetch(SMgrRelation reln, ForkNumber forknum, BlockNumber blocknum) { - elog(ERROR, "undofile_prefetch is not supported"); +#ifdef USE_PREFETCH + File file; + off_t seekpos; + + Assert(forknum == MAIN_FORKNUM); + file = undofile_get_segment_file(reln, blocknum / UNDOSEG_SIZE); + seekpos = (off_t) BLCKSZ * (blocknum % ((BlockNumber) UNDOSEG_SIZE)); + + Assert(seekpos < (off_t) BLCKSZ * UNDOSEG_SIZE); + + (void) FilePrefetch(file, seekpos, BLCKSZ, WAIT_EVENT_UNDO_FILE_PREFETCH); +#endif /* USE_PREFETCH */ } void diff --git a/src/include/pgstat.h b/src/include/pgstat.h index 1936c5db6f..2fff6734fc 100644 --- a/src/include/pgstat.h +++ b/src/include/pgstat.h @@ -937,6 +937,7 @@ typedef enum WAIT_EVENT_UNDO_CHECKPOINT_READ, WAIT_EVENT_UNDO_CHECKPOINT_SYNC, WAIT_EVENT_UNDO_CHECKPOINT_WRITE, + WAIT_EVENT_UNDO_FILE_PREFETCH, WAIT_EVENT_UNDO_FILE_READ, WAIT_EVENT_UNDO_FILE_WRITE, WAIT_EVENT_UNDO_FILE_FLUSH, diff --git a/src/include/storage/bufmgr.h b/src/include/storage/bufmgr.h index a04190aa92..5c0ed58591 100644 --- a/src/include/storage/bufmgr.h +++ b/src/include/storage/bufmgr.h @@ -165,6 +165,10 @@ extern PGDLLIMPORT int32 *LocalRefCount; extern bool ComputeIoConcurrency(int io_concurrency, double *target); extern void PrefetchBuffer(Relation reln, ForkNumber forkNum, BlockNumber blockNum); +extern void PrefetchBufferWithoutRelcache(RelFileNode rnode, + ForkNumber forkNum, + BlockNumber blockNum, + char relpersistence); extern Buffer ReadBuffer(Relation reln, BlockNumber blockNum); extern Buffer ReadBufferExtended(Relation reln, ForkNumber forkNum, BlockNumber blockNum, ReadBufferMode mode, -- 2.39.5