Skip to content

Commit e6336b8

Browse files
committed
Save a few cycles in advance_transition_function().
Keep a pre-initialized FunctionCallInfoData in AggStatePerAggData, and re-use that at each row instead of doing InitFunctionCallInfoData each time. This saves only half a dozen assignments and maybe some stack manipulation, and yet that seems to be good for a percent or two of the overall query run time for simple aggregates such as count(*). The cost is that the FunctionCallInfoData (which is about a kilobyte, on 64-bit machines) stays allocated for the duration of the query instead of being short-lived stack data. But we're already paying an equivalent space cost for each regular FuncExpr or OpExpr node, so I don't feel bad about paying it for aggregate functions. The code seems a little cleaner this way too, since the number of things passed to advance_transition_function decreases.
1 parent d59ff6c commit e6336b8

File tree

1 file changed

+38
-27
lines changed

1 file changed

+38
-27
lines changed

src/backend/executor/nodeAgg.c

Lines changed: 38 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -235,6 +235,14 @@ typedef struct AggStatePerAggData
235235
*/
236236

237237
Tuplesortstate *sortstate; /* sort object, if DISTINCT or ORDER BY */
238+
239+
/*
240+
* This field is a pre-initialized FunctionCallInfo struct used for
241+
* calling this aggregate's transfn. We save a few cycles per row by not
242+
* re-initializing the unchanging fields; which isn't much, but it seems
243+
* worth the extra space consumption.
244+
*/
245+
FunctionCallInfoData transfn_fcinfo;
238246
} AggStatePerAggData;
239247

240248
/*
@@ -290,8 +298,7 @@ static void initialize_aggregates(AggState *aggstate,
290298
AggStatePerGroup pergroup);
291299
static void advance_transition_function(AggState *aggstate,
292300
AggStatePerAgg peraggstate,
293-
AggStatePerGroup pergroupstate,
294-
FunctionCallInfoData *fcinfo);
301+
AggStatePerGroup pergroupstate);
295302
static void advance_aggregates(AggState *aggstate, AggStatePerGroup pergroup);
296303
static void process_ordered_aggregate_single(AggState *aggstate,
297304
AggStatePerAgg peraggstate,
@@ -399,28 +406,30 @@ initialize_aggregates(AggState *aggstate,
399406
* Given new input value(s), advance the transition function of an aggregate.
400407
*
401408
* The new values (and null flags) have been preloaded into argument positions
402-
* 1 and up in fcinfo, so that we needn't copy them again to pass to the
403-
* transition function. No other fields of fcinfo are assumed valid.
409+
* 1 and up in peraggstate->transfn_fcinfo, so that we needn't copy them again
410+
* to pass to the transition function. We also expect that the static fields
411+
* of the fcinfo are already initialized; that was done by ExecInitAgg().
404412
*
405413
* It doesn't matter which memory context this is called in.
406414
*/
407415
static void
408416
advance_transition_function(AggState *aggstate,
409417
AggStatePerAgg peraggstate,
410-
AggStatePerGroup pergroupstate,
411-
FunctionCallInfoData *fcinfo)
418+
AggStatePerGroup pergroupstate)
412419
{
413-
int numTransInputs = peraggstate->numTransInputs;
420+
FunctionCallInfo fcinfo = &peraggstate->transfn_fcinfo;
414421
MemoryContext oldContext;
415422
Datum newVal;
416-
int i;
417423

418424
if (peraggstate->transfn.fn_strict)
419425
{
420426
/*
421427
* For a strict transfn, nothing happens when there's a NULL input; we
422428
* just keep the prior transValue.
423429
*/
430+
int numTransInputs = peraggstate->numTransInputs;
431+
int i;
432+
424433
for (i = 1; i <= numTransInputs; i++)
425434
{
426435
if (fcinfo->argnull[i])
@@ -467,12 +476,9 @@ advance_transition_function(AggState *aggstate,
467476
/*
468477
* OK to call the transition function
469478
*/
470-
InitFunctionCallInfoData(*fcinfo, &(peraggstate->transfn),
471-
numTransInputs + 1,
472-
peraggstate->aggCollation,
473-
(void *) aggstate, NULL);
474479
fcinfo->arg[0] = pergroupstate->transValue;
475480
fcinfo->argnull[0] = pergroupstate->transValueIsNull;
481+
fcinfo->isnull = false; /* just in case transfn doesn't set it */
476482

477483
newVal = FunctionCallInvoke(fcinfo);
478484

@@ -574,19 +580,18 @@ advance_aggregates(AggState *aggstate, AggStatePerGroup pergroup)
574580
else
575581
{
576582
/* We can apply the transition function immediately */
577-
FunctionCallInfoData fcinfo;
583+
FunctionCallInfo fcinfo = &peraggstate->transfn_fcinfo;
578584

579585
/* Load values into fcinfo */
580586
/* Start from 1, since the 0th arg will be the transition value */
581587
Assert(slot->tts_nvalid >= numTransInputs);
582588
for (i = 0; i < numTransInputs; i++)
583589
{
584-
fcinfo.arg[i + 1] = slot->tts_values[i];
585-
fcinfo.argnull[i + 1] = slot->tts_isnull[i];
590+
fcinfo->arg[i + 1] = slot->tts_values[i];
591+
fcinfo->argnull[i + 1] = slot->tts_isnull[i];
586592
}
587593

588-
advance_transition_function(aggstate, peraggstate, pergroupstate,
589-
&fcinfo);
594+
advance_transition_function(aggstate, peraggstate, pergroupstate);
590595
}
591596
}
592597
}
@@ -622,17 +627,17 @@ process_ordered_aggregate_single(AggState *aggstate,
622627
MemoryContext workcontext = aggstate->tmpcontext->ecxt_per_tuple_memory;
623628
MemoryContext oldContext;
624629
bool isDistinct = (peraggstate->numDistinctCols > 0);
630+
FunctionCallInfo fcinfo = &peraggstate->transfn_fcinfo;
625631
Datum *newVal;
626632
bool *isNull;
627-
FunctionCallInfoData fcinfo;
628633

629634
Assert(peraggstate->numDistinctCols < 2);
630635

631636
tuplesort_performsort(peraggstate->sortstate);
632637

633638
/* Load the column into argument 1 (arg 0 will be transition value) */
634-
newVal = fcinfo.arg + 1;
635-
isNull = fcinfo.argnull + 1;
639+
newVal = fcinfo->arg + 1;
640+
isNull = fcinfo->argnull + 1;
636641

637642
/*
638643
* Note: if input type is pass-by-ref, the datums returned by the sort are
@@ -668,8 +673,7 @@ process_ordered_aggregate_single(AggState *aggstate,
668673
}
669674
else
670675
{
671-
advance_transition_function(aggstate, peraggstate, pergroupstate,
672-
&fcinfo);
676+
advance_transition_function(aggstate, peraggstate, pergroupstate);
673677
/* forget the old value, if any */
674678
if (!oldIsNull && !peraggstate->inputtypeByVal)
675679
pfree(DatumGetPointer(oldVal));
@@ -704,7 +708,7 @@ process_ordered_aggregate_multi(AggState *aggstate,
704708
AggStatePerGroup pergroupstate)
705709
{
706710
MemoryContext workcontext = aggstate->tmpcontext->ecxt_per_tuple_memory;
707-
FunctionCallInfoData fcinfo;
711+
FunctionCallInfo fcinfo = &peraggstate->transfn_fcinfo;
708712
TupleTableSlot *slot1 = peraggstate->evalslot;
709713
TupleTableSlot *slot2 = peraggstate->uniqslot;
710714
int numTransInputs = peraggstate->numTransInputs;
@@ -739,12 +743,11 @@ process_ordered_aggregate_multi(AggState *aggstate,
739743
/* Start from 1, since the 0th arg will be the transition value */
740744
for (i = 0; i < numTransInputs; i++)
741745
{
742-
fcinfo.arg[i + 1] = slot1->tts_values[i];
743-
fcinfo.argnull[i + 1] = slot1->tts_isnull[i];
746+
fcinfo->arg[i + 1] = slot1->tts_values[i];
747+
fcinfo->argnull[i + 1] = slot1->tts_isnull[i];
744748
}
745749

746-
advance_transition_function(aggstate, peraggstate, pergroupstate,
747-
&fcinfo);
750+
advance_transition_function(aggstate, peraggstate, pergroupstate);
748751

749752
if (numDistinctCols > 0)
750753
{
@@ -1799,6 +1802,7 @@ ExecInitAgg(Agg *node, EState *estate, int eflags)
17991802
&transfnexpr,
18001803
&finalfnexpr);
18011804

1805+
/* set up infrastructure for calling the transfn and finalfn */
18021806
fmgr_info(transfn_oid, &peraggstate->transfn);
18031807
fmgr_info_set_expr((Node *) transfnexpr, &peraggstate->transfn);
18041808

@@ -1810,6 +1814,13 @@ ExecInitAgg(Agg *node, EState *estate, int eflags)
18101814

18111815
peraggstate->aggCollation = aggref->inputcollid;
18121816

1817+
InitFunctionCallInfoData(peraggstate->transfn_fcinfo,
1818+
&peraggstate->transfn,
1819+
peraggstate->numTransInputs + 1,
1820+
peraggstate->aggCollation,
1821+
(void *) aggstate, NULL);
1822+
1823+
/* get info about relevant datatypes */
18131824
get_typlenbyval(aggref->aggtype,
18141825
&peraggstate->resulttypeLen,
18151826
&peraggstate->resulttypeByVal);

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy