In [1] I noticed a bit of a poor usage of appendStringInfoString which
just appends 4 spaces in a loop, one for each indent level of the
jsonb.  It should be better just to use appendStringInfoSpaces and
just append all the spaces in one go rather than appending 4 spaces in
a loop. That'll save having to check enlargeStringInfo() once for each
loop.

I'm aiming this mostly as a cleanup patch, but after looking at the
appendStringInfoSpaces code, I thought it could be done a bit more
efficiently by using memset instead of using the while loop that keeps
track of 2 counters. memset has the option of doing more than a char
at a time, which should be useful for larger numbers of spaces.

It does seem a bit faster when appending 8 chars at least going by the
attached spaces.c file.

With -O1
$ ./spaces
while 0.536577 seconds
memset 0.326532 seconds

However, I'm not really expecting much of a performance increase from
this change. I do at least want to make sure I've not made anything
worse, so I used pgbench to run:

select jsonb_pretty(row_to_json(pg_class)::jsonb) from pg_class;

perf top says:

Master:
 0.96%  postgres          [.] add_indent.part.0

Patched
  0.25%  postgres          [.] add_indent.part.0

I can't really detect a certain enough TPS change over the noise. I
expect it might become more significant with more complex json that
has more than a single indent level.

I could only find 1 other instance where we use appendStringInfoString
to append spaces. I've adjusted that one too.

David

[1] 
https://postgr.es/m/caaphdvrrfnsm8df24tmyozpvo-r5zp+0foqvo2xcyhrfteh...@mail.gmail.com
diff --git a/src/backend/commands/explain.c b/src/backend/commands/explain.c
index e4621ef8d6..5212a64b1e 100644
--- a/src/backend/commands/explain.c
+++ b/src/backend/commands/explain.c
@@ -3324,7 +3324,7 @@ show_hashagg_info(AggState *aggstate, ExplainState *es)
                        if (!gotone)
                                ExplainIndentText(es);
                        else
-                               appendStringInfoString(es->str, "  ");
+                               appendStringInfoSpaces(es->str, 2);
 
                        appendStringInfo(es->str, "Batches: %d  Memory Usage: " 
INT64_FORMAT "kB",
                                                         
aggstate->hash_batches_used, memPeakKb);
diff --git a/src/backend/utils/adt/jsonb.c b/src/backend/utils/adt/jsonb.c
index 4ff2eced4c..0539f41c17 100644
--- a/src/backend/utils/adt/jsonb.c
+++ b/src/backend/utils/adt/jsonb.c
@@ -626,11 +626,8 @@ add_indent(StringInfo out, bool indent, int level)
 {
        if (indent)
        {
-               int                     i;
-
                appendStringInfoCharMacro(out, '\n');
-               for (i = 0; i < level; i++)
-                       appendBinaryStringInfo(out, "    ", 4);
+               appendStringInfoSpaces(out, level * 4);
        }
 }
 
diff --git a/src/common/stringinfo.c b/src/common/stringinfo.c
index b3d3c99b8c..05b22b5c53 100644
--- a/src/common/stringinfo.c
+++ b/src/common/stringinfo.c
@@ -211,8 +211,8 @@ appendStringInfoSpaces(StringInfo str, int count)
                enlargeStringInfo(str, count);
 
                /* OK, append the spaces */
-               while (--count >= 0)
-                       str->data[str->len++] = ' ';
+               memset(&str->data[str->len], ' ', count);
+               str->len += count;
                str->data[str->len] = '\0';
        }
 }
#include <time.h>
#include <stdio.h>
#include <string.h>

int f1(char *data, int count)
{
        int len = 0;

        /* OK, append the spaces */
        while (--count >= 0)
                data[len++] = ' ';
        data[len] = '\0';
        return len;
}

int f2(char *data, int count)
{
        memset(data, ' ', count);
        data[count] = '\0';
        return count;
}

#define NLOOPS 100000000
int main(void)
{
        char buffer[8];
        clock_t start, end;
        unsigned long long x = 0;

        start = clock();
        for (int i = 0; i < NLOOPS; i++)
        {
                x += f1(buffer, sizeof(buffer) - 1);
        }
        end = clock();

        printf("while %g seconds\n", (double) (end - start) / CLOCKS_PER_SEC);

        start = clock();
        for (int i = 0; i < NLOOPS; i++)
        {
                x += f2(buffer, sizeof(buffer) - 1);
        }
        end = clock();

        printf("memset %g seconds\n", (double) (end - start) / CLOCKS_PER_SEC);
        printf("%lld\n", x);
        return 0;
}

Reply via email to