Remove size increase in ExprEvalStep caused by hashed saops
authorDavid Rowley <drowley@postgresql.org>
Wed, 6 Jul 2022 07:40:32 +0000 (19:40 +1200)
committerDavid Rowley <drowley@postgresql.org>
Wed, 6 Jul 2022 07:40:32 +0000 (19:40 +1200)
50e17ad28 increased the size of ExprEvalStep from 64 bytes up to 88 bytes.
Lots of effort was spent during the development of the current expression
evaluation code to make an instance of this struct as small as possible.
Making this struct larger than needed reduces CPU cache efficiency during
expression evaluation which causes noticeable slowdowns during query
execution.

In order to reduce the size of the struct, here we remove the fn_addr
field. The values from this field can be obtained via fcinfo, just with
some extra pointer dereferencing. The extra indirection does not seem to
cause any noticeable slowdowns.

Various other fields have been moved into the ScalarArrayOpExprHashTable
struct. These fields are only used when the ScalarArrayOpExprHashTable
pointer has already been dereferenced, so no additional pointer
dereferences occur for these. Here we also make hash_fcinfo_data the last
field in ScalarArrayOpExprHashTable so that we can avoid a further pointer
dereference to get the FunctionCallInfoBaseData. This also saves a call to
palloc().

50e17ad28 was added in 14, but it's too late to adjust the size of the
ExprEvalStep in that version, so here we just backpatch to 15, which is
currently in beta.

Author: Andres Freund, David Rowley
Discussion: https://postgr.es/m/20220616233130.rparivafipt6doj3@alap3.anarazel.de
Backpatch-through: 15

src/backend/executor/execExpr.c
src/backend/executor/execExprInterp.c
src/include/executor/execExpr.h

index 1f65daf203594295ee5450fd281e976be44be0a5..c8d7145fe3840cc404eee70ef91febe39d90d3f3 100644 (file)
@@ -1203,8 +1203,6 @@ ExecInitExprRec(Expr *node, ExprState *state,
                FmgrInfo   *finfo;
                FunctionCallInfo fcinfo;
                AclResult   aclresult;
-               FmgrInfo   *hash_finfo;
-               FunctionCallInfo hash_fcinfo;
                Oid         cmpfuncid;
 
                /*
@@ -1262,18 +1260,6 @@ ExecInitExprRec(Expr *node, ExprState *state,
                 */
                if (OidIsValid(opexpr->hashfuncid))
                {
-                   hash_finfo = palloc0(sizeof(FmgrInfo));
-                   hash_fcinfo = palloc0(SizeForFunctionCallInfo(1));
-                   fmgr_info(opexpr->hashfuncid, hash_finfo);
-                   fmgr_info_set_expr((Node *) node, hash_finfo);
-                   InitFunctionCallInfoData(*hash_fcinfo, hash_finfo,
-                                            1, opexpr->inputcollid, NULL,
-                                            NULL);
-
-                   scratch.d.hashedscalararrayop.hash_finfo = hash_finfo;
-                   scratch.d.hashedscalararrayop.hash_fcinfo_data = hash_fcinfo;
-                   scratch.d.hashedscalararrayop.hash_fn_addr = hash_finfo->fn_addr;
-
                    /* Evaluate scalar directly into left function argument */
                    ExecInitExprRec(scalararg, state,
                                    &fcinfo->args[0].value, &fcinfo->args[0].isnull);
@@ -1292,11 +1278,8 @@ ExecInitExprRec(Expr *node, ExprState *state,
                    scratch.d.hashedscalararrayop.inclause = opexpr->useOr;
                    scratch.d.hashedscalararrayop.finfo = finfo;
                    scratch.d.hashedscalararrayop.fcinfo_data = fcinfo;
-                   scratch.d.hashedscalararrayop.fn_addr = finfo->fn_addr;
+                   scratch.d.hashedscalararrayop.saop = opexpr;
 
-                   scratch.d.hashedscalararrayop.hash_finfo = hash_finfo;
-                   scratch.d.hashedscalararrayop.hash_fcinfo_data = hash_fcinfo;
-                   scratch.d.hashedscalararrayop.hash_fn_addr = hash_finfo->fn_addr;
 
                    ExprEvalPushStep(state, &scratch);
                }
index 80c5e04dd5fb2f6b6486975b77eb6f95f6694352..9fcb6be414d0a2709291a9f0eb895c8bf09c3201 100644 (file)
@@ -217,6 +217,8 @@ typedef struct ScalarArrayOpExprHashTable
 {
    saophash_hash *hashtab;     /* underlying hash table */
    struct ExprEvalStep *op;
+   FmgrInfo    hash_finfo;     /* function's lookup data */
+   FunctionCallInfoBaseData hash_fcinfo_data;  /* arguments etc */
 } ScalarArrayOpExprHashTable;
 
 /* Define parameters for ScalarArrayOpExpr hash table code generation. */
@@ -3474,13 +3476,13 @@ static uint32
 saop_element_hash(struct saophash_hash *tb, Datum key)
 {
    ScalarArrayOpExprHashTable *elements_tab = (ScalarArrayOpExprHashTable *) tb->private_data;
-   FunctionCallInfo fcinfo = elements_tab->op->d.hashedscalararrayop.hash_fcinfo_data;
+   FunctionCallInfo fcinfo = &elements_tab->hash_fcinfo_data;
    Datum       hash;
 
    fcinfo->args[0].value = key;
    fcinfo->args[0].isnull = false;
 
-   hash = elements_tab->op->d.hashedscalararrayop.hash_fn_addr(fcinfo);
+   hash = elements_tab->hash_finfo.fn_addr(fcinfo);
 
    return DatumGetUInt32(hash);
 }
@@ -3502,7 +3504,7 @@ saop_hash_element_match(struct saophash_hash *tb, Datum key1, Datum key2)
    fcinfo->args[1].value = key2;
    fcinfo->args[1].isnull = false;
 
-   result = elements_tab->op->d.hashedscalararrayop.fn_addr(fcinfo);
+   result = elements_tab->op->d.hashedscalararrayop.finfo->fn_addr(fcinfo);
 
    return DatumGetBool(result);
 }
@@ -3549,6 +3551,7 @@ ExecEvalHashedScalarArrayOp(ExprState *state, ExprEvalStep *op, ExprContext *eco
    /* Build the hash table on first evaluation */
    if (elements_tab == NULL)
    {
+       ScalarArrayOpExpr *saop;
        int16       typlen;
        bool        typbyval;
        char        typalign;
@@ -3560,6 +3563,8 @@ ExecEvalHashedScalarArrayOp(ExprState *state, ExprEvalStep *op, ExprContext *eco
        MemoryContext oldcontext;
        ArrayType  *arr;
 
+       saop = op->d.hashedscalararrayop.saop;
+
        arr = DatumGetArrayTypeP(*op->resvalue);
        nitems = ArrayGetNItems(ARR_NDIM(arr), ARR_DIMS(arr));
 
@@ -3571,10 +3576,21 @@ ExecEvalHashedScalarArrayOp(ExprState *state, ExprEvalStep *op, ExprContext *eco
        oldcontext = MemoryContextSwitchTo(econtext->ecxt_per_query_memory);
 
        elements_tab = (ScalarArrayOpExprHashTable *)
-           palloc(sizeof(ScalarArrayOpExprHashTable));
+           palloc0(offsetof(ScalarArrayOpExprHashTable, hash_fcinfo_data) +
+                   SizeForFunctionCallInfo(1));
        op->d.hashedscalararrayop.elements_tab = elements_tab;
        elements_tab->op = op;
 
+       fmgr_info(saop->hashfuncid, &elements_tab->hash_finfo);
+       fmgr_info_set_expr((Node *) saop, &elements_tab->hash_finfo);
+
+       InitFunctionCallInfoData(elements_tab->hash_fcinfo_data,
+                                &elements_tab->hash_finfo,
+                                1,
+                                saop->inputcollid,
+                                NULL,
+                                NULL);
+
        /*
         * Create the hash table sizing it according to the number of elements
         * in the array.  This does assume that the array has no duplicates.
@@ -3669,7 +3685,7 @@ ExecEvalHashedScalarArrayOp(ExprState *state, ExprEvalStep *op, ExprContext *eco
            fcinfo->args[1].value = (Datum) 0;
            fcinfo->args[1].isnull = true;
 
-           result = op->d.hashedscalararrayop.fn_addr(fcinfo);
+           result = op->d.hashedscalararrayop.finfo->fn_addr(fcinfo);
            resultnull = fcinfo->isnull;
 
            /*
index fa2d9be4c9932fa0b158dd4ef80853d248162c25..1e3f1bbee8614f56c22766fb0106005c59e96df0 100644 (file)
@@ -584,12 +584,7 @@ typedef struct ExprEvalStep
            struct ScalarArrayOpExprHashTable *elements_tab;
            FmgrInfo   *finfo;  /* function's lookup data */
            FunctionCallInfo fcinfo_data;   /* arguments etc */
-           /* faster to access without additional indirection: */
-           PGFunction  fn_addr;    /* actual call address */
-           FmgrInfo   *hash_finfo; /* function's lookup data */
-           FunctionCallInfo hash_fcinfo_data;  /* arguments etc */
-           /* faster to access without additional indirection: */
-           PGFunction  hash_fn_addr;   /* actual call address */
+           ScalarArrayOpExpr *saop;
        }           hashedscalararrayop;
 
        /* for EEOP_XMLEXPR */