Speedup WindowAgg code by moving uncommon code out-of-line
authorDavid Rowley <[email protected]>
Thu, 5 Sep 2024 03:59:47 +0000 (15:59 +1200)
committerDavid Rowley <[email protected]>
Thu, 5 Sep 2024 03:59:47 +0000 (15:59 +1200)
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 <[email protected]>
Reviewed-by: Tatsuo Ishii <[email protected]>
Discussion: https://postgr.es/m/CAApHDvqPgFtwme2Zyf75BpMLwYr2mnUstDyPiP%3DEpudYuQTPPQ%40mail.gmail.com

src/backend/executor/nodeWindowAgg.c

index 3221fa1522a3a2ae46e709193e13cbc3349dfcf0..88a85f556b62ffe9fa6a1aa963daf4dd9a8e86e8 100644 (file)
@@ -2032,6 +2032,82 @@ update_grouptailpos(WindowAggState *winstate)
    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
@@ -2061,68 +2137,8 @@ ExecWindowAgg(PlanState *pstate)
     * rescan).  These are assumed to hold constant throughout the scan; if
     * user gives us a volatile expression, we'll only use its initial value.
     */
-   if (winstate->all_first)
-   {
-       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;
-   }
+   if (unlikely(winstate->all_first))
+       calculate_frame_offsets(pstate);
 
    /* We need to loop as the runCondition or qual may filter out tuples */
    for (;;)