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;
}