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: