]> granicus.if.org Git - postgresql/commitdiff
Improve performance of SendRowDescriptionMessage.
authorAndres Freund <andres@anarazel.de>
Wed, 11 Oct 2017 23:49:31 +0000 (16:49 -0700)
committerAndres Freund <andres@anarazel.de>
Thu, 12 Oct 2017 00:23:23 +0000 (17:23 -0700)
There's three categories of changes leading to better performance:
- Splitting the per-attribute part of SendRowDescriptionMessage into a
  v2 and a v3 version allows avoiding branches for every attribute.
- Preallocating the size of the buffer to be big enough for all
  attributes and then using pq_write* avoids unnecessary buffer
  size checks & resizing.
- Reusing a persistently allocated StringInfo for all
  SendRowDescriptionMessage() invocations avoids repeated allocations
  & reallocations.

Author: Andres Freund
Discussion: https://postgr.es/m/20170914063418.sckdzgjfrsbekae4@alap3.anarazel.de

src/backend/access/common/printtup.c
src/backend/tcop/postgres.c
src/include/access/printtup.h

index c00b372b849ad7eb355afc1e675408714922c31a..02cd1beef79b774326c26fe0e62919e414205583 100644 (file)
@@ -32,6 +32,10 @@ static bool printtup_internal_20(TupleTableSlot *slot, DestReceiver *self);
 static void printtup_shutdown(DestReceiver *self);
 static void printtup_destroy(DestReceiver *self);
 
+static void SendRowDescriptionCols_2(StringInfo buf, TupleDesc typeinfo,
+                                                List *targetlist, int16 *formats);
+static void SendRowDescriptionCols_3(StringInfo buf, TupleDesc typeinfo,
+                                                List *targetlist, int16 *formats);
 
 /* ----------------------------------------------------------------
  *             printtup / debugtup support
@@ -161,7 +165,8 @@ printtup_startup(DestReceiver *self, int operation, TupleDesc typeinfo)
         * descriptor of the tuples.
         */
        if (myState->sendDescrip)
-               SendRowDescriptionMessage(typeinfo,
+               SendRowDescriptionMessage(&myState->buf,
+                                                                 typeinfo,
                                                                  FetchPortalTargetList(portal),
                                                                  portal->formats);
 
@@ -189,61 +194,126 @@ printtup_startup(DestReceiver *self, int operation, TupleDesc typeinfo)
  * send zeroes for the format codes in that case.
  */
 void
-SendRowDescriptionMessage(TupleDesc typeinfo, List *targetlist, int16 *formats)
+SendRowDescriptionMessage(StringInfo buf, TupleDesc typeinfo,
+                                                 List *targetlist, int16 *formats)
 {
        int                     natts = typeinfo->natts;
        int                     proto = PG_PROTOCOL_MAJOR(FrontendProtocol);
+
+       /* tuple descriptor message type */
+       pq_beginmessage_reuse(buf, 'T');
+       /* # of attrs in tuples */
+       pq_sendint16(buf, natts);
+
+       if (proto >= 3)
+               SendRowDescriptionCols_3(buf, typeinfo, targetlist, formats);
+       else
+               SendRowDescriptionCols_2(buf, typeinfo, targetlist, formats);
+
+       pq_endmessage_reuse(buf);
+}
+
+/*
+ * Send description for each column when using v3+ protocol
+ */
+static void
+SendRowDescriptionCols_3(StringInfo buf, TupleDesc typeinfo, List *targetlist, int16 *formats)
+{
+       int                     natts = typeinfo->natts;
        int                     i;
-       StringInfoData buf;
        ListCell   *tlist_item = list_head(targetlist);
 
-       pq_beginmessage(&buf, 'T'); /* tuple descriptor message type */
-       pq_sendint(&buf, natts, 2); /* # of attrs in tuples */
+       /*
+        * Preallocate memory for the entire message to be sent. That allows to
+        * use the significantly faster inline pqformat.h functions and to avoid
+        * reallocations.
+        *
+        * Have to overestimate the size of the column-names, to account for
+        * character set overhead.
+        */
+       enlargeStringInfo(buf, (NAMEDATALEN * MAX_CONVERSION_GROWTH /* attname */
+                                                       + sizeof(Oid)   /* resorigtbl */
+                                                       + sizeof(AttrNumber)    /* resorigcol */
+                                                       + sizeof(Oid)   /* atttypid */
+                                                       + sizeof(int16) /* attlen */
+                                                       + sizeof(int32) /* attypmod */
+                                                       + sizeof(int16) /* format */
+                                                       ) * natts);
 
        for (i = 0; i < natts; ++i)
        {
                Form_pg_attribute att = TupleDescAttr(typeinfo, i);
                Oid                     atttypid = att->atttypid;
                int32           atttypmod = att->atttypmod;
+               Oid                     resorigtbl;
+               AttrNumber      resorigcol;
+               int16           format;
+
+               /*
+                * If column is a domain, send the base type and typmod instead.
+                * Lookup before sending any ints, for efficiency.
+                */
+               atttypid = getBaseTypeAndTypmod(atttypid, &atttypmod);
 
-               pq_sendstring(&buf, NameStr(att->attname));
-               /* column ID info appears in protocol 3.0 and up */
-               if (proto >= 3)
+               /* Do we have a non-resjunk tlist item? */
+               while (tlist_item &&
+                          ((TargetEntry *) lfirst(tlist_item))->resjunk)
+                       tlist_item = lnext(tlist_item);
+               if (tlist_item)
                {
-                       /* Do we have a non-resjunk tlist item? */
-                       while (tlist_item &&
-                                  ((TargetEntry *) lfirst(tlist_item))->resjunk)
-                               tlist_item = lnext(tlist_item);
-                       if (tlist_item)
-                       {
-                               TargetEntry *tle = (TargetEntry *) lfirst(tlist_item);
-
-                               pq_sendint(&buf, tle->resorigtbl, 4);
-                               pq_sendint(&buf, tle->resorigcol, 2);
-                               tlist_item = lnext(tlist_item);
-                       }
-                       else
-                       {
-                               /* No info available, so send zeroes */
-                               pq_sendint(&buf, 0, 4);
-                               pq_sendint(&buf, 0, 2);
-                       }
+                       TargetEntry *tle = (TargetEntry *) lfirst(tlist_item);
+
+                       resorigtbl = tle->resorigtbl;
+                       resorigcol = tle->resorigcol;
+                       tlist_item = lnext(tlist_item);
                }
-               /* If column is a domain, send the base type and typmod instead */
-               atttypid = getBaseTypeAndTypmod(atttypid, &atttypmod);
-               pq_sendint(&buf, (int) atttypid, sizeof(atttypid));
-               pq_sendint(&buf, att->attlen, sizeof(att->attlen));
-               pq_sendint(&buf, atttypmod, sizeof(atttypmod));
-               /* format info appears in protocol 3.0 and up */
-               if (proto >= 3)
+               else
                {
-                       if (formats)
-                               pq_sendint(&buf, formats[i], 2);
-                       else
-                               pq_sendint(&buf, 0, 2);
+                       /* No info available, so send zeroes */
+                       resorigtbl = 0;
+                       resorigcol = 0;
                }
+
+               if (formats)
+                       format = formats[i];
+               else
+                       format = 0;
+
+               pq_writestring(buf, NameStr(att->attname));
+               pq_writeint32(buf, resorigtbl);
+               pq_writeint16(buf, resorigcol);
+               pq_writeint32(buf, atttypid);
+               pq_writeint16(buf, att->attlen);
+               pq_writeint32(buf, atttypmod);
+               pq_writeint16(buf, format);
+       }
+}
+
+/*
+ * Send description for each column when using v2 protocol
+ */
+static void
+SendRowDescriptionCols_2(StringInfo buf, TupleDesc typeinfo, List *targetlist, int16 *formats)
+{
+       int                     natts = typeinfo->natts;
+       int                     i;
+
+       for (i = 0; i < natts; ++i)
+       {
+               Form_pg_attribute att = TupleDescAttr(typeinfo, i);
+               Oid                     atttypid = att->atttypid;
+               int32           atttypmod = att->atttypmod;
+
+               /* If column is a domain, send the base type and typmod instead */
+               atttypid = getBaseTypeAndTypmod(atttypid, &atttypmod);
+
+               pq_sendstring(buf, NameStr(att->attname));
+               /* column ID only info appears in protocol 3.0 and up */
+               pq_sendint32(buf, atttypid);
+               pq_sendint16(buf, att->attlen);
+               pq_sendint32(buf, atttypmod);
+               /* format info only appears in protocol 3.0 and up */
        }
-       pq_endmessage(&buf);
 }
 
 /*
index edea6f177bb1e0d1c17c0d77e0dad2a7dc3e5b16..338ce81331d88657ef1d19a62bdccecbbd6dad23 100644 (file)
@@ -165,6 +165,10 @@ static bool RecoveryConflictPending = false;
 static bool RecoveryConflictRetryable = true;
 static ProcSignalReason RecoveryConflictReason;
 
+/* reused buffer to pass to SendRowDescriptionMessage() */
+static MemoryContext row_description_context = NULL;
+static StringInfoData row_description_buf;
+
 /* ----------------------------------------------------------------
  *             decls for routines only used in this file
  * ----------------------------------------------------------------
@@ -2315,7 +2319,6 @@ static void
 exec_describe_statement_message(const char *stmt_name)
 {
        CachedPlanSource *psrc;
-       StringInfoData buf;
        int                     i;
 
        /*
@@ -2371,16 +2374,17 @@ exec_describe_statement_message(const char *stmt_name)
        /*
         * First describe the parameters...
         */
-       pq_beginmessage(&buf, 't'); /* parameter description message type */
-       pq_sendint(&buf, psrc->num_params, 2);
+       pq_beginmessage_reuse(&row_description_buf, 't'); /* parameter description
+                                                                                                          * message type */
+       pq_sendint(&row_description_buf, psrc->num_params, 2);
 
        for (i = 0; i < psrc->num_params; i++)
        {
                Oid                     ptype = psrc->param_types[i];
 
-               pq_sendint(&buf, (int) ptype, 4);
+               pq_sendint(&row_description_buf, (int) ptype, 4);
        }
-       pq_endmessage(&buf);
+       pq_endmessage_reuse(&row_description_buf);
 
        /*
         * Next send RowDescription or NoData to describe the result...
@@ -2392,7 +2396,10 @@ exec_describe_statement_message(const char *stmt_name)
                /* Get the plan's primary targetlist */
                tlist = CachedPlanGetTargetList(psrc, NULL);
 
-               SendRowDescriptionMessage(psrc->resultDesc, tlist, NULL);
+               SendRowDescriptionMessage(&row_description_buf,
+                                                                 psrc->resultDesc,
+                                                                 tlist,
+                                                                 NULL);
        }
        else
                pq_putemptymessage('n');        /* NoData */
@@ -2444,7 +2451,8 @@ exec_describe_portal_message(const char *portal_name)
                return;                                 /* can't actually do anything... */
 
        if (portal->tupDesc)
-               SendRowDescriptionMessage(portal->tupDesc,
+               SendRowDescriptionMessage(&row_description_buf,
+                                                                 portal->tupDesc,
                                                                  FetchPortalTargetList(portal),
                                                                  portal->formats);
        else
@@ -3830,6 +3838,19 @@ PostgresMain(int argc, char *argv[],
                                                                                   "MessageContext",
                                                                                   ALLOCSET_DEFAULT_SIZES);
 
+       /*
+        * Create memory context and buffer used for RowDescription messages. As
+        * SendRowDescriptionMessage(), via exec_describe_statement_message(), is
+        * frequently executed for ever single statement, we don't want to
+        * allocate a separate buffer every time.
+        */
+       row_description_context = AllocSetContextCreate(TopMemoryContext,
+                                                                                                       "RowDescriptionContext",
+                                                                                                       ALLOCSET_DEFAULT_SIZES);
+       MemoryContextSwitchTo(row_description_context);
+       initStringInfo(&row_description_buf);
+       MemoryContextSwitchTo(TopMemoryContext);
+
        /*
         * Remember stand-alone backend startup time
         */
index 641715e4165e911928dc8b9aeed506dd0d37d1e5..1b5a003a99216e367fa93a89e1716cf9283cf3a3 100644 (file)
@@ -20,8 +20,8 @@ extern DestReceiver *printtup_create_DR(CommandDest dest);
 
 extern void SetRemoteDestReceiverParams(DestReceiver *self, Portal portal);
 
-extern void SendRowDescriptionMessage(TupleDesc typeinfo, List *targetlist,
-                                                 int16 *formats);
+extern void SendRowDescriptionMessage(StringInfo buf,
+                                                 TupleDesc typeinfo, List *targetlist, int16 *formats);
 
 extern void debugStartup(DestReceiver *self, int operation,
                         TupleDesc typeinfo);