While doing some benchmarking of some fast-to-execute queries, I see
that set_ps_display() popping up on the profiles.  Looking a little
deeper, there are some inefficiencies in there that we could fix.

For example, the following is pretty poor:

strlcpy(ps_buffer + ps_buffer_fixed_size, activity,
        ps_buffer_size - ps_buffer_fixed_size);
ps_buffer_cur_len = strlen(ps_buffer);

We already know the strlen of the fixed-sized part, so why bother
doing strlen on the entire thing?  Also, if we did just do
strlen(activity), we could just memcpy, which would be much faster
than strlcpy's byte-at-a-time method of copying.

Adjusting that lead me to notice that we often just pass string
constants to set_ps_display(), so we already know the strlen for this
at compile time. So maybe we can just have set_ps_display_with_len()
and then make a static inline wrapper that does strlen() so that when
the compiler can figure out the length, it just hard codes it.

After doing that, I went over all usages of set_ps_display() to see if
any of those call sites knew the length already in a way that the
compiler wouldn't be able to deduce. There were a few cases to adjust
when setting the process title to contain the command tag.

After fixing up the set_ps_display()s to use set_ps_display_with_len()
where possible, I discovered some not so nice code which appends "
waiting" onto the process title. Basically, there's a bunch of code
that looks like this:

const char *old_status;
int len;

old_status = get_ps_display(&len);
new_status = (char *) palloc(len + 8 + 1);
memcpy(new_status, old_status, len);
strcpy(new_status + len, " waiting");
set_ps_display(new_status);
new_status[len] = '\0'; /* truncate off " waiting" */

Seeing that made me wonder if we shouldn't just have something more
generic for setting a suffix on the process title.  I came up with
set_ps_display_suffix() and set_ps_display_remove_suffix().  The above
code can just become:

set_ps_display_suffix("waiting");

then to remove the "waiting" suffix, just:

set_ps_display_remove_suffix();

I considered adding a format version to append the suffix as there's
one case that could make use of it, but in the end, decided it might
be overkill, so I left that code like:

char buffer[32];

sprintf(buffer, "waiting for %X/%X", LSN_FORMAT_ARGS(lsn));
set_ps_display_suffix(buffer);

I don't think that's terrible enough to warrant making a va_args
version of set_ps_display_suffix(), especially for just 1 instance of
it.

I also resisted making set_ps_display_suffix_with_len(). The new code
should be quite a bit
faster already without troubling over that additional function.

I've attached the patch.

David
diff --git a/src/backend/replication/syncrep.c 
b/src/backend/replication/syncrep.c
index 80d681b71c..889e20b5dd 100644
--- a/src/backend/replication/syncrep.c
+++ b/src/backend/replication/syncrep.c
@@ -148,8 +148,6 @@ static bool SyncRepQueueIsOrderedByLSN(int mode);
 void
 SyncRepWaitForLSN(XLogRecPtr lsn, bool commit)
 {
-       char       *new_status = NULL;
-       const char *old_status;
        int                     mode;
 
        /*
@@ -216,15 +214,10 @@ SyncRepWaitForLSN(XLogRecPtr lsn, bool commit)
        /* Alter ps display to show waiting for sync rep. */
        if (update_process_title)
        {
-               int                     len;
-
-               old_status = get_ps_display(&len);
-               new_status = (char *) palloc(len + 32 + 1);
-               memcpy(new_status, old_status, len);
-               sprintf(new_status + len, " waiting for %X/%X",
-                               LSN_FORMAT_ARGS(lsn));
-               set_ps_display(new_status);
-               new_status[len] = '\0'; /* truncate off " waiting ..." */
+               char            buffer[32];
+
+               sprintf(buffer, "waiting for %X/%X", LSN_FORMAT_ARGS(lsn));
+               set_ps_display_suffix(buffer);
        }
 
        /*
@@ -322,12 +315,9 @@ SyncRepWaitForLSN(XLogRecPtr lsn, bool commit)
        MyProc->syncRepState = SYNC_REP_NOT_WAITING;
        MyProc->waitLSN = 0;
 
-       if (new_status)
-       {
-               /* Reset ps display */
-               set_ps_display(new_status);
-               pfree(new_status);
-       }
+       /* reset ps display to remove the suffix */
+       if (update_process_title)
+               set_ps_display_remove_suffix();
 }
 
 /*
diff --git a/src/backend/storage/buffer/bufmgr.c 
b/src/backend/storage/buffer/bufmgr.c
index 2d6dbc6561..98904a7c05 100644
--- a/src/backend/storage/buffer/bufmgr.c
+++ b/src/backend/storage/buffer/bufmgr.c
@@ -4302,8 +4302,8 @@ void
 LockBufferForCleanup(Buffer buffer)
 {
        BufferDesc *bufHdr;
-       char       *new_status = NULL;
        TimestampTz waitStart = 0;
+       bool            waiting = false;
        bool            logged_recovery_conflict = false;
 
        Assert(BufferIsPinned(buffer));
@@ -4350,11 +4350,11 @@ LockBufferForCleanup(Buffer buffer)
                                                                        
waitStart, GetCurrentTimestamp(),
                                                                        NULL, 
false);
 
-                       /* Report change to non-waiting status */
-                       if (new_status)
+                       if (waiting)
                        {
-                               set_ps_display(new_status);
-                               pfree(new_status);
+                               /* reset ps display to remove the suffix if we 
added one */
+                               set_ps_display_remove_suffix();
+                               waiting = false;
                        }
                        return;
                }
@@ -4374,18 +4374,11 @@ LockBufferForCleanup(Buffer buffer)
                /* Wait to be signaled by UnpinBuffer() */
                if (InHotStandby)
                {
-                       /* Report change to waiting status */
-                       if (update_process_title && new_status == NULL)
+                       if (!waiting)
                        {
-                               const char *old_status;
-                               int                     len;
-
-                               old_status = get_ps_display(&len);
-                               new_status = (char *) palloc(len + 8 + 1);
-                               memcpy(new_status, old_status, len);
-                               strcpy(new_status + len, " waiting");
-                               set_ps_display(new_status);
-                               new_status[len] = '\0'; /* truncate off " 
waiting" */
+                               /* adjust the process title to indicate that 
it's waiting */
+                               set_ps_display_suffix("waiting");
+                               waiting = true;
                        }
 
                        /*
diff --git a/src/backend/storage/ipc/standby.c 
b/src/backend/storage/ipc/standby.c
index 94cc860f5f..9a73ae67d0 100644
--- a/src/backend/storage/ipc/standby.c
+++ b/src/backend/storage/ipc/standby.c
@@ -362,7 +362,7 @@ ResolveRecoveryConflictWithVirtualXIDs(VirtualTransactionId 
*waitlist,
                                                                           bool 
report_waiting)
 {
        TimestampTz waitStart = 0;
-       char       *new_status = NULL;
+       bool            waiting = false;
        bool            logged_recovery_conflict = false;
 
        /* Fast exit, to avoid a kernel call if there's no work to be done. */
@@ -400,14 +400,14 @@ 
ResolveRecoveryConflictWithVirtualXIDs(VirtualTransactionId *waitlist,
                                        pg_usleep(5000L);
                        }
 
-                       if (waitStart != 0 && (!logged_recovery_conflict || 
new_status == NULL))
+                       if (waitStart != 0 && (!logged_recovery_conflict || 
!waiting))
                        {
                                TimestampTz now = 0;
                                bool            maybe_log_conflict;
                                bool            maybe_update_title;
 
                                maybe_log_conflict = 
(log_recovery_conflict_waits && !logged_recovery_conflict);
-                               maybe_update_title = (update_process_title && 
new_status == NULL);
+                               maybe_update_title = (update_process_title && 
!waiting);
 
                                /* Get the current timestamp if not report yet 
*/
                                if (maybe_log_conflict || maybe_update_title)
@@ -420,15 +420,8 @@ 
ResolveRecoveryConflictWithVirtualXIDs(VirtualTransactionId *waitlist,
                                if (maybe_update_title &&
                                        TimestampDifferenceExceeds(waitStart, 
now, 500))
                                {
-                                       const char *old_status;
-                                       int                     len;
-
-                                       old_status = get_ps_display(&len);
-                                       new_status = (char *) palloc(len + 8 + 
1);
-                                       memcpy(new_status, old_status, len);
-                                       strcpy(new_status + len, " waiting");
-                                       set_ps_display(new_status);
-                                       new_status[len] = '\0'; /* truncate off 
" waiting" */
+                                       set_ps_display_suffix("waiting");
+                                       waiting = true;
                                }
 
                                /*
@@ -456,12 +449,10 @@ 
ResolveRecoveryConflictWithVirtualXIDs(VirtualTransactionId *waitlist,
                LogRecoveryConflict(reason, waitStart, GetCurrentTimestamp(),
                                                        NULL, false);
 
-       /* Reset ps display if we changed it */
-       if (new_status)
-       {
-               set_ps_display(new_status);
-               pfree(new_status);
-       }
+       /* reset ps display to remove the suffix if we added one */
+       if (waiting)
+               set_ps_display_remove_suffix();
+
 }
 
 /*
diff --git a/src/backend/storage/lmgr/lock.c b/src/backend/storage/lmgr/lock.c
index a87372f33f..af1b0146a9 100644
--- a/src/backend/storage/lmgr/lock.c
+++ b/src/backend/storage/lmgr/lock.c
@@ -1810,24 +1810,12 @@ WaitOnLock(LOCALLOCK *locallock, ResourceOwner owner)
 {
        LOCKMETHODID lockmethodid = LOCALLOCK_LOCKMETHOD(*locallock);
        LockMethod      lockMethodTable = LockMethods[lockmethodid];
-       char       *volatile new_status = NULL;
 
        LOCK_PRINT("WaitOnLock: sleeping on lock",
                           locallock->lock, locallock->tag.mode);
 
-       /* Report change to waiting status */
-       if (update_process_title)
-       {
-               const char *old_status;
-               int                     len;
-
-               old_status = get_ps_display(&len);
-               new_status = (char *) palloc(len + 8 + 1);
-               memcpy(new_status, old_status, len);
-               strcpy(new_status + len, " waiting");
-               set_ps_display(new_status);
-               new_status[len] = '\0'; /* truncate off " waiting" */
-       }
+       /* adjust the process title to indicate that it's waiting */
+       set_ps_display_suffix("waiting");
 
        awaitedLock = locallock;
        awaitedOwner = owner;
@@ -1874,12 +1862,8 @@ WaitOnLock(LOCALLOCK *locallock, ResourceOwner owner)
        {
                /* In this path, awaitedLock remains set until LockErrorCleanup 
*/
 
-               /* Report change to non-waiting status */
-               if (update_process_title)
-               {
-                       set_ps_display(new_status);
-                       pfree(new_status);
-               }
+               /* reset ps display to remove the suffix if we added one */
+               set_ps_display_remove_suffix();
 
                /* and propagate the error */
                PG_RE_THROW();
@@ -1888,12 +1872,8 @@ WaitOnLock(LOCALLOCK *locallock, ResourceOwner owner)
 
        awaitedLock = NULL;
 
-       /* Report change to non-waiting status */
-       if (update_process_title)
-       {
-               set_ps_display(new_status);
-               pfree(new_status);
-       }
+       /* reset ps display to remove the suffix if we added one */
+       set_ps_display_remove_suffix();
 
        LOCK_PRINT("WaitOnLock: wakeup on lock",
                           locallock->lock, locallock->tag.mode);
diff --git a/src/backend/tcop/postgres.c b/src/backend/tcop/postgres.c
index 5d439f2710..cab709b07b 100644
--- a/src/backend/tcop/postgres.c
+++ b/src/backend/tcop/postgres.c
@@ -1071,6 +1071,8 @@ exec_simple_query(const char *query_string)
                Portal          portal;
                DestReceiver *receiver;
                int16           format;
+               const char *cmdtagname;
+               size_t          cmdtaglen;
 
                pgstat_report_query_id(0, true);
 
@@ -1081,8 +1083,9 @@ exec_simple_query(const char *query_string)
                 * destination.
                 */
                commandTag = CreateCommandTag(parsetree->stmt);
+               cmdtagname = GetCommandTagNameAndLen(commandTag, &cmdtaglen);
 
-               set_ps_display(GetCommandTagName(commandTag));
+               set_ps_display_with_len(cmdtagname, cmdtaglen);
 
                BeginCommand(commandTag, dest);
 
@@ -2064,6 +2067,8 @@ exec_execute_message(const char *portal_name, long 
max_rows)
        char            msec_str[32];
        ParamsErrorCbData params_data;
        ErrorContextCallback params_errcxt;
+       const char *cmdtagname;
+       size_t          cmdtaglen;
 
        /* Adjust destination to tell printtup.c what to do */
        dest = whereToSendOutput;
@@ -2110,7 +2115,9 @@ exec_execute_message(const char *portal_name, long 
max_rows)
 
        pgstat_report_activity(STATE_RUNNING, sourceText);
 
-       set_ps_display(GetCommandTagName(portal->commandTag));
+       cmdtagname = GetCommandTagNameAndLen(portal->commandTag, &cmdtaglen);
+
+       set_ps_display_with_len(cmdtagname, cmdtaglen);
 
        if (save_log_statement_stats)
                ResetUsage();
diff --git a/src/backend/utils/misc/ps_status.c 
b/src/backend/utils/misc/ps_status.c
index 06bdc2afd1..45cf76b0cd 100644
--- a/src/backend/utils/misc/ps_status.c
+++ b/src/backend/utils/misc/ps_status.c
@@ -100,12 +100,19 @@ static size_t ps_buffer_cur_len;  /* nominal 
strlen(ps_buffer) */
 
 static size_t ps_buffer_fixed_size; /* size of the constant prefix */
 
+/*
+ * Length of ps_buffer before the suffix was appeneded to the end, or 0 if we
+ * didn't set a suffix.
+ */
+static size_t ps_buffer_nosuffix_len;
+
 #endif                                                 /* not PS_USE_NONE */
 
 /* save the original argv[] location here */
 static int     save_argc;
 static char **save_argv;
 
+static void set_ps_display_internal(void);
 
 /*
  * Call this early in startup to save the original argc/argv values.
@@ -332,15 +339,117 @@ init_ps_display(const char *fixed_part)
 #endif                                                 /* not PS_USE_NONE */
 }
 
+/*
+ * set_ps_display_suffix
+ *             Adjust the process title to append 'suffix' onto the end with a 
space
+ *             between it and the current process title.
+ */
+void
+set_ps_display_suffix(const char *suffix)
+{
+       size_t  len;
+
+#ifndef PS_USE_NONE
+       /* update_process_title=off disables updates */
+       if (!update_process_title)
+               return;
+
+       /* no ps display for stand-alone backend */
+       if (!IsUnderPostmaster)
+               return;
+
+#ifdef PS_USE_CLOBBER_ARGV
+       /* If ps_buffer is a pointer, it might still be null */
+       if (!ps_buffer)
+               return;
+#endif
+
+       if (ps_buffer_nosuffix_len > 0)
+               ps_buffer_cur_len = ps_buffer_nosuffix_len;
+       else
+               ps_buffer_nosuffix_len = ps_buffer_cur_len;
+
+       len = strlen(suffix);
+
+       /* check if we have enough space to append the suffix */
+       if (ps_buffer_cur_len + len + 1 >= ps_buffer_size)
+       {
+               /* not enough space.  Check the buffer isn't full already */
+               if (ps_buffer_cur_len < ps_buffer_size - 1)
+               {
+                       /* append a space before the suffix */
+                       ps_buffer[ps_buffer_cur_len++] = ' ';
+
+                       /* Not enough space.  Just add what we can */
+                       memcpy(ps_buffer + ps_buffer_cur_len, suffix,
+                                  ps_buffer_size - ps_buffer_cur_len - 1);
+                       ps_buffer[ps_buffer_size - 1] = '\0';
+                       ps_buffer_cur_len = ps_buffer_size - 1;
+               }
+       }
+       else
+       {
+               ps_buffer[ps_buffer_cur_len++] = ' ';
+               memcpy(ps_buffer + ps_buffer_cur_len, suffix, len + 1);
+               ps_buffer_cur_len = ps_buffer_cur_len + len;
+       }
+
+       Assert(strlen(ps_buffer) == ps_buffer_cur_len);
+
+       /* and set the new title */
+       set_ps_display_internal();
+#endif                                                 /* not PS_USE_NONE */
+}
+
+/*
+ * set_ps_display_remove_suffix
+ *             Remove the process display suffix added by set_ps_display_suffix
+ */
+void
+set_ps_display_remove_suffix(void)
+{
+#ifndef PS_USE_NONE
+       /* update_process_title=off disables updates */
+       if (!update_process_title)
+               return;
+
+       /* no ps display for stand-alone backend */
+       if (!IsUnderPostmaster)
+               return;
+
+#ifdef PS_USE_CLOBBER_ARGV
+       /* If ps_buffer is a pointer, it might still be null */
+       if (!ps_buffer)
+               return;
+#endif
+
+       /* check we added a suffix */
+       if (ps_buffer_nosuffix_len == 0)
+               return;         /* no suffix */
 
+       /* remove the suffix from ps_buffer */
+       ps_buffer[ps_buffer_nosuffix_len] = '\0';
+       ps_buffer_cur_len = ps_buffer_nosuffix_len;
+       ps_buffer_nosuffix_len = 0;
+
+       Assert(ps_buffer_cur_len == strlen(ps_buffer));
+
+       /* and set the new title */
+       set_ps_display_internal();
+#endif                                                 /* not PS_USE_NONE */
+}
 
 /*
  * Call this to update the ps status display to a fixed prefix plus an
  * indication of what you're currently doing passed in the argument.
+ *
+ * 'len' must be the same as strlen(activity)
  */
 void
-set_ps_display(const char *activity)
+set_ps_display_with_len(const char *activity, size_t len)
 {
+       Assert(strlen(activity) == len);
+
 #ifndef PS_USE_NONE
        /* update_process_title=off disables updates */
        if (!update_process_title)
@@ -356,13 +465,34 @@ set_ps_display(const char *activity)
                return;
 #endif
 
+       /* wipe out the suffix when the title is completely changed */
+       ps_buffer_nosuffix_len = 0;
+
        /* Update ps_buffer to contain both fixed part and activity */
-       strlcpy(ps_buffer + ps_buffer_fixed_size, activity,
-                       ps_buffer_size - ps_buffer_fixed_size);
-       ps_buffer_cur_len = strlen(ps_buffer);
+       if (ps_buffer_fixed_size + len >= ps_buffer_size)
+       {
+               /* handle the case where ps_buffer doesn't have enough space */
+               memcpy(ps_buffer + ps_buffer_fixed_size, activity,
+                          ps_buffer_size - ps_buffer_fixed_size - 1);
+               ps_buffer[ps_buffer_size - 1] = '\0';
+               ps_buffer_cur_len = ps_buffer_size - 1;
+       }
+       else
+       {
+               memcpy(ps_buffer + ps_buffer_fixed_size, activity, len + 1);
+               ps_buffer_cur_len = ps_buffer_fixed_size + len;
+       }
+       Assert(strlen(ps_buffer) == ps_buffer_cur_len);
 
        /* Transmit new setting to kernel, if necessary */
+       set_ps_display_internal();
+#endif                                                 /* not PS_USE_NONE */
+}
 
+#ifndef PS_USE_NONE
+static void
+set_ps_display_internal(void)
+{
 #ifdef PS_USE_SETPROCTITLE
        setproctitle("%s", ps_buffer);
 #elif defined(PS_USE_SETPROCTITLE_FAST)
@@ -400,9 +530,8 @@ set_ps_display(const char *activity)
                ident_handle = CreateEvent(NULL, TRUE, FALSE, name);
        }
 #endif                                                 /* PS_USE_WIN32 */
-#endif                                                 /* not PS_USE_NONE */
 }
-
+#endif
 
 /*
  * Returns what's currently in the ps display, in case someone needs
diff --git a/src/include/utils/ps_status.h b/src/include/utils/ps_status.h
index 6953a326f1..ff5a2b2b8a 100644
--- a/src/include/utils/ps_status.h
+++ b/src/include/utils/ps_status.h
@@ -25,7 +25,22 @@ extern char **save_ps_display_args(int argc, char **argv);
 
 extern void init_ps_display(const char *fixed_part);
 
-extern void set_ps_display(const char *activity);
+extern void set_ps_display_suffix(const char *suffix);
+
+extern void set_ps_display_remove_suffix(void);
+
+extern void set_ps_display_with_len(const char *activity, size_t len);
+
+/*
+ * set_ps_display
+ *             inlined to allow strlen to be evaluated during compilation when
+ *             passing string constants.
+ */
+static inline void
+set_ps_display(const char *activity)
+{
+       set_ps_display_with_len(activity, strlen(activity));
+}
 
 extern const char *get_ps_display(int *displen);
 

Reply via email to