postgresql-git-bt_prefetch_backseq.diff
application/octet-stream
Filename: postgresql-git-bt_prefetch_backseq.diff
Type: application/octet-stream
Part: 0
Patch
Same data as JSON:
GET /api/v1/attachments/:id/patch
the parsed metadata as JSON — format, series position, per-file stats; never the diff bytes.
API reference →
Format: unified
| File | + | − |
|---|---|---|
| src/backend/access/nbtree/nbtpage.c | 13 | 0 |
| src/backend/access/nbtree/nbtree.c | 3 | 0 |
| src/backend/access/nbtree/nbtsearch.c | 60 | 9 |
| src/include/access/nbtree.h | 5 | 0 |
diff --git a/src/backend/access/nbtree/nbtpage.c b/src/backend/access/nbtree/nbtpage.c
index 016ce22..1115afc 100644
--- a/src/backend/access/nbtree/nbtpage.c
+++ b/src/backend/access/nbtree/nbtpage.c
@@ -487,6 +487,19 @@ _bt_log_reuse_page(Relation rel, BlockNumber blkno, TransactionId latestRemovedX
}
/*
+ * _bt_prefetchbuf() -- Prefetch a buffer by block number
+ */
+void
+_bt_prefetchbuf(Relation rel, BlockNumber blkno)
+{
+ if (blkno != P_NEW && blkno != P_NONE)
+ {
+ /* Just prefetch an existing block of the relation */
+ PrefetchBuffer(rel, MAIN_FORKNUM, blkno);
+ }
+}
+
+/*
* _bt_getbuf() -- Get a buffer by block number for read or write.
*
* blkno == P_NEW means to get an unallocated index page. The page
diff --git a/src/backend/access/nbtree/nbtree.c b/src/backend/access/nbtree/nbtree.c
index b055eaf..a2b9d99 100644
--- a/src/backend/access/nbtree/nbtree.c
+++ b/src/backend/access/nbtree/nbtree.c
@@ -432,6 +432,9 @@ btbeginscan(PG_FUNCTION_ARGS)
so->killedItems = NULL; /* until needed */
so->numKilled = 0;
+ so->backSeqRun = 0;
+ so->backSeqPos = 0;
+
/*
* We don't know yet whether the scan will be index-only, so we do not
* allocate the tuple workspace arrays until btrescan. However, we set up
diff --git a/src/backend/access/nbtree/nbtsearch.c b/src/backend/access/nbtree/nbtsearch.c
index e0c9523..a7a9e2e 100644
--- a/src/backend/access/nbtree/nbtsearch.c
+++ b/src/backend/access/nbtree/nbtsearch.c
@@ -28,7 +28,8 @@ static bool _bt_readpage(IndexScanDesc scan, ScanDirection dir,
OffsetNumber offnum);
static void _bt_saveitem(BTScanOpaque so, int itemIndex,
OffsetNumber offnum, IndexTuple itup);
-static bool _bt_steppage(IndexScanDesc scan, ScanDirection dir);
+static bool _bt_steppage(IndexScanDesc scan, ScanDirection dir,
+ bool prefetch);
static Buffer _bt_walk_left(Relation rel, Buffer buf);
static bool _bt_endpoint(IndexScanDesc scan, ScanDirection dir);
@@ -961,7 +962,7 @@ _bt_first(IndexScanDesc scan, ScanDirection dir)
* There's no actually-matching data on this page. Try to advance to
* the next page. Return false if there's no matching data at all.
*/
- if (!_bt_steppage(scan, dir))
+ if (!_bt_steppage(scan, dir, false))
return false;
}
@@ -1008,7 +1009,7 @@ _bt_next(IndexScanDesc scan, ScanDirection dir)
/* We must acquire lock before applying _bt_steppage */
Assert(BufferIsValid(so->currPos.buf));
LockBuffer(so->currPos.buf, BT_READ);
- if (!_bt_steppage(scan, dir))
+ if (!_bt_steppage(scan, dir, target_prefetch_pages > 0))
return false;
/* Drop the lock, but not pin, on the new page */
LockBuffer(so->currPos.buf, BUFFER_LOCK_UNLOCK);
@@ -1021,7 +1022,7 @@ _bt_next(IndexScanDesc scan, ScanDirection dir)
/* We must acquire lock before applying _bt_steppage */
Assert(BufferIsValid(so->currPos.buf));
LockBuffer(so->currPos.buf, BT_READ);
- if (!_bt_steppage(scan, dir))
+ if (!_bt_steppage(scan, dir, target_prefetch_pages > 0))
return false;
/* Drop the lock, but not pin, on the new page */
LockBuffer(so->currPos.buf, BUFFER_LOCK_UNLOCK);
@@ -1075,9 +1076,11 @@ _bt_readpage(IndexScanDesc scan, ScanDirection dir, OffsetNumber offnum)
/*
* we must save the page's right-link while scanning it; this tells us
* where to step right to after we're done with these items. There is no
- * corresponding need for the left-link, since splits always go right.
+ * corresponding need for the left-link, since splits always go right,
+ * but we need it for back-sequential scan detection.
*/
so->currPos.nextPage = opaque->btpo_next;
+ so->currPos.prevPage = opaque->btpo_prev;
/* initialize tuple workspace to empty */
so->currPos.nextTupleOffset = 0;
@@ -1180,7 +1183,7 @@ _bt_saveitem(BTScanOpaque so, int itemIndex,
* locks and pins, set so->currPos.buf to InvalidBuffer, and return FALSE.
*/
static bool
-_bt_steppage(IndexScanDesc scan, ScanDirection dir)
+_bt_steppage(IndexScanDesc scan, ScanDirection dir, bool prefetch)
{
BTScanOpaque so = (BTScanOpaque) scan->opaque;
Relation rel;
@@ -1243,8 +1246,17 @@ _bt_steppage(IndexScanDesc scan, ScanDirection dir)
PredicateLockPage(rel, blkno, scan->xs_snapshot);
/* see if there are any matches on this page */
/* note that this will clear moreRight if we can stop */
- if (_bt_readpage(scan, dir, P_FIRSTDATAKEY(opaque)))
+ if (_bt_readpage(scan, dir, P_FIRSTDATAKEY(opaque))) {
+ if (prefetch && so->currPos.moreRight
+ && (opaque->btpo_next != (blkno+1)))
+ {
+ /* start prefetch on next page, but not
+ if we're reading sequentially already,
+ as it's counterproductive in those cases */
+ _bt_prefetchbuf(rel, opaque->btpo_next);
+ }
break;
+ }
}
/* nope, keep going */
blkno = opaque->btpo_next;
@@ -1288,11 +1300,50 @@ _bt_steppage(IndexScanDesc scan, ScanDirection dir)
opaque = (BTPageOpaque) PageGetSpecialPointer(page);
if (!P_IGNORE(opaque))
{
+ /* We must rely on the previously saved prevPage link! */
+ BlockNumber blkno = so->currPos.prevPage;
+
PredicateLockPage(rel, BufferGetBlockNumber(so->currPos.buf), scan->xs_snapshot);
/* see if there are any matches on this page */
/* note that this will clear moreLeft if we can stop */
- if (_bt_readpage(scan, dir, PageGetMaxOffsetNumber(page)))
+ if (_bt_readpage(scan, dir, PageGetMaxOffsetNumber(page))) {
+ if (prefetch && so->currPos.moreLeft) {
+ /* detect back-sequential runs and increase
+ prefetch window blindly downwards 2 blocks
+ at a time. This only works in our favor
+ for index-only scans */
+ if (scan->xs_want_itup && blkno > 0 && opaque->btpo_prev == (blkno-1)) {
+ BlockNumber backPos;
+
+ if (so->backSeqRun == 0)
+ backPos = (blkno-1);
+ else
+ backPos = so->backSeqPos;
+ so->backSeqRun++;
+
+ if (backPos > 0 && (blkno - backPos) <= target_prefetch_pages) {
+ _bt_prefetchbuf(rel, backPos--);
+ /* don't start back-seq prefetch too early */
+ if (so->backSeqRun >= target_prefetch_pages
+ && backPos > 0
+ && (blkno - backPos) <= target_prefetch_pages)
+ {
+ _bt_prefetchbuf(rel, backPos--);
+ }
+ }
+
+ so->backSeqPos = backPos;
+ } else {
+ /* start prefetch on next page */
+ if (so->backSeqRun != 0) {
+ if (opaque->btpo_prev > blkno || opaque->btpo_prev < so->backSeqPos)
+ so->backSeqRun = 0;
+ }
+ _bt_prefetchbuf(rel, opaque->btpo_prev);
+ }
+ }
break;
+ }
}
}
}
@@ -1587,7 +1638,7 @@ _bt_endpoint(IndexScanDesc scan, ScanDirection dir)
* There's no actually-matching data on this page. Try to advance to
* the next page. Return false if there's no matching data at all.
*/
- if (!_bt_steppage(scan, dir))
+ if (!_bt_steppage(scan, dir, false))
return false;
}
diff --git a/src/include/access/nbtree.h b/src/include/access/nbtree.h
index d4941e0..71f1565 100644
--- a/src/include/access/nbtree.h
+++ b/src/include/access/nbtree.h
@@ -496,6 +496,7 @@ typedef struct BTScanPosData
Buffer buf; /* if valid, the buffer is pinned */
BlockNumber nextPage; /* page's right link when we scanned it */
+ BlockNumber prevPage; /* page's left link when we scanned it */
/*
* moreLeft and moreRight track whether we think there may be matching
@@ -576,6 +577,9 @@ typedef struct BTScanOpaqueData
int markItemIndex; /* itemIndex, or -1 if not valid */
/* keep these last in struct for efficiency */
+ unsigned int backSeqRun; /* number of back-sequential pages in a run */
+ BlockNumber backSeqPos; /* blkid last prefetched in back-sequential
+ runs */
BTScanPosData currPos; /* current position data */
BTScanPosData markPos; /* marked position, if any */
} BTScanOpaqueData;
@@ -628,6 +632,7 @@ extern Buffer _bt_getroot(Relation rel, int access);
extern Buffer _bt_gettrueroot(Relation rel);
extern void _bt_checkpage(Relation rel, Buffer buf);
extern Buffer _bt_getbuf(Relation rel, BlockNumber blkno, int access);
+extern void _bt_prefetchbuf(Relation rel, BlockNumber blkno);
extern Buffer _bt_relandgetbuf(Relation rel, Buffer obuf,
BlockNumber blkno, int access);
extern void _bt_relbuf(Relation rel, Buffer buf);