Re: unnecessary executor overheads around seqscans

From: Heikki Linnakangas <hlinnaka(at)iki(dot)fi>
To: Andres Freund <andres(at)anarazel(dot)de>, pgsql-hackers(at)postgresql(dot)org, Amit Langote <amitlangote09(at)gmail(dot)com>, Amit Kapila <amit(dot)kapila16(at)gmail(dot)com>, David Rowley <dgrowleyml(at)gmail(dot)com>
Subject: Re: unnecessary executor overheads around seqscans
Date: 2026-01-26 15:23:16
Message-ID: 2c266fc0-8cd0-41c3-bd28-a874c422d158@iki.fi
Views: Whole Thread | Raw Message | Download mbox | Resend email
Thread:
Lists: pgsql-hackers

On 23/01/2026 22:16, Andres Freund wrote:
> Hi,
>
> In [1] I was looking at the profile of a seqscan with a where clause that
> doesn't match any of the many rows. I was a bit saddened by where we were
> spending time.
>
>
> - The fetching of variables, as well as the null check of scandesc, in
> SeqNext() is repeated in every loop iteration of ExecScanExtended, despite
> that obviously not being required after the first iteration
>
> We could perhaps address this by moving the check to the callers of
> ExecScanExtended() or by extending ExecScanExtended to have an explicit
> beginscan callback that it calls after.

For context, we're talking about this in SeqNext:

> /*
> * get information from the estate and scan state
> */
> scandesc = node->ss.ss_currentScanDesc;
> estate = node->ss.ps.state;
> direction = estate->es_direction;
> slot = node->ss.ss_ScanTupleSlot;

Hmm. I guess the compiler doesn't know that the variables don't change
between calls, so it has to fetch them on every iteration. Passing them
through a 'const' pointer might give it clue, but I'm not sure how to
shoehorn that here.

Perhaps we should turn the ExecScanExtended() function inside out.
Instead of passing SeqNext as a callback to ExecScanExtended(), we would
have a function like this (for illustration purposes only, doesn't compile):

> /* ----------------------------------------------------------------
> * ExecSeqNextInline
> *
> * This is a workhorse for ExecSeqScan. It's inlined into
> * specialized implementations for cases where epqstate, qual,
> * projInfo are NULL or not.
> * ----------------------------------------------------------------
> */
> static pg_attribute_always_inline TupleTableSlot *
> ExecSeqScanInline(SeqScanState *node,
> EPQState *epqstate,
> ExprState *qual,
> ProjectionInfo *projInfo)
> {
> /*
> * get information from the estate and scan state
> */
> scandesc = node->ss.ss_currentScanDesc;
> estate = node->ss.ps.state;
> direction = estate->es_direction;
> slot = node->ss.ss_ScanTupleSlot;
>
> if (scandesc == NULL)
> {
> /*
> * We reach here if the scan is not parallel, or if we're serially
> * executing a scan that was planned to be parallel.
> */
> scandesc = table_beginscan(node->ss.ss_currentRelation,
> estate->es_snapshot,
> 0, NULL);
> node->ss.ss_currentScanDesc = scandesc;
> }
>
> for (;;)
> {
> /* do ExecScanFetch(), except for the call to SeqScanNext */
> slot = ExecScanFetchEPQ(...);
>
> /*
> * get the row next tuple from the table
> */
> if (slot == NULL)
> slot = table_scan_getnextslot(scandesc, direction, slot);
>
> if (slot == NULL)
> break;
>
> /*
> * Does it pass the quals? (This does the parts of ExecScanExtended()
> * after the ExecScanFetch() call)
> */
> slot = ExecScanProjectAndFilter(&node->ss, slot, qual, projInfo);
> if (slot)
> return slot;
> }
> return NULL;
> }

- Heikki

In response to

Browse pgsql-hackers by date

  From Date Subject
Next Message Zsolt Parragi 2026-01-26 15:36:30 Re: meson: Make test output much more useful on failure (both in CI and locally)
Previous Message Peter Eisentraut 2026-01-26 15:09:00 Re: Fix accidentally cast away qualifiers