Avoid pointer chasing in _bt_readpage inner loop.
authorPeter Geoghegan <[email protected]>
Mon, 8 Dec 2025 18:48:09 +0000 (13:48 -0500)
committerPeter Geoghegan <[email protected]>
Mon, 8 Dec 2025 18:48:09 +0000 (13:48 -0500)
Make _bt_readpage pass down the current scan direction to various
utility functions within its pstate variable.  Also have _bt_readpage
work off of a local copy of scan->ignore_killed_tuples within its
per-tuple loop (rather than using scan->ignore_killed_tuples directly).

Testing has shown that this significantly benefits large range scans,
which are naturally able to take full advantage of the pstate.startikey
optimization added by commit 8a510275.  Running a pgbench script with a
"SELECT abalance FROM pgbench_accounts WHERE aid BETWEEN ..." query
shows an increase in transaction throughput of over 5%.  There also
appears to be a small performance benefit when running pgbench's
built-in select-only script.

Follow-up to commit 65d6acbc.

Author: Peter Geoghegan <[email protected]>
Reviewed-By: Victor Yegorov <[email protected]>
Discussion: https://postgr.es/m/CAH2-WzmwMwcwKFgaf+mYPwiz3iL4AqpXnwtW_O0vqpWPXRom9Q@mail.gmail.com

src/backend/access/nbtree/nbtreadpage.c

index 00df52b81bed1a0a217a01463a1a76f69f145025..ac67b6f7a34127f4ef47d9771424d21ec918cb0b 100644 (file)
@@ -31,6 +31,7 @@
 typedef struct BTReadPageState
 {
        /* Input parameters, set by _bt_readpage for _bt_checkkeys */
+       ScanDirection dir;                      /* current scan direction */
        OffsetNumber minoff;            /* Lowest non-pivot tuple's offset */
        OffsetNumber maxoff;            /* Highest non-pivot tuple's offset */
        IndexTuple      finaltup;               /* Needed by scans with array keys */
@@ -140,7 +141,8 @@ _bt_readpage(IndexScanDesc scan, ScanDirection dir, OffsetNumber offnum,
        OffsetNumber minoff;
        OffsetNumber maxoff;
        BTReadPageState pstate;
-       bool            arrayKeys;
+       bool            arrayKeys,
+                               ignore_killed_tuples = scan->ignore_killed_tuples;
        int                     itemIndex,
                                indnatts;
 
@@ -151,7 +153,7 @@ _bt_readpage(IndexScanDesc scan, ScanDirection dir, OffsetNumber offnum,
        so->currPos.prevPage = opaque->btpo_prev;
        so->currPos.nextPage = opaque->btpo_next;
        /* delay setting so->currPos.lsn until _bt_drop_lock_and_maybe_pin */
-       so->currPos.dir = dir;
+       pstate.dir = so->currPos.dir = dir;
        so->currPos.nextTupleOffset = 0;
 
        /* either moreRight or moreLeft should be set now (may be unset later) */
@@ -244,7 +246,7 @@ _bt_readpage(IndexScanDesc scan, ScanDirection dir, OffsetNumber offnum,
                         * If the scan specifies not to return killed tuples, then we
                         * treat a killed tuple as not passing the qual
                         */
-                       if (scan->ignore_killed_tuples && ItemIdIsDead(iid))
+                       if (ignore_killed_tuples && ItemIdIsDead(iid))
                        {
                                offnum = OffsetNumberNext(offnum);
                                continue;
@@ -402,7 +404,7 @@ _bt_readpage(IndexScanDesc scan, ScanDirection dir, OffsetNumber offnum,
                         * uselessly advancing to the page to the left.  This is similar
                         * to the high key optimization used by forward scans.
                         */
-                       if (scan->ignore_killed_tuples && ItemIdIsDead(iid))
+                       if (ignore_killed_tuples && ItemIdIsDead(iid))
                        {
                                if (offnum > minoff)
                                {
@@ -1157,8 +1159,8 @@ _bt_checkkeys(IndexScanDesc scan, BTReadPageState *pstate, bool arrayKeys,
                          IndexTuple tuple, int tupnatts)
 {
        TupleDesc       tupdesc = RelationGetDescr(scan->indexRelation);
-       BTScanOpaque so = (BTScanOpaque) scan->opaque;
-       ScanDirection dir = so->currPos.dir;
+       BTScanOpaque so PG_USED_FOR_ASSERTS_ONLY = (BTScanOpaque) scan->opaque;
+       ScanDirection dir = pstate->dir;
        int                     ikey = pstate->startikey;
        bool            res;
 
@@ -2059,8 +2061,7 @@ static void
 _bt_checkkeys_look_ahead(IndexScanDesc scan, BTReadPageState *pstate,
                                                 int tupnatts, TupleDesc tupdesc)
 {
-       BTScanOpaque so = (BTScanOpaque) scan->opaque;
-       ScanDirection dir = so->currPos.dir;
+       ScanDirection dir = pstate->dir;
        OffsetNumber aheadoffnum;
        IndexTuple      ahead;
 
@@ -2193,7 +2194,7 @@ _bt_advance_array_keys(IndexScanDesc scan, BTReadPageState *pstate,
 {
        BTScanOpaque so = (BTScanOpaque) scan->opaque;
        Relation        rel = scan->indexRelation;
-       ScanDirection dir = so->currPos.dir;
+       ScanDirection dir = pstate ? pstate->dir : ForwardScanDirection;
        int                     arrayidx = 0;
        bool            beyond_end_advance = false,
                                skip_array_advanced = false,