Re: unnecessary executor overheads around seqscans - Mailing list pgsql-hackers

From Heikki Linnakangas
Subject Re: unnecessary executor overheads around seqscans
Date
Msg-id 2c266fc0-8cd0-41c3-bd28-a874c422d158@iki.fi
Whole thread Raw
In response to unnecessary executor overheads around seqscans  (Andres Freund <andres@anarazel.de>)
Responses Re: unnecessary executor overheads around seqscans
List 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



pgsql-hackers by date:

Previous
From: Peter Eisentraut
Date:
Subject: Re: Fix accidentally cast away qualifiers
Next
From: Zsolt Parragi
Date:
Subject: Re: meson: Make test output much more useful on failure (both in CI and locally)