Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commitfb9f955

Browse files
amitlanDavid Rowley
and
David Rowley
committed
Refactor ExecScan() to allow inlining of its core logic
This commit refactors ExecScan() by moving its tuple-fetching,filtering, and projection logic into an inline-able function,ExecScanExtended(), defined in src/include/executor/execScan.h.ExecScanExtended() accepts parameters for EvalPlanQual state,qualifiers (ExprState), and projection (ProjectionInfo).Specialized variants of the execution function of a given Scan node(for example, ExecSeqScan() for SeqScan) can then pass const-NULL forunused parameters. This allows the compiler to inline the logic andeliminate unnecessary branches or checks. Each variant function thuscontains only the necessary code, optimizing execution for scanswhere these features are not needed.The variant function to be used is determined in the ExecInit*()function of the node and assigned to the ExecProcNode function pointerin the node's PlanState, effectively turning runtime checks andconditional branches on the NULLness of epqstate, qual, and projInfointo static ones, provided the compiler successfully eliminatesunnecessary checks from the inlined code of ExecScanExtended().Currently, only ExecSeqScan() is modified to take advantage of thisinline-ability. Other Scan nodes might benefit from such specializedvariant functions but that is left as future work.Benchmarks performed by Junwang Zhao, David Rowley and myself show upto a 5% reduction in execution time for queries that rely heavily onSeq Scans. The most significant improvements were observed inscenarios where EvalPlanQual, qualifiers, and projection were notrequired, but other cases also benefit from reduced runtime overheaddue to the inlining and removal of unnecessary code paths.The idea for this patch first came from Andres Freund in an off-listdiscussion. The refactoring approach implemented here is based on aproposal by David Rowley, significantly improving upon the patch I(amitlan) initially proposed.Suggested-by: Andres Freund <andres@anarazel.de>Co-authored-by: David Rowley <dgrowleyml@gmail.com>Reviewed-by: David Rowley <dgrowleyml@gmail.com>Reviewed-by: Junwang Zhao <zhjwpku@gmail.com>Tested-by: Junwang Zhao <zhjwpku@gmail.com>Tested-by: David Rowley <dgrowleyml@gmail.com>Discussion:https://postgr.es/m/CA+HiwqGaH-otvqW_ce-paL=96JvU4j+Xbuk+14esJNDwefdkOg@mail.gmail.com
1 parent4feba03 commitfb9f955

File tree

3 files changed

+365
-203
lines changed

3 files changed

+365
-203
lines changed

‎src/backend/executor/execScan.c

Lines changed: 9 additions & 198 deletions
Original file line numberDiff line numberDiff line change
@@ -19,118 +19,9 @@
1919
#include"postgres.h"
2020

2121
#include"executor/executor.h"
22+
#include"executor/execScan.h"
2223
#include"miscadmin.h"
2324

24-
25-
26-
/*
27-
* ExecScanFetch -- check interrupts & fetch next potential tuple
28-
*
29-
* This routine is concerned with substituting a test tuple if we are
30-
* inside an EvalPlanQual recheck. If we aren't, just execute
31-
* the access method's next-tuple routine.
32-
*/
33-
staticinlineTupleTableSlot*
34-
ExecScanFetch(ScanState*node,
35-
ExecScanAccessMtdaccessMtd,
36-
ExecScanRecheckMtdrecheckMtd)
37-
{
38-
EState*estate=node->ps.state;
39-
40-
CHECK_FOR_INTERRUPTS();
41-
42-
if (estate->es_epq_active!=NULL)
43-
{
44-
EPQState*epqstate=estate->es_epq_active;
45-
46-
/*
47-
* We are inside an EvalPlanQual recheck. Return the test tuple if
48-
* one is available, after rechecking any access-method-specific
49-
* conditions.
50-
*/
51-
Indexscanrelid= ((Scan*)node->ps.plan)->scanrelid;
52-
53-
if (scanrelid==0)
54-
{
55-
/*
56-
* This is a ForeignScan or CustomScan which has pushed down a
57-
* join to the remote side. The recheck method is responsible not
58-
* only for rechecking the scan/join quals but also for storing
59-
* the correct tuple in the slot.
60-
*/
61-
62-
TupleTableSlot*slot=node->ss_ScanTupleSlot;
63-
64-
if (!(*recheckMtd) (node,slot))
65-
ExecClearTuple(slot);/* would not be returned by scan */
66-
returnslot;
67-
}
68-
elseif (epqstate->relsubs_done[scanrelid-1])
69-
{
70-
/*
71-
* Return empty slot, as either there is no EPQ tuple for this rel
72-
* or we already returned it.
73-
*/
74-
75-
TupleTableSlot*slot=node->ss_ScanTupleSlot;
76-
77-
returnExecClearTuple(slot);
78-
}
79-
elseif (epqstate->relsubs_slot[scanrelid-1]!=NULL)
80-
{
81-
/*
82-
* Return replacement tuple provided by the EPQ caller.
83-
*/
84-
85-
TupleTableSlot*slot=epqstate->relsubs_slot[scanrelid-1];
86-
87-
Assert(epqstate->relsubs_rowmark[scanrelid-1]==NULL);
88-
89-
/* Mark to remember that we shouldn't return it again */
90-
epqstate->relsubs_done[scanrelid-1]= true;
91-
92-
/* Return empty slot if we haven't got a test tuple */
93-
if (TupIsNull(slot))
94-
returnNULL;
95-
96-
/* Check if it meets the access-method conditions */
97-
if (!(*recheckMtd) (node,slot))
98-
returnExecClearTuple(slot);/* would not be returned by
99-
* scan */
100-
returnslot;
101-
}
102-
elseif (epqstate->relsubs_rowmark[scanrelid-1]!=NULL)
103-
{
104-
/*
105-
* Fetch and return replacement tuple using a non-locking rowmark.
106-
*/
107-
108-
TupleTableSlot*slot=node->ss_ScanTupleSlot;
109-
110-
/* Mark to remember that we shouldn't return more */
111-
epqstate->relsubs_done[scanrelid-1]= true;
112-
113-
if (!EvalPlanQualFetchRowMark(epqstate,scanrelid,slot))
114-
returnNULL;
115-
116-
/* Return empty slot if we haven't got a test tuple */
117-
if (TupIsNull(slot))
118-
returnNULL;
119-
120-
/* Check if it meets the access-method conditions */
121-
if (!(*recheckMtd) (node,slot))
122-
returnExecClearTuple(slot);/* would not be returned by
123-
* scan */
124-
returnslot;
125-
}
126-
}
127-
128-
/*
129-
* Run the node-type-specific access method function to get the next tuple
130-
*/
131-
return (*accessMtd) (node);
132-
}
133-
13425
/* ----------------------------------------------------------------
13526
*ExecScan
13627
*
@@ -157,100 +48,20 @@ ExecScan(ScanState *node,
15748
ExecScanAccessMtdaccessMtd,/* function returning a tuple */
15849
ExecScanRecheckMtdrecheckMtd)
15950
{
160-
ExprContext*econtext;
51+
EPQState*epqstate;
16152
ExprState*qual;
16253
ProjectionInfo*projInfo;
16354

164-
/*
165-
* Fetch data from node
166-
*/
55+
epqstate=node->ps.state->es_epq_active;
16756
qual=node->ps.qual;
16857
projInfo=node->ps.ps_ProjInfo;
169-
econtext=node->ps.ps_ExprContext;
170-
171-
/* interrupt checks are in ExecScanFetch */
172-
173-
/*
174-
* If we have neither a qual to check nor a projection to do, just skip
175-
* all the overhead and return the raw scan tuple.
176-
*/
177-
if (!qual&& !projInfo)
178-
{
179-
ResetExprContext(econtext);
180-
returnExecScanFetch(node,accessMtd,recheckMtd);
181-
}
182-
183-
/*
184-
* Reset per-tuple memory context to free any expression evaluation
185-
* storage allocated in the previous tuple cycle.
186-
*/
187-
ResetExprContext(econtext);
188-
189-
/*
190-
* get a tuple from the access method. Loop until we obtain a tuple that
191-
* passes the qualification.
192-
*/
193-
for (;;)
194-
{
195-
TupleTableSlot*slot;
19658

197-
slot=ExecScanFetch(node,accessMtd,recheckMtd);
198-
199-
/*
200-
* if the slot returned by the accessMtd contains NULL, then it means
201-
* there is nothing more to scan so we just return an empty slot,
202-
* being careful to use the projection result slot so it has correct
203-
* tupleDesc.
204-
*/
205-
if (TupIsNull(slot))
206-
{
207-
if (projInfo)
208-
returnExecClearTuple(projInfo->pi_state.resultslot);
209-
else
210-
returnslot;
211-
}
212-
213-
/*
214-
* place the current tuple into the expr context
215-
*/
216-
econtext->ecxt_scantuple=slot;
217-
218-
/*
219-
* check that the current tuple satisfies the qual-clause
220-
*
221-
* check for non-null qual here to avoid a function call to ExecQual()
222-
* when the qual is null ... saves only a few cycles, but they add up
223-
* ...
224-
*/
225-
if (qual==NULL||ExecQual(qual,econtext))
226-
{
227-
/*
228-
* Found a satisfactory scan tuple.
229-
*/
230-
if (projInfo)
231-
{
232-
/*
233-
* Form a projection tuple, store it in the result tuple slot
234-
* and return it.
235-
*/
236-
returnExecProject(projInfo);
237-
}
238-
else
239-
{
240-
/*
241-
* Here, we aren't projecting, so just return scan tuple.
242-
*/
243-
returnslot;
244-
}
245-
}
246-
else
247-
InstrCountFiltered1(node,1);
248-
249-
/*
250-
* Tuple fails qual, so free per-tuple memory and try again.
251-
*/
252-
ResetExprContext(econtext);
253-
}
59+
returnExecScanExtended(node,
60+
accessMtd,
61+
recheckMtd,
62+
epqstate,
63+
qual,
64+
projInfo);
25465
}
25566

25667
/*

‎src/backend/executor/nodeSeqscan.c

Lines changed: 110 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@
2929

3030
#include"access/relscan.h"
3131
#include"access/tableam.h"
32+
#include"executor/execScan.h"
3233
#include"executor/executor.h"
3334
#include"executor/nodeSeqscan.h"
3435
#include"utils/rel.h"
@@ -99,22 +100,105 @@ SeqRecheck(SeqScanState *node, TupleTableSlot *slot)
99100
*ExecSeqScan(node)
100101
*
101102
*Scans the relation sequentially and returns the next qualifying
102-
*tuple.
103-
*We call the ExecScan() routine and pass it the appropriate
104-
*access method functions.
103+
*tuple. This variant is used when there is no es_eqp_active, no qual
104+
*and no projection. Passing const-NULLs for these to ExecScanExtended
105+
*allows the compiler to eliminate the additional code that would
106+
*ordinarily be required for the evaluation of these.
105107
* ----------------------------------------------------------------
106108
*/
107109
staticTupleTableSlot*
108110
ExecSeqScan(PlanState*pstate)
109111
{
110112
SeqScanState*node=castNode(SeqScanState,pstate);
111113

114+
Assert(pstate->state->es_epq_active==NULL);
115+
Assert(pstate->qual==NULL);
116+
Assert(pstate->ps_ProjInfo==NULL);
117+
118+
returnExecScanExtended(&node->ss,
119+
(ExecScanAccessMtd)SeqNext,
120+
(ExecScanRecheckMtd)SeqRecheck,
121+
NULL,
122+
NULL,
123+
NULL);
124+
}
125+
126+
/*
127+
* Variant of ExecSeqScan() but when qual evaluation is required.
128+
*/
129+
staticTupleTableSlot*
130+
ExecSeqScanWithQual(PlanState*pstate)
131+
{
132+
SeqScanState*node=castNode(SeqScanState,pstate);
133+
134+
Assert(pstate->state->es_epq_active==NULL);
135+
Assert(pstate->qual!=NULL);
136+
Assert(pstate->ps_ProjInfo==NULL);
137+
138+
returnExecScanExtended(&node->ss,
139+
(ExecScanAccessMtd)SeqNext,
140+
(ExecScanRecheckMtd)SeqRecheck,
141+
NULL,
142+
pstate->qual,
143+
NULL);
144+
}
145+
146+
/*
147+
* Variant of ExecSeqScan() but when projection is required.
148+
*/
149+
staticTupleTableSlot*
150+
ExecSeqScanWithProject(PlanState*pstate)
151+
{
152+
SeqScanState*node=castNode(SeqScanState,pstate);
153+
154+
Assert(pstate->state->es_epq_active==NULL);
155+
Assert(pstate->qual==NULL);
156+
Assert(pstate->ps_ProjInfo!=NULL);
157+
158+
returnExecScanExtended(&node->ss,
159+
(ExecScanAccessMtd)SeqNext,
160+
(ExecScanRecheckMtd)SeqRecheck,
161+
NULL,
162+
NULL,
163+
pstate->ps_ProjInfo);
164+
}
165+
166+
/*
167+
* Variant of ExecSeqScan() but when qual evaluation and projection are
168+
* required.
169+
*/
170+
staticTupleTableSlot*
171+
ExecSeqScanWithQualProject(PlanState*pstate)
172+
{
173+
SeqScanState*node=castNode(SeqScanState,pstate);
174+
175+
Assert(pstate->state->es_epq_active==NULL);
176+
Assert(pstate->qual!=NULL);
177+
Assert(pstate->ps_ProjInfo!=NULL);
178+
179+
returnExecScanExtended(&node->ss,
180+
(ExecScanAccessMtd)SeqNext,
181+
(ExecScanRecheckMtd)SeqRecheck,
182+
NULL,
183+
pstate->qual,
184+
pstate->ps_ProjInfo);
185+
}
186+
187+
/*
188+
* Variant of ExecSeqScan for when EPQ evaluation is required. We don't
189+
* bother adding variants of this for with/without qual and projection as
190+
* EPQ doesn't seem as exciting a case to optimize for.
191+
*/
192+
staticTupleTableSlot*
193+
ExecSeqScanEPQ(PlanState*pstate)
194+
{
195+
SeqScanState*node=castNode(SeqScanState,pstate);
196+
112197
returnExecScan(&node->ss,
113198
(ExecScanAccessMtd)SeqNext,
114199
(ExecScanRecheckMtd)SeqRecheck);
115200
}
116201

117-
118202
/* ----------------------------------------------------------------
119203
*ExecInitSeqScan
120204
* ----------------------------------------------------------------
@@ -137,7 +221,6 @@ ExecInitSeqScan(SeqScan *node, EState *estate, int eflags)
137221
scanstate=makeNode(SeqScanState);
138222
scanstate->ss.ps.plan= (Plan*)node;
139223
scanstate->ss.ps.state=estate;
140-
scanstate->ss.ps.ExecProcNode=ExecSeqScan;
141224

142225
/*
143226
* Miscellaneous initialization
@@ -171,6 +254,28 @@ ExecInitSeqScan(SeqScan *node, EState *estate, int eflags)
171254
scanstate->ss.ps.qual=
172255
ExecInitQual(node->scan.plan.qual, (PlanState*)scanstate);
173256

257+
/*
258+
* When EvalPlanQual() is not in use, assign ExecProcNode for this node
259+
* based on the presence of qual and projection. Each ExecSeqScan*()
260+
* variant is optimized for the specific combination of these conditions.
261+
*/
262+
if (scanstate->ss.ps.state->es_epq_active!=NULL)
263+
scanstate->ss.ps.ExecProcNode=ExecSeqScanEPQ;
264+
elseif (scanstate->ss.ps.qual==NULL)
265+
{
266+
if (scanstate->ss.ps.ps_ProjInfo==NULL)
267+
scanstate->ss.ps.ExecProcNode=ExecSeqScan;
268+
else
269+
scanstate->ss.ps.ExecProcNode=ExecSeqScanWithProject;
270+
}
271+
else
272+
{
273+
if (scanstate->ss.ps.ps_ProjInfo==NULL)
274+
scanstate->ss.ps.ExecProcNode=ExecSeqScanWithQual;
275+
else
276+
scanstate->ss.ps.ExecProcNode=ExecSeqScanWithQualProject;
277+
}
278+
174279
returnscanstate;
175280
}
176281

0 commit comments

Comments
 (0)

[8]ページ先頭

©2009-2025 Movatter.jp