Speedup WindowAgg code by moving uncommon code out-of-line

The code to calculate the frame offsets is only performed once per scan.
Moving this code out of line gives a small (around 4-5%) speedup when testing
with some CPUs.  Other tested CPUs are indifferent to the change.

Reviewed-by: Ashutosh Bapat <ashutosh.bapat.oss@gmail.com>
Reviewed-by: Tatsuo Ishii <ishii@postgresql.org>
Discussion: https://postgr.es/m/CAApHDvqPgFtwme2Zyf75BpMLwYr2mnUstDyPiP%3DEpudYuQTPPQ%40mail.gmail.com
This commit is contained in:
David Rowley 2024-09-05 15:59:47 +12:00
parent 06421b0843
commit 19b861f880
1 changed files with 78 additions and 62 deletions

View File

@ -2032,6 +2032,82 @@ update_grouptailpos(WindowAggState *winstate)
MemoryContextSwitchTo(oldcontext); MemoryContextSwitchTo(oldcontext);
} }
/*
* calculate_frame_offsets
* Determine the startOffsetValue and endOffsetValue values for the
* WindowAgg's frame options.
*/
static pg_noinline void
calculate_frame_offsets(PlanState *pstate)
{
WindowAggState *winstate = castNode(WindowAggState, pstate);
ExprContext *econtext;
int frameOptions = winstate->frameOptions;
Datum value;
bool isnull;
int16 len;
bool byval;
/* Ensure we've not been called before for this scan */
Assert(winstate->all_first);
econtext = winstate->ss.ps.ps_ExprContext;
if (frameOptions & FRAMEOPTION_START_OFFSET)
{
Assert(winstate->startOffset != NULL);
value = ExecEvalExprSwitchContext(winstate->startOffset,
econtext,
&isnull);
if (isnull)
ereport(ERROR,
(errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED),
errmsg("frame starting offset must not be null")));
/* copy value into query-lifespan context */
get_typlenbyval(exprType((Node *) winstate->startOffset->expr),
&len,
&byval);
winstate->startOffsetValue = datumCopy(value, byval, len);
if (frameOptions & (FRAMEOPTION_ROWS | FRAMEOPTION_GROUPS))
{
/* value is known to be int8 */
int64 offset = DatumGetInt64(value);
if (offset < 0)
ereport(ERROR,
(errcode(ERRCODE_INVALID_PRECEDING_OR_FOLLOWING_SIZE),
errmsg("frame starting offset must not be negative")));
}
}
if (frameOptions & FRAMEOPTION_END_OFFSET)
{
Assert(winstate->endOffset != NULL);
value = ExecEvalExprSwitchContext(winstate->endOffset,
econtext,
&isnull);
if (isnull)
ereport(ERROR,
(errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED),
errmsg("frame ending offset must not be null")));
/* copy value into query-lifespan context */
get_typlenbyval(exprType((Node *) winstate->endOffset->expr),
&len,
&byval);
winstate->endOffsetValue = datumCopy(value, byval, len);
if (frameOptions & (FRAMEOPTION_ROWS | FRAMEOPTION_GROUPS))
{
/* value is known to be int8 */
int64 offset = DatumGetInt64(value);
if (offset < 0)
ereport(ERROR,
(errcode(ERRCODE_INVALID_PRECEDING_OR_FOLLOWING_SIZE),
errmsg("frame ending offset must not be negative")));
}
}
winstate->all_first = false;
}
/* ----------------- /* -----------------
* ExecWindowAgg * ExecWindowAgg
@ -2061,68 +2137,8 @@ ExecWindowAgg(PlanState *pstate)
* rescan). These are assumed to hold constant throughout the scan; if * rescan). These are assumed to hold constant throughout the scan; if
* user gives us a volatile expression, we'll only use its initial value. * user gives us a volatile expression, we'll only use its initial value.
*/ */
if (winstate->all_first) if (unlikely(winstate->all_first))
{ calculate_frame_offsets(pstate);
int frameOptions = winstate->frameOptions;
Datum value;
bool isnull;
int16 len;
bool byval;
econtext = winstate->ss.ps.ps_ExprContext;
if (frameOptions & FRAMEOPTION_START_OFFSET)
{
Assert(winstate->startOffset != NULL);
value = ExecEvalExprSwitchContext(winstate->startOffset,
econtext,
&isnull);
if (isnull)
ereport(ERROR,
(errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED),
errmsg("frame starting offset must not be null")));
/* copy value into query-lifespan context */
get_typlenbyval(exprType((Node *) winstate->startOffset->expr),
&len, &byval);
winstate->startOffsetValue = datumCopy(value, byval, len);
if (frameOptions & (FRAMEOPTION_ROWS | FRAMEOPTION_GROUPS))
{
/* value is known to be int8 */
int64 offset = DatumGetInt64(value);
if (offset < 0)
ereport(ERROR,
(errcode(ERRCODE_INVALID_PRECEDING_OR_FOLLOWING_SIZE),
errmsg("frame starting offset must not be negative")));
}
}
if (frameOptions & FRAMEOPTION_END_OFFSET)
{
Assert(winstate->endOffset != NULL);
value = ExecEvalExprSwitchContext(winstate->endOffset,
econtext,
&isnull);
if (isnull)
ereport(ERROR,
(errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED),
errmsg("frame ending offset must not be null")));
/* copy value into query-lifespan context */
get_typlenbyval(exprType((Node *) winstate->endOffset->expr),
&len, &byval);
winstate->endOffsetValue = datumCopy(value, byval, len);
if (frameOptions & (FRAMEOPTION_ROWS | FRAMEOPTION_GROUPS))
{
/* value is known to be int8 */
int64 offset = DatumGetInt64(value);
if (offset < 0)
ereport(ERROR,
(errcode(ERRCODE_INVALID_PRECEDING_OR_FOLLOWING_SIZE),
errmsg("frame ending offset must not be negative")));
}
}
winstate->all_first = false;
}
/* We need to loop as the runCondition or qual may filter out tuples */ /* We need to loop as the runCondition or qual may filter out tuples */
for (;;) for (;;)