Tweak a few of the most heavily used function call points to zero out
authorTom Lane
Fri, 4 Oct 2002 17:19:55 +0000 (17:19 +0000)
committerTom Lane
Fri, 4 Oct 2002 17:19:55 +0000 (17:19 +0000)
just the significant fields of FunctionCallInfoData, rather than MemSet'ing
the whole struct to zero.  Unused positions in the arg[] array will
thereby contain garbage rather than zeroes.  This buys back some of the
performance hit from increasing FUNC_MAX_ARGS.  Also tweak tuplesort.c
code for more speed by marking some routines 'inline'.  All together
these changes speed up simple sorts, like count(distinct int4column),
by about 25% on a P4 running RH Linux 7.2.

src/backend/executor/nodeAgg.c
src/backend/utils/fmgr/fmgr.c
src/backend/utils/sort/tuplesort.c

index 0ebf2f7151e5050a4e49c164df14fa7e8151f3c6..36ded53df0ca5dac1118db23dd8b754264693bc8 100644 (file)
@@ -46,7 +46,7 @@
  * Portions Copyright (c) 1994, Regents of the University of California
  *
  * IDENTIFICATION
- *   $Header: /cvsroot/pgsql/src/backend/executor/nodeAgg.c,v 1.88 2002/09/28 20:00:19 tgl Exp $
+ *   $Header: /cvsroot/pgsql/src/backend/executor/nodeAgg.c,v 1.89 2002/10/04 17:19:55 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -275,8 +275,18 @@ advance_transition_function(AggStatePerAgg peraggstate,
        }
    }
 
-   /* OK to call the transition function */
-   MemSet(&fcinfo, 0, sizeof(fcinfo));
+   /*
+    * OK to call the transition function
+    *
+    * This is heavily-used code, so manually zero just the necessary fields
+    * instead of using MemSet().  Compare FunctionCall2().
+    */
+
+   /* MemSet(&fcinfo, 0, sizeof(fcinfo)); */
+   fcinfo.context = NULL;
+   fcinfo.resultinfo = NULL;
+   fcinfo.isnull = false;
+
    fcinfo.flinfo = &peraggstate->transfn;
    fcinfo.nargs = 2;
    fcinfo.arg[0] = peraggstate->transValue;
index c0cef75e8c01bbeb0c3f5b9e5f01df060261f836..470d4a11dd6ef481e7ade6bf3be2b671835ad75d 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *   $Header: /cvsroot/pgsql/src/backend/utils/fmgr/fmgr.c,v 1.62 2002/09/04 20:31:30 momjian Exp $
+ *   $Header: /cvsroot/pgsql/src/backend/utils/fmgr/fmgr.c,v 1.63 2002/10/04 17:19:55 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -689,6 +689,14 @@ fmgr_security_definer(PG_FUNCTION_ARGS)
 
 /*-------------------------------------------------------------------------
  *     Support routines for callers of fmgr-compatible functions
+ *
+ * NOTE: the simplest way to reliably initialize a FunctionCallInfoData
+ * is to MemSet it to zeroes and then fill in the fields that should be
+ * nonzero.  However, in a few of the most heavily used paths, we instead
+ * just zero the fields that must be zero.  This saves a fair number of
+ * cycles so it's worth the extra maintenance effort.  Also see inlined
+ * version of FunctionCall2 in utils/sort/tuplesort.c if you need to change
+ * these routines!
  *-------------------------------------------------------------------------
  */
 
@@ -703,9 +711,15 @@ DirectFunctionCall1(PGFunction func, Datum arg1)
    FunctionCallInfoData fcinfo;
    Datum       result;
 
-   MemSet(&fcinfo, 0, sizeof(fcinfo));
+   /* MemSet(&fcinfo, 0, sizeof(fcinfo)); */
+   fcinfo.flinfo = NULL;
+   fcinfo.context = NULL;
+   fcinfo.resultinfo = NULL;
+   fcinfo.isnull = false;
+
    fcinfo.nargs = 1;
    fcinfo.arg[0] = arg1;
+   fcinfo.argnull[0] = false;
 
    result = (*func) (&fcinfo);
 
@@ -723,10 +737,17 @@ DirectFunctionCall2(PGFunction func, Datum arg1, Datum arg2)
    FunctionCallInfoData fcinfo;
    Datum       result;
 
-   MemSet(&fcinfo, 0, sizeof(fcinfo));
+   /* MemSet(&fcinfo, 0, sizeof(fcinfo)); */
+   fcinfo.flinfo = NULL;
+   fcinfo.context = NULL;
+   fcinfo.resultinfo = NULL;
+   fcinfo.isnull = false;
+
    fcinfo.nargs = 2;
    fcinfo.arg[0] = arg1;
    fcinfo.arg[1] = arg2;
+   fcinfo.argnull[0] = false;
+   fcinfo.argnull[1] = false;
 
    result = (*func) (&fcinfo);
 
@@ -936,10 +957,15 @@ FunctionCall1(FmgrInfo *flinfo, Datum arg1)
    FunctionCallInfoData fcinfo;
    Datum       result;
 
-   MemSet(&fcinfo, 0, sizeof(fcinfo));
+   /* MemSet(&fcinfo, 0, sizeof(fcinfo)); */
+   fcinfo.context = NULL;
+   fcinfo.resultinfo = NULL;
+   fcinfo.isnull = false;
+
    fcinfo.flinfo = flinfo;
    fcinfo.nargs = 1;
    fcinfo.arg[0] = arg1;
+   fcinfo.argnull[0] = false;
 
    result = FunctionCallInvoke(&fcinfo);
 
@@ -957,11 +983,17 @@ FunctionCall2(FmgrInfo *flinfo, Datum arg1, Datum arg2)
    FunctionCallInfoData fcinfo;
    Datum       result;
 
-   MemSet(&fcinfo, 0, sizeof(fcinfo));
+   /* MemSet(&fcinfo, 0, sizeof(fcinfo)); */
+   fcinfo.context = NULL;
+   fcinfo.resultinfo = NULL;
+   fcinfo.isnull = false;
+
    fcinfo.flinfo = flinfo;
    fcinfo.nargs = 2;
    fcinfo.arg[0] = arg1;
    fcinfo.arg[1] = arg2;
+   fcinfo.argnull[0] = false;
+   fcinfo.argnull[1] = false;
 
    result = FunctionCallInvoke(&fcinfo);
 
index 6dfbd95bf100fe14141d27dcb59d8aab47806214..38e73f8a63e722db7ac59b6d9708717df017a713 100644 (file)
@@ -78,7 +78,7 @@
  * Portions Copyright (c) 1994, Regents of the University of California
  *
  * IDENTIFICATION
- *   $Header: /cvsroot/pgsql/src/backend/utils/sort/tuplesort.c,v 1.27 2002/09/04 20:31:33 momjian Exp $
+ *   $Header: /cvsroot/pgsql/src/backend/utils/sort/tuplesort.c,v 1.28 2002/10/04 17:19:55 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -1694,6 +1694,216 @@ qsort_comparetup(const void *a, const void *b)
 }
 
 
+/*
+ * This routine selects an appropriate sorting function to implement
+ * a sort operator as efficiently as possible. The straightforward
+ * method is to use the operator's implementation proc --- ie, "<"
+ * comparison. However, that way often requires two calls of the function
+ * per comparison. If we can find a btree three-way comparator function
+ * associated with the operator, we can use it to do the comparisons
+ * more efficiently.  We also support the possibility that the operator
+ * is ">" (descending sort), in which case we have to reverse the output
+ * of the btree comparator.
+ *
+ * Possibly this should live somewhere else (backend/catalog/, maybe?).
+ */
+void
+SelectSortFunction(Oid sortOperator,
+                  RegProcedure *sortFunction,
+                  SortFunctionKind *kind)
+{
+   Relation    relation;
+   HeapScanDesc scan;
+   ScanKeyData skey[1];
+   HeapTuple   tuple;
+   Form_pg_operator optup;
+   Oid         opclass = InvalidOid;
+
+   /*
+    * Scan pg_amop to see if the target operator is registered as the "<"
+    * or ">" operator of any btree opclass.  It's possible that it might
+    * be registered both ways (eg, if someone were to build a "reverse
+    * sort" opclass for some reason); prefer the "<" case if so. If the
+    * operator is registered the same way in multiple opclasses, assume
+    * we can use the associated comparator function from any one.
+    */
+   ScanKeyEntryInitialize(&skey[0], 0x0,
+                          Anum_pg_amop_amopopr,
+                          F_OIDEQ,
+                          ObjectIdGetDatum(sortOperator));
+
+   relation = heap_openr(AccessMethodOperatorRelationName, AccessShareLock);
+   scan = heap_beginscan(relation, SnapshotNow, 1, skey);
+
+   while ((tuple = heap_getnext(scan, ForwardScanDirection)) != NULL)
+   {
+       Form_pg_amop aform = (Form_pg_amop) GETSTRUCT(tuple);
+
+       if (!opclass_is_btree(aform->amopclaid))
+           continue;
+       if (aform->amopstrategy == BTLessStrategyNumber)
+       {
+           opclass = aform->amopclaid;
+           *kind = SORTFUNC_CMP;
+           break;              /* done looking */
+       }
+       else if (aform->amopstrategy == BTGreaterStrategyNumber)
+       {
+           opclass = aform->amopclaid;
+           *kind = SORTFUNC_REVCMP;
+           /* keep scanning in hopes of finding a BTLess entry */
+       }
+   }
+
+   heap_endscan(scan);
+   heap_close(relation, AccessShareLock);
+
+   if (OidIsValid(opclass))
+   {
+       /* Found a suitable opclass, get its comparator support function */
+       tuple = SearchSysCache(AMPROCNUM,
+                              ObjectIdGetDatum(opclass),
+                              Int16GetDatum(BTORDER_PROC),
+                              0, 0);
+       if (HeapTupleIsValid(tuple))
+       {
+           Form_pg_amproc aform = (Form_pg_amproc) GETSTRUCT(tuple);
+
+           *sortFunction = aform->amproc;
+           ReleaseSysCache(tuple);
+           Assert(RegProcedureIsValid(*sortFunction));
+           return;
+       }
+   }
+
+   /*
+    * Can't find a comparator, so use the operator as-is.  Decide whether
+    * it is forward or reverse sort by looking at its name (grotty, but
+    * this only matters for deciding which end NULLs should get sorted
+    * to).
+    */
+   tuple = SearchSysCache(OPEROID,
+                          ObjectIdGetDatum(sortOperator),
+                          0, 0, 0);
+   if (!HeapTupleIsValid(tuple))
+       elog(ERROR, "SelectSortFunction: cache lookup failed for operator %u",
+            sortOperator);
+   optup = (Form_pg_operator) GETSTRUCT(tuple);
+   if (strcmp(NameStr(optup->oprname), ">") == 0)
+       *kind = SORTFUNC_REVLT;
+   else
+       *kind = SORTFUNC_LT;
+   *sortFunction = optup->oprcode;
+   ReleaseSysCache(tuple);
+
+   Assert(RegProcedureIsValid(*sortFunction));
+}
+
+/*
+ * Inline-able copy of FunctionCall2() to save some cycles in sorting.
+ */
+static inline Datum
+myFunctionCall2(FmgrInfo *flinfo, Datum arg1, Datum arg2)
+{
+   FunctionCallInfoData fcinfo;
+   Datum       result;
+
+   /* MemSet(&fcinfo, 0, sizeof(fcinfo)); */
+   fcinfo.context = NULL;
+   fcinfo.resultinfo = NULL;
+   fcinfo.isnull = false;
+
+   fcinfo.flinfo = flinfo;
+   fcinfo.nargs = 2;
+   fcinfo.arg[0] = arg1;
+   fcinfo.arg[1] = arg2;
+   fcinfo.argnull[0] = false;
+   fcinfo.argnull[1] = false;
+
+   result = FunctionCallInvoke(&fcinfo);
+
+   /* Check for null result, since caller is clearly not expecting one */
+   if (fcinfo.isnull)
+       elog(ERROR, "FunctionCall2: function %u returned NULL",
+            fcinfo.flinfo->fn_oid);
+
+   return result;
+}
+
+/*
+ * Apply a sort function (by now converted to fmgr lookup form)
+ * and return a 3-way comparison result.  This takes care of handling
+ * NULLs and sort ordering direction properly.
+ */
+inline int32
+ApplySortFunction(FmgrInfo *sortFunction, SortFunctionKind kind,
+                 Datum datum1, bool isNull1,
+                 Datum datum2, bool isNull2)
+{
+   switch (kind)
+   {
+       case SORTFUNC_LT:
+           if (isNull1)
+           {
+               if (isNull2)
+                   return 0;
+               return 1;       /* NULL sorts after non-NULL */
+           }
+           if (isNull2)
+               return -1;
+           if (DatumGetBool(myFunctionCall2(sortFunction, datum1, datum2)))
+               return -1;      /* a < b */
+           if (DatumGetBool(myFunctionCall2(sortFunction, datum2, datum1)))
+               return 1;       /* a > b */
+           return 0;
+
+       case SORTFUNC_REVLT:
+           /* We reverse the ordering of NULLs, but not the operator */
+           if (isNull1)
+           {
+               if (isNull2)
+                   return 0;
+               return -1;      /* NULL sorts before non-NULL */
+           }
+           if (isNull2)
+               return 1;
+           if (DatumGetBool(myFunctionCall2(sortFunction, datum1, datum2)))
+               return -1;      /* a < b */
+           if (DatumGetBool(myFunctionCall2(sortFunction, datum2, datum1)))
+               return 1;       /* a > b */
+           return 0;
+
+       case SORTFUNC_CMP:
+           if (isNull1)
+           {
+               if (isNull2)
+                   return 0;
+               return 1;       /* NULL sorts after non-NULL */
+           }
+           if (isNull2)
+               return -1;
+           return DatumGetInt32(myFunctionCall2(sortFunction,
+                                                datum1, datum2));
+
+       case SORTFUNC_REVCMP:
+           if (isNull1)
+           {
+               if (isNull2)
+                   return 0;
+               return -1;      /* NULL sorts before non-NULL */
+           }
+           if (isNull2)
+               return 1;
+           return -DatumGetInt32(myFunctionCall2(sortFunction,
+                                                 datum1, datum2));
+
+       default:
+           elog(ERROR, "Invalid SortFunctionKind %d", (int) kind);
+           return 0;           /* can't get here, but keep compiler quiet */
+   }
+}
+
+
 /*
  * Routines specialized for HeapTuple case
  */
@@ -1991,182 +2201,3 @@ readtup_datum(Tuplesortstate *state, int tapenum, unsigned int len)
                                     MAXALIGN(sizeof(DatumTuple)));
    return (void *) tuple;
 }
-
-
-/*
- * This routine selects an appropriate sorting function to implement
- * a sort operator as efficiently as possible. The straightforward
- * method is to use the operator's implementation proc --- ie, "<"
- * comparison. However, that way often requires two calls of the function
- * per comparison. If we can find a btree three-way comparator function
- * associated with the operator, we can use it to do the comparisons
- * more efficiently.  We also support the possibility that the operator
- * is ">" (descending sort), in which case we have to reverse the output
- * of the btree comparator.
- *
- * Possibly this should live somewhere else (backend/catalog/, maybe?).
- */
-void
-SelectSortFunction(Oid sortOperator,
-                  RegProcedure *sortFunction,
-                  SortFunctionKind *kind)
-{
-   Relation    relation;
-   HeapScanDesc scan;
-   ScanKeyData skey[1];
-   HeapTuple   tuple;
-   Form_pg_operator optup;
-   Oid         opclass = InvalidOid;
-
-   /*
-    * Scan pg_amop to see if the target operator is registered as the "<"
-    * or ">" operator of any btree opclass.  It's possible that it might
-    * be registered both ways (eg, if someone were to build a "reverse
-    * sort" opclass for some reason); prefer the "<" case if so. If the
-    * operator is registered the same way in multiple opclasses, assume
-    * we can use the associated comparator function from any one.
-    */
-   ScanKeyEntryInitialize(&skey[0], 0x0,
-                          Anum_pg_amop_amopopr,
-                          F_OIDEQ,
-                          ObjectIdGetDatum(sortOperator));
-
-   relation = heap_openr(AccessMethodOperatorRelationName, AccessShareLock);
-   scan = heap_beginscan(relation, SnapshotNow, 1, skey);
-
-   while ((tuple = heap_getnext(scan, ForwardScanDirection)) != NULL)
-   {
-       Form_pg_amop aform = (Form_pg_amop) GETSTRUCT(tuple);
-
-       if (!opclass_is_btree(aform->amopclaid))
-           continue;
-       if (aform->amopstrategy == BTLessStrategyNumber)
-       {
-           opclass = aform->amopclaid;
-           *kind = SORTFUNC_CMP;
-           break;              /* done looking */
-       }
-       else if (aform->amopstrategy == BTGreaterStrategyNumber)
-       {
-           opclass = aform->amopclaid;
-           *kind = SORTFUNC_REVCMP;
-           /* keep scanning in hopes of finding a BTLess entry */
-       }
-   }
-
-   heap_endscan(scan);
-   heap_close(relation, AccessShareLock);
-
-   if (OidIsValid(opclass))
-   {
-       /* Found a suitable opclass, get its comparator support function */
-       tuple = SearchSysCache(AMPROCNUM,
-                              ObjectIdGetDatum(opclass),
-                              Int16GetDatum(BTORDER_PROC),
-                              0, 0);
-       if (HeapTupleIsValid(tuple))
-       {
-           Form_pg_amproc aform = (Form_pg_amproc) GETSTRUCT(tuple);
-
-           *sortFunction = aform->amproc;
-           ReleaseSysCache(tuple);
-           Assert(RegProcedureIsValid(*sortFunction));
-           return;
-       }
-   }
-
-   /*
-    * Can't find a comparator, so use the operator as-is.  Decide whether
-    * it is forward or reverse sort by looking at its name (grotty, but
-    * this only matters for deciding which end NULLs should get sorted
-    * to).
-    */
-   tuple = SearchSysCache(OPEROID,
-                          ObjectIdGetDatum(sortOperator),
-                          0, 0, 0);
-   if (!HeapTupleIsValid(tuple))
-       elog(ERROR, "SelectSortFunction: cache lookup failed for operator %u",
-            sortOperator);
-   optup = (Form_pg_operator) GETSTRUCT(tuple);
-   if (strcmp(NameStr(optup->oprname), ">") == 0)
-       *kind = SORTFUNC_REVLT;
-   else
-       *kind = SORTFUNC_LT;
-   *sortFunction = optup->oprcode;
-   ReleaseSysCache(tuple);
-
-   Assert(RegProcedureIsValid(*sortFunction));
-}
-
-/*
- * Apply a sort function (by now converted to fmgr lookup form)
- * and return a 3-way comparison result.  This takes care of handling
- * NULLs and sort ordering direction properly.
- */
-int32
-ApplySortFunction(FmgrInfo *sortFunction, SortFunctionKind kind,
-                 Datum datum1, bool isNull1,
-                 Datum datum2, bool isNull2)
-{
-   switch (kind)
-   {
-       case SORTFUNC_LT:
-           if (isNull1)
-           {
-               if (isNull2)
-                   return 0;
-               return 1;       /* NULL sorts after non-NULL */
-           }
-           if (isNull2)
-               return -1;
-           if (DatumGetBool(FunctionCall2(sortFunction, datum1, datum2)))
-               return -1;      /* a < b */
-           if (DatumGetBool(FunctionCall2(sortFunction, datum2, datum1)))
-               return 1;       /* a > b */
-           return 0;
-
-       case SORTFUNC_REVLT:
-           /* We reverse the ordering of NULLs, but not the operator */
-           if (isNull1)
-           {
-               if (isNull2)
-                   return 0;
-               return -1;      /* NULL sorts before non-NULL */
-           }
-           if (isNull2)
-               return 1;
-           if (DatumGetBool(FunctionCall2(sortFunction, datum1, datum2)))
-               return -1;      /* a < b */
-           if (DatumGetBool(FunctionCall2(sortFunction, datum2, datum1)))
-               return 1;       /* a > b */
-           return 0;
-
-       case SORTFUNC_CMP:
-           if (isNull1)
-           {
-               if (isNull2)
-                   return 0;
-               return 1;       /* NULL sorts after non-NULL */
-           }
-           if (isNull2)
-               return -1;
-           return DatumGetInt32(FunctionCall2(sortFunction,
-                                              datum1, datum2));
-
-       case SORTFUNC_REVCMP:
-           if (isNull1)
-           {
-               if (isNull2)
-                   return 0;
-               return -1;      /* NULL sorts before non-NULL */
-           }
-           if (isNull2)
-               return 1;
-           return -DatumGetInt32(FunctionCall2(sortFunction,
-                                               datum1, datum2));
-
-       default:
-           elog(ERROR, "Invalid SortFunctionKind %d", (int) kind);
-           return 0;           /* can't get here, but keep compiler quiet */
-   }
-}