From: Tom Lane <tgl@sss.pgh.pa.us>
To: Andres Freund <andres@anarazel.de>
Cc: pgsql-hackers@lists.postgresql.org, Thomas Munro <thomas.munro@enterprisedb.com>
Cc: Andrew Gierth <andrew@tao11.riddles.org.uk>
Cc: Alexander Kuzmenkov <a.kuzmenkov@postgrespro.ru>
Subject: Re: Performance improvements for src/port/snprintf.c
Date: Tue, 02 Oct 2018 17:54:31 -0400
Message-ID: <30551.1538517271@sss.pgh.pa.us> (raw)
In-Reply-To: <6FEC34CD-B8C0-44E2-8D1F-2320568BDC78@anarazel.de>
References: <11787.1534530779@sss.pgh.pa.us>
<2238.1534716465@sss.pgh.pa.us>
<f5dd41da-911c-fd97-1b78-e0f8c0141897@postgrespro.ru>
<15785.1536776055@sss.pgh.pa.us>
<20180926220419.lorxpb564gakdjkz@alap3.anarazel.de>
<20180926221814.el5nwmkez6zfmzbz@alap3.anarazel.de>
<14302.1538005507@sss.pgh.pa.us>
<20180926235821.zmpxqv66ob7v2ffi@alap3.anarazel.de>
<15882.1538007944@sss.pgh.pa.us>
<20180927004022.lfhffbj3roxzf5ca@alap3.anarazel.de>
<20180927005705.shbl3n5hq3t65n2j@alap3.anarazel.de>
<87r2hffwda.fsf@news-spur.riddles.org.uk>
<CAEepm=1-3AG2O25VNnLXsjCM3EHBLW=jyJtpp_AMGG6a-GRJZA@mail.gmail.com>
<6FEC34CD-B8C0-44E2-8D1F-2320568BDC78@anarazel.de>
Here's a version of this patch rebased over commit 625b38ea0.
That commit's fix for the possibly-expensive memset means that we need
to reconsider performance numbers for this patch. I re-ran my previous
tests, and it's still looking like this is a substantial win, as it makes
snprintf.c faster than the native snprintf for most non-float cases.
We're still stuck at something like 10% penalty for float cases.
While there might be value in implementing our own float printing code,
I have a pretty hard time getting excited about the cost/benefit ratio
of that. I think that what we probably really ought to do here is hack
float4out/float8out to bypass the extra overhead, as in the 0002 patch
below.
For reference, I attach the testbed I'm using now plus some results.
I wasn't able to get my cranky NetBSD system up today, so I don't
have results for that. However, I did add recent glibc (Fedora 28)
to the mix, and I was interested to discover that they seem to have
added a fast-path for format strings that are exactly "%s", just as
NetBSD did. I wonder if we should reconsider our position on doing
that. It'd be a simple enough addition...
regards, tom lane
Attachments:download all (4 files, .tar.gz)0001-snprintf-speedups-5.patch (text/x-diff, 28.4K) downloaddiff --git a/configure b/configureindex 6414ec1..0448c6b 100755
*** a/configure
--- b/configure
*************** fi
*** 15100,15106 ****
LIBS_including_readline="$LIBS"
LIBS=`echo "$LIBS" | sed -e 's/-ledit//g' -e 's/-lreadline//g'`
! for ac_func in cbrt clock_gettime fdatasync getifaddrs getpeerucred getrlimit mbstowcs_l memmove poll posix_fallocate ppoll pstat pthread_is_threaded_np readlink setproctitle setproctitle_fast setsid shm_open symlink sync_file_range utime utimes wcstombs_l
do :
as_ac_var=`$as_echo "ac_cv_func_$ac_func" | $as_tr_sh`
ac_fn_c_check_func "$LINENO" "$ac_func" "$as_ac_var"
--- 15100,15106 ----
LIBS_including_readline="$LIBS"
LIBS=`echo "$LIBS" | sed -e 's/-ledit//g' -e 's/-lreadline//g'`
! for ac_func in cbrt clock_gettime fdatasync getifaddrs getpeerucred getrlimit mbstowcs_l memmove poll posix_fallocate ppoll pstat pthread_is_threaded_np readlink setproctitle setproctitle_fast setsid shm_open strchrnul symlink sync_file_range utime utimes wcstombs_l
do :
as_ac_var=`$as_echo "ac_cv_func_$ac_func" | $as_tr_sh`
ac_fn_c_check_func "$LINENO" "$ac_func" "$as_ac_var"
diff --git a/configure.in b/configure.inindex 158d5a1..23b5bb8 100644
*** a/configure.in
--- b/configure.in
*************** PGAC_FUNC_WCSTOMBS_L
*** 1571,1577 ****
LIBS_including_readline="$LIBS"
LIBS=`echo "$LIBS" | sed -e 's/-ledit//g' -e 's/-lreadline//g'`
! AC_CHECK_FUNCS([cbrt clock_gettime fdatasync getifaddrs getpeerucred getrlimit mbstowcs_l memmove poll posix_fallocate ppoll pstat pthread_is_threaded_np readlink setproctitle setproctitle_fast setsid shm_open symlink sync_file_range utime utimes wcstombs_l])
AC_REPLACE_FUNCS(fseeko)
case $host_os in
--- 1571,1577 ----
LIBS_including_readline="$LIBS"
LIBS=`echo "$LIBS" | sed -e 's/-ledit//g' -e 's/-lreadline//g'`
! AC_CHECK_FUNCS([cbrt clock_gettime fdatasync getifaddrs getpeerucred getrlimit mbstowcs_l memmove poll posix_fallocate ppoll pstat pthread_is_threaded_np readlink setproctitle setproctitle_fast setsid shm_open strchrnul symlink sync_file_range utime utimes wcstombs_l])
AC_REPLACE_FUNCS(fseeko)
case $host_os in
diff --git a/src/include/pg_config.h.in b/src/include/pg_config.h.inindex 90dda8e..7894caa 100644
*** a/src/include/pg_config.h.in
--- b/src/include/pg_config.h.in
***************
*** 523,528 ****
--- 523,531 ----
/* Define to 1 if you have the <stdlib.h> header file. */
#undef HAVE_STDLIB_H
+ /* Define to 1 if you have the `strchrnul' function. */+ #undef HAVE_STRCHRNUL+
/* Define to 1 if you have the `strerror_r' function. */
#undef HAVE_STRERROR_R
diff --git a/src/include/pg_config.h.win32 b/src/include/pg_config.h.win32index 93bb773..f7a051d 100644
*** a/src/include/pg_config.h.win32
--- b/src/include/pg_config.h.win32
***************
*** 394,399 ****
--- 394,402 ----
/* Define to 1 if you have the <stdlib.h> header file. */
#define HAVE_STDLIB_H 1
+ /* Define to 1 if you have the `strchrnul' function. */+ /* #undef HAVE_STRCHRNUL */+
/* Define to 1 if you have the `strerror_r' function. */
/* #undef HAVE_STRERROR_R */
diff --git a/src/port/snprintf.c b/src/port/snprintf.cindex 1be5f70..3094ad8 100644
*** a/src/port/snprintf.c
--- b/src/port/snprintf.c
*************** flushbuffer(PrintfTarget *target)
*** 314,320 ****
}
! static void fmtstr(char *value, int leftjust, int minlen, int maxwidth,
int pointflag, PrintfTarget *target);
static void fmtptr(void *value, PrintfTarget *target);
static void fmtint(int64 value, char type, int forcesign,
--- 314,322 ----
}
! static bool find_arguments(const char *format, va_list args,
! PrintfArgValue *argvalues);
! static void fmtstr(const char *value, int leftjust, int minlen, int maxwidth,
int pointflag, PrintfTarget *target);
static void fmtptr(void *value, PrintfTarget *target);
static void fmtint(int64 value, char type, int forcesign,
*************** static void fmtfloat(double value, char
*** 326,336 ****
PrintfTarget *target);
static void dostr(const char *str, int slen, PrintfTarget *target);
static void dopr_outch(int c, PrintfTarget *target);
static int adjust_sign(int is_negative, int forcesign, int *signvalue);
! static void adjust_padlen(int minlen, int vallen, int leftjust, int *padlen);
! static void leading_pad(int zpad, int *signvalue, int *padlen,
PrintfTarget *target);
! static void trailing_pad(int *padlen, PrintfTarget *target);
/*
--- 328,339 ----
PrintfTarget *target);
static void dostr(const char *str, int slen, PrintfTarget *target);
static void dopr_outch(int c, PrintfTarget *target);
+ static void dopr_outchmulti(int c, int slen, PrintfTarget *target);
static int adjust_sign(int is_negative, int forcesign, int *signvalue);
! static int compute_padlen(int minlen, int vallen, int leftjust);
! static void leading_pad(int zpad, int signvalue, int *padlen,
PrintfTarget *target);
! static void trailing_pad(int padlen, PrintfTarget *target);
/*
*************** static void
*** 340,349 ****
dopr(PrintfTarget *target, const char *format, va_list args)
{
int save_errno = errno;
! const char *format_start = format;
int ch;
bool have_dollar;
- bool have_non_dollar;
bool have_star;
bool afterstar;
int accum;
--- 343,351 ----
dopr(PrintfTarget *target, const char *format, va_list args)
{
int save_errno = errno;
! const char *first_pct = NULL;
int ch;
bool have_dollar;
bool have_star;
bool afterstar;
int accum;
*************** dopr(PrintfTarget *target, const char *f
*** 355,580 ****
int precision;
int zpad;
int forcesign;
- int last_dollar;
int fmtpos;
int cvalue;
int64 numvalue;
double fvalue;
char *strvalue;
- int i;- PrintfArgType argtypes[PG_NL_ARGMAX + 1];
PrintfArgValue argvalues[PG_NL_ARGMAX + 1];
/*
! * Parse the format string to determine whether there are %n$ format
! * specs, and identify the types and order of the format parameters.
*/
! have_dollar = have_non_dollar = false;
! last_dollar = 0;
! MemSet(argtypes, 0, sizeof(argtypes));
! while ((ch = *format++) != '\0')
{
! if (ch != '%')
! continue;
! longflag = longlongflag = pointflag = 0;
! fmtpos = accum = 0;
! afterstar = false;
! nextch1:
! ch = *format++;
! if (ch == '\0')
! break; /* illegal, but we don't complain */
! switch (ch)
{
! case '-':
! case '+':
! goto nextch1;
! case '0':
! case '1':
! case '2':
! case '3':
! case '4':
! case '5':
! case '6':
! case '7':
! case '8':
! case '9':
! accum = accum * 10 + (ch - '0');
! goto nextch1;
! case '.':
! pointflag = 1;
! accum = 0;
! goto nextch1;
! case '*':
! if (afterstar)
! have_non_dollar = true; /* multiple stars */
! afterstar = true;
! accum = 0;
! goto nextch1;
! case '$':
! have_dollar = true;
! if (accum <= 0 || accum > PG_NL_ARGMAX)
! goto bad_format;
! if (afterstar)
! {
! if (argtypes[accum] &&
! argtypes[accum] != ATYPE_INT)
! goto bad_format;
! argtypes[accum] = ATYPE_INT;
! last_dollar = Max(last_dollar, accum);
! afterstar = false;
! }
! else
! fmtpos = accum;
! accum = 0;
! goto nextch1;
! case 'l':
! if (longflag)
! longlongflag = 1;
! else
! longflag = 1;
! goto nextch1;
! case 'z':
! #if SIZEOF_SIZE_T == 8
! #ifdef HAVE_LONG_INT_64
! longflag = 1;
! #elif defined(HAVE_LONG_LONG_INT_64)
! longlongflag = 1;
! #else
! #error "Don't know how to print 64bit integers"
! #endif
#else
! /* assume size_t is same size as int */
#endif
- goto nextch1;- case 'h':- case '\'':- /* ignore these */- goto nextch1;- case 'd':- case 'i':- case 'o':- case 'u':- case 'x':- case 'X':- if (fmtpos)- {- PrintfArgType atype;
! if (longlongflag)
! atype = ATYPE_LONGLONG;
! else if (longflag)
! atype = ATYPE_LONG;
! else
! atype = ATYPE_INT;
! if (argtypes[fmtpos] &&
! argtypes[fmtpos] != atype)
! goto bad_format;
! argtypes[fmtpos] = atype;
! last_dollar = Max(last_dollar, fmtpos);
! }
! else
! have_non_dollar = true;
! break;
! case 'c':
! if (fmtpos)
! {
! if (argtypes[fmtpos] &&
! argtypes[fmtpos] != ATYPE_INT)
! goto bad_format;
! argtypes[fmtpos] = ATYPE_INT;
! last_dollar = Max(last_dollar, fmtpos);
! }
! else
! have_non_dollar = true;
! break;
! case 's':
! case 'p':
! if (fmtpos)
! {
! if (argtypes[fmtpos] &&
! argtypes[fmtpos] != ATYPE_CHARPTR)
! goto bad_format;
! argtypes[fmtpos] = ATYPE_CHARPTR;
! last_dollar = Max(last_dollar, fmtpos);
! }
! else
! have_non_dollar = true;
! break;
! case 'e':
! case 'E':
! case 'f':
! case 'g':
! case 'G':
! if (fmtpos)
! {
! if (argtypes[fmtpos] &&
! argtypes[fmtpos] != ATYPE_DOUBLE)
! goto bad_format;
! argtypes[fmtpos] = ATYPE_DOUBLE;
! last_dollar = Max(last_dollar, fmtpos);
! }
! else
! have_non_dollar = true;
break;
! case 'm':
! case '%':
break;
}
/*
! * If we finish the spec with afterstar still set, there's a
! * non-dollar star in there.
*/
! if (afterstar)
! have_non_dollar = true;
! }
!
! /* Per spec, you use either all dollar or all not. */
! if (have_dollar && have_non_dollar)
! goto bad_format;
!
! /*
! * In dollar mode, collect the arguments in physical order.
! */
! for (i = 1; i <= last_dollar; i++)
! {
! switch (argtypes[i])
! {
! case ATYPE_NONE:
! goto bad_format;
! case ATYPE_INT:
! argvalues[i].i = va_arg(args, int);
! break;
! case ATYPE_LONG:
! argvalues[i].l = va_arg(args, long);
! break;
! case ATYPE_LONGLONG:
! argvalues[i].ll = va_arg(args, int64);
! break;
! case ATYPE_DOUBLE:
! argvalues[i].d = va_arg(args, double);
! break;
! case ATYPE_CHARPTR:
! argvalues[i].cptr = va_arg(args, char *);
! break;
! }
! }
!
! /*
! * At last we can parse the format for real.
! */
! format = format_start;
! while ((ch = *format++) != '\0')
! {
! if (target->failed)
! break;
! if (ch != '%')
! {
! dopr_outch(ch, target);
! continue;
! }
fieldwidth = precision = zpad = leftjust = forcesign = 0;
longflag = longlongflag = pointflag = 0;
fmtpos = accum = 0;
--- 357,417 ----
int precision;
int zpad;
int forcesign;
int fmtpos;
int cvalue;
int64 numvalue;
double fvalue;
char *strvalue;
PrintfArgValue argvalues[PG_NL_ARGMAX + 1];
/*
! * Initially, we suppose the format string does not use %n$. The first
! * time we come to a conversion spec that has that, we'll call
! * find_arguments() to check for consistent use of %n$ and fill the
! * argvalues array with the argument values in the correct order.
*/
! have_dollar = false;
! while (*format != '\0')
{
! /* Locate next conversion specifier */
! if (*format != '%')
{
! const char *next_pct = format + 1;
!
! /*
! * If strchrnul exists (it's a glibc-ism), it's a good bit faster
! * than the equivalent manual loop. Note: this doesn't compile
! * cleanly without -D_GNU_SOURCE, but we normally use that on
! * glibc platforms.
! */
! #ifdef HAVE_STRCHRNUL
! next_pct = strchrnul(next_pct, '%');
#else
! while (*next_pct != '\0' && *next_pct != '%')
! next_pct++;
#endif
! /* Dump literal data we just scanned over */
! dostr(format, next_pct - format, target);
! if (target->failed)
break;
!
! if (*next_pct == '\0')
break;
+ format = next_pct;
}
/*
! * Remember start of first conversion spec; if we find %n$, then it's
! * sufficient for find_arguments() to start here, without rescanning
! * earlier literal text.
*/
! if (first_pct == NULL)
! first_pct = format;
! /* Process conversion spec starting at *format */
! format++;
fieldwidth = precision = zpad = leftjust = forcesign = 0;
longflag = longlongflag = pointflag = 0;
fmtpos = accum = 0;
*************** nextch2:
*** 618,624 ****
case '*':
if (have_dollar)
{
! /* process value after reading n$ */
afterstar = true;
}
else
--- 455,465 ----
case '*':
if (have_dollar)
{
! /*
! * We'll process value after reading n$. Note it's OK to
! * assume have_dollar is set correctly, because in a valid
! * format string the initial % must have had n$ if * does.
! */
afterstar = true;
}
else
*************** nextch2:
*** 649,654 ****
--- 490,503 ----
accum = 0;
goto nextch2;
case '$':
+ /* First dollar sign? */+ if (!have_dollar)+ {+ /* Yup, so examine all conversion specs in format */+ if (!find_arguments(first_pct, args, argvalues))+ goto bad_format;+ have_dollar = true;+ }
if (afterstar)
{
/* fetch and process star value */
*************** nextch2:
*** 836,841 ****
--- 685,694 ----
dopr_outch('%', target);
break;
}
+ + /* Check for failure after each conversion spec */+ if (target->failed)+ break;
}
return;
*************** bad_format:
*** 845,852 ****
target->failed = true;
}
static void
! fmtstr(char *value, int leftjust, int minlen, int maxwidth,
int pointflag, PrintfTarget *target)
{
int padlen,
--- 698,933 ----
target->failed = true;
}
+ /*+ * find_arguments(): sort out the arguments for a format spec with %n$+ *+ * If format is valid, return true and fill argvalues[i] with the value+ * for the conversion spec that has %i$ or *i$. Else return false.+ */+ static bool+ find_arguments(const char *format, va_list args,+ PrintfArgValue *argvalues)+ {+ int ch;+ bool afterstar;+ int accum;+ int longlongflag;+ int longflag;+ int fmtpos;+ int i;+ int last_dollar;+ PrintfArgType argtypes[PG_NL_ARGMAX + 1];+ + /* Initialize to "no dollar arguments known" */+ last_dollar = 0;+ MemSet(argtypes, 0, sizeof(argtypes));+ + /*+ * This loop must accept the same format strings as the one in dopr().+ * However, we don't need to analyze them to the same level of detail.+ *+ * Since we're only called if there's a dollar-type spec somewhere, we can+ * fail immediately if we find a non-dollar spec. Per the C99 standard,+ * all argument references in the format string must be one or the other.+ */+ while (*format != '\0')+ {+ /* Locate next conversion specifier */+ if (*format != '%')+ {+ /* Unlike dopr, we can just quit if there's no more specifiers */+ format = strchr(format + 1, '%');+ if (format == NULL)+ break;+ }+ + /* Process conversion spec starting at *format */+ format++;+ longflag = longlongflag = 0;+ fmtpos = accum = 0;+ afterstar = false;+ nextch1:+ ch = *format++;+ if (ch == '\0')+ break; /* illegal, but we don't complain */+ switch (ch)+ {+ case '-':+ case '+':+ goto nextch1;+ case '0':+ case '1':+ case '2':+ case '3':+ case '4':+ case '5':+ case '6':+ case '7':+ case '8':+ case '9':+ accum = accum * 10 + (ch - '0');+ goto nextch1;+ case '.':+ accum = 0;+ goto nextch1;+ case '*':+ if (afterstar)+ return false; /* previous star missing dollar */+ afterstar = true;+ accum = 0;+ goto nextch1;+ case '$':+ if (accum <= 0 || accum > PG_NL_ARGMAX)+ return false;+ if (afterstar)+ {+ if (argtypes[accum] &&+ argtypes[accum] != ATYPE_INT)+ return false;+ argtypes[accum] = ATYPE_INT;+ last_dollar = Max(last_dollar, accum);+ afterstar = false;+ }+ else+ fmtpos = accum;+ accum = 0;+ goto nextch1;+ case 'l':+ if (longflag)+ longlongflag = 1;+ else+ longflag = 1;+ goto nextch1;+ case 'z':+ #if SIZEOF_SIZE_T == 8+ #ifdef HAVE_LONG_INT_64+ longflag = 1;+ #elif defined(HAVE_LONG_LONG_INT_64)+ longlongflag = 1;+ #else+ #error "Don't know how to print 64bit integers"+ #endif+ #else+ /* assume size_t is same size as int */+ #endif+ goto nextch1;+ case 'h':+ case '\'':+ /* ignore these */+ goto nextch1;+ case 'd':+ case 'i':+ case 'o':+ case 'u':+ case 'x':+ case 'X':+ if (fmtpos)+ {+ PrintfArgType atype;+ + if (longlongflag)+ atype = ATYPE_LONGLONG;+ else if (longflag)+ atype = ATYPE_LONG;+ else+ atype = ATYPE_INT;+ if (argtypes[fmtpos] &&+ argtypes[fmtpos] != atype)+ return false;+ argtypes[fmtpos] = atype;+ last_dollar = Max(last_dollar, fmtpos);+ }+ else+ return false; /* non-dollar conversion spec */+ break;+ case 'c':+ if (fmtpos)+ {+ if (argtypes[fmtpos] &&+ argtypes[fmtpos] != ATYPE_INT)+ return false;+ argtypes[fmtpos] = ATYPE_INT;+ last_dollar = Max(last_dollar, fmtpos);+ }+ else+ return false; /* non-dollar conversion spec */+ break;+ case 's':+ case 'p':+ if (fmtpos)+ {+ if (argtypes[fmtpos] &&+ argtypes[fmtpos] != ATYPE_CHARPTR)+ return false;+ argtypes[fmtpos] = ATYPE_CHARPTR;+ last_dollar = Max(last_dollar, fmtpos);+ }+ else+ return false; /* non-dollar conversion spec */+ break;+ case 'e':+ case 'E':+ case 'f':+ case 'g':+ case 'G':+ if (fmtpos)+ {+ if (argtypes[fmtpos] &&+ argtypes[fmtpos] != ATYPE_DOUBLE)+ return false;+ argtypes[fmtpos] = ATYPE_DOUBLE;+ last_dollar = Max(last_dollar, fmtpos);+ }+ else+ return false; /* non-dollar conversion spec */+ break;+ case 'm':+ case '%':+ break;+ }+ + /*+ * If we finish the spec with afterstar still set, there's a+ * non-dollar star in there.+ */+ if (afterstar)+ return false; /* non-dollar conversion spec */+ }+ + /*+ * Format appears valid so far, so collect the arguments in physical+ * order. (Since we rejected any non-dollar specs that would have+ * collected arguments, we know that dopr() hasn't collected any yet.)+ */+ for (i = 1; i <= last_dollar; i++)+ {+ switch (argtypes[i])+ {+ case ATYPE_NONE:+ return false;+ case ATYPE_INT:+ argvalues[i].i = va_arg(args, int);+ break;+ case ATYPE_LONG:+ argvalues[i].l = va_arg(args, long);+ break;+ case ATYPE_LONGLONG:+ argvalues[i].ll = va_arg(args, int64);+ break;+ case ATYPE_DOUBLE:+ argvalues[i].d = va_arg(args, double);+ break;+ case ATYPE_CHARPTR:+ argvalues[i].cptr = va_arg(args, char *);+ break;+ }+ }+ + return true;+ }+
static void
! fmtstr(const char *value, int leftjust, int minlen, int maxwidth,
int pointflag, PrintfTarget *target)
{
int padlen,
*************** fmtstr(char *value, int leftjust, int mi
*** 861,877 ****
else
vallen = strlen(value);
! adjust_padlen(minlen, vallen, leftjust, &padlen);
! while (padlen > 0)
{
! dopr_outch(' ', target);
! --padlen;
}
dostr(value, vallen, target);
! trailing_pad(&padlen, target);
}
static void
--- 942,958 ----
else
vallen = strlen(value);
! padlen = compute_padlen(minlen, vallen, leftjust);
! if (padlen > 0)
{
! dopr_outchmulti(' ', padlen, target);
! padlen = 0;
}
dostr(value, vallen, target);
! trailing_pad(padlen, target);
}
static void
*************** fmtint(int64 value, char type, int force
*** 899,905 ****
int signvalue = 0;
char convert[64];
int vallen = 0;
! int padlen = 0; /* amount to pad */
int zeropad; /* extra leading zeroes */
switch (type)
--- 980,986 ----
int signvalue = 0;
char convert[64];
int vallen = 0;
! int padlen; /* amount to pad */
int zeropad; /* extra leading zeroes */
switch (type)
*************** fmtint(int64 value, char type, int force
*** 947,988 ****
do
{
! convert[vallen++] = cvt[uvalue % base];
uvalue = uvalue / base;
} while (uvalue);
}
zeropad = Max(0, precision - vallen);
! adjust_padlen(minlen, vallen + zeropad, leftjust, &padlen);
! leading_pad(zpad, &signvalue, &padlen, target);
! while (zeropad-- > 0)
! dopr_outch('0', target);
! while (vallen > 0)
! dopr_outch(convert[--vallen], target);
! trailing_pad(&padlen, target);
}
static void
fmtchar(int value, int leftjust, int minlen, PrintfTarget *target)
{
! int padlen = 0; /* amount to pad */
! adjust_padlen(minlen, 1, leftjust, &padlen);
! while (padlen > 0)
{
! dopr_outch(' ', target);
! --padlen;
}
dopr_outch(value, target);
! trailing_pad(&padlen, target);
}
static void
--- 1028,1068 ----
do
{
! convert[sizeof(convert) - (++vallen)] = cvt[uvalue % base];
uvalue = uvalue / base;
} while (uvalue);
}
zeropad = Max(0, precision - vallen);
! padlen = compute_padlen(minlen, vallen + zeropad, leftjust);
! leading_pad(zpad, signvalue, &padlen, target);
! if (zeropad > 0)
! dopr_outchmulti('0', zeropad, target);
! dostr(convert + sizeof(convert) - vallen, vallen, target);
! trailing_pad(padlen, target);
}
static void
fmtchar(int value, int leftjust, int minlen, PrintfTarget *target)
{
! int padlen; /* amount to pad */
! padlen = compute_padlen(minlen, 1, leftjust);
! if (padlen > 0)
{
! dopr_outchmulti(' ', padlen, target);
! padlen = 0;
}
dopr_outch(value, target);
! trailing_pad(padlen, target);
}
static void
*************** fmtfloat(double value, char type, int fo
*** 993,1002 ****
int signvalue = 0;
int prec;
int vallen;
! char fmt[32];
char convert[1024];
int zeropadlen = 0; /* amount to pad with zeroes */
! int padlen = 0; /* amount to pad with spaces */
/*
* We rely on the regular C library's sprintf to do the basic conversion,
--- 1073,1086 ----
int signvalue = 0;
int prec;
int vallen;
! char fmt[8];
char convert[1024];
int zeropadlen = 0; /* amount to pad with zeroes */
! int padlen; /* amount to pad with spaces */
!
! /* Handle sign (NaNs have no sign) */
! if (!isnan(value) && adjust_sign((value < 0), forcesign, &signvalue))
! value = -value;
/*
* We rely on the regular C library's sprintf to do the basic conversion,
*************** fmtfloat(double value, char type, int fo
*** 1018,1034 ****
if (pointflag)
{
- if (sprintf(fmt, "%%.%d%c", prec, type) < 0)- goto fail;
zeropadlen = precision - prec;
}
- else if (sprintf(fmt, "%%%c", type) < 0)- goto fail;- - if (!isnan(value) && adjust_sign((value < 0), forcesign, &signvalue))- value = -value;- - vallen = sprintf(convert, fmt, value);
if (vallen < 0)
goto fail;
--- 1102,1122 ----
if (pointflag)
{
zeropadlen = precision - prec;
+ fmt[0] = '%';+ fmt[1] = '.';+ fmt[2] = '*';+ fmt[3] = type;+ fmt[4] = '\0';+ vallen = sprintf(convert, fmt, prec, value);+ }+ else+ {+ fmt[0] = '%';+ fmt[1] = type;+ fmt[2] = '\0';+ vallen = sprintf(convert, fmt, value);
}
if (vallen < 0)
goto fail;
*************** fmtfloat(double value, char type, int fo
*** 1036,1044 ****
if (zeropadlen > 0 && !isdigit((unsigned char) convert[vallen - 1]))
zeropadlen = 0;
! adjust_padlen(minlen, vallen + zeropadlen, leftjust, &padlen);
! leading_pad(zpad, &signvalue, &padlen, target);
if (zeropadlen > 0)
{
--- 1124,1132 ----
if (zeropadlen > 0 && !isdigit((unsigned char) convert[vallen - 1]))
zeropadlen = 0;
! padlen = compute_padlen(minlen, vallen + zeropadlen, leftjust);
! leading_pad(zpad, signvalue, &padlen, target);
if (zeropadlen > 0)
{
*************** fmtfloat(double value, char type, int fo
*** 1049,1066 ****
epos = strrchr(convert, 'E');
if (epos)
{
! /* pad after exponent */
dostr(convert, epos - convert, target);
! while (zeropadlen-- > 0)
! dopr_outch('0', target);
dostr(epos, vallen - (epos - convert), target);
}
else
{
/* no exponent, pad after the digits */
dostr(convert, vallen, target);
! while (zeropadlen-- > 0)
! dopr_outch('0', target);
}
}
else
--- 1137,1154 ----
epos = strrchr(convert, 'E');
if (epos)
{
! /* pad before exponent */
dostr(convert, epos - convert, target);
! if (zeropadlen > 0)
! dopr_outchmulti('0', zeropadlen, target);
dostr(epos, vallen - (epos - convert), target);
}
else
{
/* no exponent, pad after the digits */
dostr(convert, vallen, target);
! if (zeropadlen > 0)
! dopr_outchmulti('0', zeropadlen, target);
}
}
else
*************** fmtfloat(double value, char type, int fo
*** 1069,1075 ****
dostr(convert, vallen, target);
}
! trailing_pad(&padlen, target);
return;
fail:
--- 1157,1163 ----
dostr(convert, vallen, target);
}
! trailing_pad(padlen, target);
return;
fail:
*************** fail:
*** 1079,1084 ****
--- 1167,1179 ----
static void
dostr(const char *str, int slen, PrintfTarget *target)
{
+ /* fast path for common case of slen == 1 */+ if (slen == 1)+ {+ dopr_outch(*str, target);+ return;+ }+
while (slen > 0)
{
int avail;
*************** dopr_outch(int c, PrintfTarget *target)
*** 1122,1127 ****
--- 1217,1258 ----
*(target->bufptr++) = c;
}
+ static void+ dopr_outchmulti(int c, int slen, PrintfTarget *target)+ {+ /* fast path for common case of slen == 1 */+ if (slen == 1)+ {+ dopr_outch(c, target);+ return;+ }+ + while (slen > 0)+ {+ int avail;+ + if (target->bufend != NULL)+ avail = target->bufend - target->bufptr;+ else+ avail = slen;+ if (avail <= 0)+ {+ /* buffer full, can we dump to stream? */+ if (target->stream == NULL)+ {+ target->nchars += slen; /* no, lose the data */+ return;+ }+ flushbuffer(target);+ continue;+ }+ avail = Min(avail, slen);+ memset(target->bufptr, c, avail);+ target->bufptr += avail;+ slen -= avail;+ }+ }+
static int
adjust_sign(int is_negative, int forcesign, int *signvalue)
*************** adjust_sign(int is_negative, int forcesi
*** 1137,1178 ****
}
! static void
! adjust_padlen(int minlen, int vallen, int leftjust, int *padlen)
{
! *padlen = minlen - vallen;
! if (*padlen < 0)
! *padlen = 0;
if (leftjust)
! *padlen = -(*padlen);
}
static void
! leading_pad(int zpad, int *signvalue, int *padlen, PrintfTarget *target)
{
if (*padlen > 0 && zpad)
{
! if (*signvalue)
{
! dopr_outch(*signvalue, target);
--(*padlen);
! *signvalue = 0;
}
! while (*padlen > 0)
{
! dopr_outch(zpad, target);
! --(*padlen);
}
}
! while (*padlen > (*signvalue != 0))
{
! dopr_outch(' ', target);
! --(*padlen);
}
! if (*signvalue)
{
! dopr_outch(*signvalue, target);
if (*padlen > 0)
--(*padlen);
else if (*padlen < 0)
--- 1268,1315 ----
}
! static int
! compute_padlen(int minlen, int vallen, int leftjust)
{
! int padlen;
!
! padlen = minlen - vallen;
! if (padlen < 0)
! padlen = 0;
if (leftjust)
! padlen = -padlen;
! return padlen;
}
static void
! leading_pad(int zpad, int signvalue, int *padlen, PrintfTarget *target)
{
+ int maxpad;+
if (*padlen > 0 && zpad)
{
! if (signvalue)
{
! dopr_outch(signvalue, target);
--(*padlen);
! signvalue = 0;
}
! if (*padlen > 0)
{
! dopr_outchmulti(zpad, *padlen, target);
! *padlen = 0;
}
}
! maxpad = (signvalue != 0);
! if (*padlen > maxpad)
{
! dopr_outchmulti(' ', *padlen - maxpad, target);
! *padlen = maxpad;
}
! if (signvalue)
{
! dopr_outch(signvalue, target);
if (*padlen > 0)
--(*padlen);
else if (*padlen < 0)
*************** leading_pad(int zpad, int *signvalue, in
*** 1182,1192 ****
static void
! trailing_pad(int *padlen, PrintfTarget *target)
{
! while (*padlen < 0)
! {
! dopr_outch(' ', target);
! ++(*padlen);
! }
}
--- 1319,1326 ----
static void
! trailing_pad(int padlen, PrintfTarget *target)
{
! if (padlen < 0)
! dopr_outchmulti(' ', -padlen, target);
}
0002-hacky-fix-for-float48out-1.patch (text/x-diff, 2.2K) downloaddiff --git a/src/backend/utils/adt/float.c b/src/backend/utils/adt/float.cindex df35557..2e68991 100644
*** a/src/backend/utils/adt/float.c
--- b/src/backend/utils/adt/float.c
*************** float4out(PG_FUNCTION_ARGS)
*** 258,269 ****
break;
default:
{
int ndig = FLT_DIG + extra_float_digits;
if (ndig < 1)
ndig = 1;
! ascii = psprintf("%.*g", ndig, num);
}
}
--- 258,287 ----
break;
default:
{
+ /*+ * We don't go through snprintf.c here because, for this+ * particular choice of format string, it adds nothing of+ * value to the native behavior of sprintf() --- except+ * handling buffer overrun. We just make the buffer big+ * enough to not have to worry.+ */+ #undef sprintf
int ndig = FLT_DIG + extra_float_digits;
+ int len PG_USED_FOR_ASSERTS_ONLY;+ /* Neither of these limits can trigger, but be paranoid */
if (ndig < 1)
ndig = 1;
+ else if (ndig > 32)+ ndig = 32;
! ascii = (char *) palloc(64);
!
! len = sprintf(ascii, "%.*g", ndig, num);
!
! Assert(len > 0 && len < 64);
!
! #define sprintf pg_sprintf
}
}
*************** float8out_internal(double num)
*** 494,505 ****
break;
default:
{
int ndig = DBL_DIG + extra_float_digits;
if (ndig < 1)
ndig = 1;
! ascii = psprintf("%.*g", ndig, num);
}
}
--- 512,541 ----
break;
default:
{
+ /*+ * We don't go through snprintf.c here because, for this+ * particular choice of format string, it adds nothing of+ * value to the native behavior of sprintf() --- except+ * handling buffer overrun. We just make the buffer big+ * enough to not have to worry.+ */+ #undef sprintf
int ndig = DBL_DIG + extra_float_digits;
+ int len PG_USED_FOR_ASSERTS_ONLY;+ /* Neither of these limits can trigger, but be paranoid */
if (ndig < 1)
ndig = 1;
+ else if (ndig > 32)+ ndig = 32;
! ascii = (char *) palloc(64);
!
! len = sprintf(ascii, "%.*g", ndig, num);
!
! Assert(len > 0 && len < 64);
!
! #define sprintf pg_sprintf
}
}
=
time-results (text/plain, 17.2K) download
old glibc (RHEL6) vs snprintf.c HEAD:
Test case: %2$.*3$f %1$d
snprintf time = 768.907 ms total, 0.000768907 ms per iteration
pg_snprintf time = 1059.85 ms total, 0.00105985 ms per iteration
ratio = 1.378
Test case: %.*g
snprintf time = 782.535 ms total, 0.000782535 ms per iteration
pg_snprintf time = 1061.71 ms total, 0.00106171 ms per iteration
ratio = 1.357
Test case: %d %d
snprintf time = 162.616 ms total, 0.000162616 ms per iteration
pg_snprintf time = 135.103 ms total, 0.000135103 ms per iteration
ratio = 0.831
Test case: %10d
snprintf time = 143.027 ms total, 0.000143027 ms per iteration
pg_snprintf time = 123.307 ms total, 0.000123307 ms per iteration
ratio = 0.862
Test case: %s
snprintf time = 306.78 ms total, 0.00030678 ms per iteration
pg_snprintf time = 89.8803 ms total, 8.98803e-05 ms per iteration
ratio = 0.293
Test case: %sx
snprintf time = 310.329 ms total, 0.000310329 ms per iteration
pg_snprintf time = 97.4798 ms total, 9.74798e-05 ms per iteration
ratio = 0.314
Test case: %d 0123456789001234567890012345678900123456789001234567890012345678900123456789001234567890012345678900123456789001234567890012345678900123456789001234567890012345678900123456789001234567890012345678900123456789001234567890
snprintf time = 155.288 ms total, 0.000155288 ms per iteration
pg_snprintf time = 1393.57 ms total, 0.00139357 ms per iteration
ratio = 8.974
Test case: %1$d 0123456789001234567890012345678900123456789001234567890012345678900123456789001234567890012345678900123456789001234567890012345678900123456789001234567890012345678900123456789001234567890012345678900123456789001234567890
snprintf time = 238.737 ms total, 0.000238737 ms per iteration
pg_snprintf time = 1407 ms total, 0.001407 ms per iteration
ratio = 5.894
old glibc (RHEL6) vs snprintf.c v5 patch:
Test case: %2$.*3$f %1$d
snprintf time = 768.425 ms total, 0.000768425 ms per iteration
pg_snprintf time = 895.599 ms total, 0.000895599 ms per iteration
ratio = 1.165
Test case: %.*g
snprintf time = 781.704 ms total, 0.000781704 ms per iteration
pg_snprintf time = 854.84 ms total, 0.00085484 ms per iteration
ratio = 1.094
Test case: %d %d
snprintf time = 163.596 ms total, 0.000163596 ms per iteration
pg_snprintf time = 134.837 ms total, 0.000134837 ms per iteration
ratio = 0.824
Test case: %10d
snprintf time = 143.684 ms total, 0.000143684 ms per iteration
pg_snprintf time = 91.3437 ms total, 9.13437e-05 ms per iteration
ratio = 0.636
Test case: %s
snprintf time = 307.769 ms total, 0.000307769 ms per iteration
pg_snprintf time = 70.5951 ms total, 7.05951e-05 ms per iteration
ratio = 0.229
Test case: %sx
snprintf time = 309.641 ms total, 0.000309641 ms per iteration
pg_snprintf time = 85.2016 ms total, 8.52016e-05 ms per iteration
ratio = 0.275
Test case: %d 0123456789001234567890012345678900123456789001234567890012345678900123456789001234567890012345678900123456789001234567890012345678900123456789001234567890012345678900123456789001234567890012345678900123456789001234567890
snprintf time = 154.108 ms total, 0.000154108 ms per iteration
pg_snprintf time = 126.425 ms total, 0.000126425 ms per iteration
ratio = 0.820
Test case: %1$d 0123456789001234567890012345678900123456789001234567890012345678900123456789001234567890012345678900123456789001234567890012345678900123456789001234567890012345678900123456789001234567890012345678900123456789001234567890
snprintf time = 239.205 ms total, 0.000239205 ms per iteration
pg_snprintf time = 220.167 ms total, 0.000220167 ms per iteration
ratio = 0.920
new glibc (Fedora 28) vs snprintf.c HEAD:
Test case: %2$.*3$f %1$d
snprintf time = 486.994 ms total, 0.000486994 ms per iteration
pg_snprintf time = 579.769 ms total, 0.000579769 ms per iteration
ratio = 1.191
Test case: %.*g
snprintf time = 472.281 ms total, 0.000472281 ms per iteration
pg_snprintf time = 640.499 ms total, 0.000640499 ms per iteration
ratio = 1.356
Test case: %d %d
snprintf time = 82.5786 ms total, 8.25786e-05 ms per iteration
pg_snprintf time = 82.8582 ms total, 8.28582e-05 ms per iteration
ratio = 1.003
Test case: %10d
snprintf time = 76.3904 ms total, 7.63904e-05 ms per iteration
pg_snprintf time = 68.9414 ms total, 6.89414e-05 ms per iteration
ratio = 0.902
Test case: %s
snprintf time = 7.80277 ms total, 7.80277e-06 ms per iteration
pg_snprintf time = 38.8177 ms total, 3.88177e-05 ms per iteration
ratio = 4.975
Test case: %sx
snprintf time = 58.1852 ms total, 5.81852e-05 ms per iteration
pg_snprintf time = 40.532 ms total, 4.0532e-05 ms per iteration
ratio = 0.697
Test case: %d 0123456789001234567890012345678900123456789001234567890012345678900123456789001234567890012345678900123456789001234567890012345678900123456789001234567890012345678900123456789001234567890012345678900123456789001234567890
snprintf time = 69.3546 ms total, 6.93546e-05 ms per iteration
pg_snprintf time = 578.326 ms total, 0.000578326 ms per iteration
ratio = 8.339
Test case: %1$d 0123456789001234567890012345678900123456789001234567890012345678900123456789001234567890012345678900123456789001234567890012345678900123456789001234567890012345678900123456789001234567890012345678900123456789001234567890
snprintf time = 124.402 ms total, 0.000124402 ms per iteration
pg_snprintf time = 589.417 ms total, 0.000589417 ms per iteration
ratio = 4.738
new glibc (Fedora 28) vs snprintf.c v5 patch:
Test case: %2$.*3$f %1$d
snprintf time = 488.016 ms total, 0.000488016 ms per iteration
pg_snprintf time = 486.862 ms total, 0.000486862 ms per iteration
ratio = 0.998
Test case: %.*g
snprintf time = 472.963 ms total, 0.000472963 ms per iteration
pg_snprintf time = 514.987 ms total, 0.000514987 ms per iteration
ratio = 1.089
Test case: %d %d
snprintf time = 82.7157 ms total, 8.27157e-05 ms per iteration
pg_snprintf time = 80.7262 ms total, 8.07262e-05 ms per iteration
ratio = 0.976
Test case: %10d
snprintf time = 77.337 ms total, 7.7337e-05 ms per iteration
pg_snprintf time = 48.0275 ms total, 4.80275e-05 ms per iteration
ratio = 0.621
Test case: %s
snprintf time = 7.74283 ms total, 7.74283e-06 ms per iteration
pg_snprintf time = 25.7698 ms total, 2.57698e-05 ms per iteration
ratio = 3.328
Test case: %sx
snprintf time = 59.1255 ms total, 5.91255e-05 ms per iteration
pg_snprintf time = 30.5548 ms total, 3.05548e-05 ms per iteration
ratio = 0.517
Test case: %d 0123456789001234567890012345678900123456789001234567890012345678900123456789001234567890012345678900123456789001234567890012345678900123456789001234567890012345678900123456789001234567890012345678900123456789001234567890
snprintf time = 68.0464 ms total, 6.80464e-05 ms per iteration
pg_snprintf time = 55.9024 ms total, 5.59024e-05 ms per iteration
ratio = 0.822
Test case: %1$d 0123456789001234567890012345678900123456789001234567890012345678900123456789001234567890012345678900123456789001234567890012345678900123456789001234567890012345678900123456789001234567890012345678900123456789001234567890
snprintf time = 124.641 ms total, 0.000124641 ms per iteration
pg_snprintf time = 79.1252 ms total, 7.91252e-05 ms per iteration
ratio = 0.635
FreeBSD 11.0 vs snprintf.c HEAD:
Test case: %2$.*3$f %1$d
snprintf time = 592.785 ms total, 0.000592785 ms per iteration
pg_snprintf time = 604.65 ms total, 0.00060465 ms per iteration
ratio = 1.020
Test case: %.*g
snprintf time = 514.67 ms total, 0.00051467 ms per iteration
pg_snprintf time = 822.477 ms total, 0.000822477 ms per iteration
ratio = 1.598
Test case: %d %d
snprintf time = 182.617 ms total, 0.000182617 ms per iteration
pg_snprintf time = 81.1515 ms total, 8.11515e-05 ms per iteration
ratio = 0.444
Test case: %10d
snprintf time = 147.134 ms total, 0.000147134 ms per iteration
pg_snprintf time = 60.1539 ms total, 6.01539e-05 ms per iteration
ratio = 0.409
Test case: %s
snprintf time = 130.103 ms total, 0.000130103 ms per iteration
pg_snprintf time = 65.1186 ms total, 6.51186e-05 ms per iteration
ratio = 0.501
Test case: %sx
snprintf time = 161.099 ms total, 0.000161099 ms per iteration
pg_snprintf time = 67.597 ms total, 6.7597e-05 ms per iteration
ratio = 0.420
Test case: %d 0123456789001234567890012345678900123456789001234567890012345678900123456789001234567890012345678900123456789001234567890012345678900123456789001234567890012345678900123456789001234567890012345678900123456789001234567890
snprintf time = 272.016 ms total, 0.000272016 ms per iteration
pg_snprintf time = 576.049 ms total, 0.000576049 ms per iteration
ratio = 2.118
Test case: %1$d 0123456789001234567890012345678900123456789001234567890012345678900123456789001234567890012345678900123456789001234567890012345678900123456789001234567890012345678900123456789001234567890012345678900123456789001234567890
snprintf time = 377.863 ms total, 0.000377863 ms per iteration
pg_snprintf time = 591.675 ms total, 0.000591675 ms per iteration
ratio = 1.566
FreeBSD 11.0 vs snprintf.c v5 patch:
Test case: %2$.*3$f %1$d
snprintf time = 506.083 ms total, 0.000506083 ms per iteration
pg_snprintf time = 406.696 ms total, 0.000406696 ms per iteration
ratio = 0.804
Test case: %.*g
snprintf time = 516.361 ms total, 0.000516361 ms per iteration
pg_snprintf time = 583.85 ms total, 0.00058385 ms per iteration
ratio = 1.131
Test case: %d %d
snprintf time = 183.341 ms total, 0.000183341 ms per iteration
pg_snprintf time = 115.537 ms total, 0.000115537 ms per iteration
ratio = 0.630
Test case: %10d
snprintf time = 148.071 ms total, 0.000148071 ms per iteration
pg_snprintf time = 77.2526 ms total, 7.72526e-05 ms per iteration
ratio = 0.522
Test case: %s
snprintf time = 130.47 ms total, 0.00013047 ms per iteration
pg_snprintf time = 63.1665 ms total, 6.31665e-05 ms per iteration
ratio = 0.484
Test case: %sx
snprintf time = 161.038 ms total, 0.000161038 ms per iteration
pg_snprintf time = 66.365 ms total, 6.6365e-05 ms per iteration
ratio = 0.412
Test case: %d 0123456789001234567890012345678900123456789001234567890012345678900123456789001234567890012345678900123456789001234567890012345678900123456789001234567890012345678900123456789001234567890012345678900123456789001234567890
snprintf time = 275.3 ms total, 0.0002753 ms per iteration
pg_snprintf time = 169.824 ms total, 0.000169824 ms per iteration
ratio = 0.617
Test case: %1$d 0123456789001234567890012345678900123456789001234567890012345678900123456789001234567890012345678900123456789001234567890012345678900123456789001234567890012345678900123456789001234567890012345678900123456789001234567890
snprintf time = 377.169 ms total, 0.000377169 ms per iteration
pg_snprintf time = 324.277 ms total, 0.000324277 ms per iteration
ratio = 0.860
OpenBSD 6.0 vs snprintf.c HEAD:
Test case: %2$.*3$f %1$d
snprintf time = 366.693 ms total, 0.000366693 ms per iteration
pg_snprintf time = 575.859 ms total, 0.000575859 ms per iteration
ratio = 1.570
Test case: %.*g
snprintf time = 487.42 ms total, 0.00048742 ms per iteration
pg_snprintf time = 783.286 ms total, 0.000783286 ms per iteration
ratio = 1.607
Test case: %d %d
snprintf time = 175.423 ms total, 0.000175423 ms per iteration
pg_snprintf time = 95.583 ms total, 9.5583e-05 ms per iteration
ratio = 0.545
Test case: %10d
snprintf time = 145.509 ms total, 0.000145509 ms per iteration
pg_snprintf time = 80.7363 ms total, 8.07363e-05 ms per iteration
ratio = 0.555
Test case: %s
snprintf time = 160.49 ms total, 0.00016049 ms per iteration
pg_snprintf time = 69.3179 ms total, 6.93179e-05 ms per iteration
ratio = 0.432
Test case: %sx
snprintf time = 179.017 ms total, 0.000179017 ms per iteration
pg_snprintf time = 72.7243 ms total, 7.27243e-05 ms per iteration
ratio = 0.406
Test case: %d 0123456789001234567890012345678900123456789001234567890012345678900123456789001234567890012345678900123456789001234567890012345678900123456789001234567890012345678900123456789001234567890012345678900123456789001234567890
snprintf time = 893.578 ms total, 0.000893578 ms per iteration
pg_snprintf time = 721.957 ms total, 0.000721957 ms per iteration
ratio = 0.808
Test case: %1$d 0123456789001234567890012345678900123456789001234567890012345678900123456789001234567890012345678900123456789001234567890012345678900123456789001234567890012345678900123456789001234567890012345678900123456789001234567890
snprintf time = 1696.39 ms total, 0.00169639 ms per iteration
pg_snprintf time = 731.144 ms total, 0.000731144 ms per iteration
ratio = 0.431
OpenBSD 6.0 vs snprintf.c v5 patch:
Test case: %2$.*3$f %1$d
snprintf time = 368.158 ms total, 0.000368158 ms per iteration
pg_snprintf time = 408.526 ms total, 0.000408526 ms per iteration
ratio = 1.110
Test case: %.*g
snprintf time = 486.741 ms total, 0.000486741 ms per iteration
pg_snprintf time = 548.396 ms total, 0.000548396 ms per iteration
ratio = 1.127
Test case: %d %d
snprintf time = 175.101 ms total, 0.000175101 ms per iteration
pg_snprintf time = 123.1 ms total, 0.0001231 ms per iteration
ratio = 0.703
Test case: %10d
snprintf time = 145.661 ms total, 0.000145661 ms per iteration
pg_snprintf time = 82.1964 ms total, 8.21964e-05 ms per iteration
ratio = 0.564
Test case: %s
snprintf time = 160.213 ms total, 0.000160213 ms per iteration
pg_snprintf time = 62.7285 ms total, 6.27285e-05 ms per iteration
ratio = 0.392
Test case: %sx
snprintf time = 178.462 ms total, 0.000178462 ms per iteration
pg_snprintf time = 66.0236 ms total, 6.60236e-05 ms per iteration
ratio = 0.370
Test case: %d 0123456789001234567890012345678900123456789001234567890012345678900123456789001234567890012345678900123456789001234567890012345678900123456789001234567890012345678900123456789001234567890012345678900123456789001234567890
snprintf time = 892.687 ms total, 0.000892687 ms per iteration
pg_snprintf time = 183.339 ms total, 0.000183339 ms per iteration
ratio = 0.205
Test case: %1$d 0123456789001234567890012345678900123456789001234567890012345678900123456789001234567890012345678900123456789001234567890012345678900123456789001234567890012345678900123456789001234567890012345678900123456789001234567890
snprintf time = 1672.81 ms total, 0.00167281 ms per iteration
pg_snprintf time = 218.761 ms total, 0.000218761 ms per iteration
ratio = 0.131
macOS Mojave vs snprintf.c HEAD:
Test case: %2$.*3$f %1$d
snprintf time = 215.462 ms total, 0.000215462 ms per iteration
pg_snprintf time = 301.006 ms total, 0.000301006 ms per iteration
ratio = 1.397
Test case: %.*g
snprintf time = 329.865 ms total, 0.000329865 ms per iteration
pg_snprintf time = 501.367 ms total, 0.000501367 ms per iteration
ratio = 1.520
Test case: %d %d
snprintf time = 83.8561 ms total, 8.38561e-05 ms per iteration
pg_snprintf time = 65.7689 ms total, 6.57689e-05 ms per iteration
ratio = 0.784
Test case: %10d
snprintf time = 65.5346 ms total, 6.55346e-05 ms per iteration
pg_snprintf time = 59.9587 ms total, 5.99587e-05 ms per iteration
ratio = 0.915
Test case: %s
snprintf time = 67.0085 ms total, 6.70085e-05 ms per iteration
pg_snprintf time = 32.315 ms total, 3.2315e-05 ms per iteration
ratio = 0.482
Test case: %sx
snprintf time = 78.1157 ms total, 7.81157e-05 ms per iteration
pg_snprintf time = 32.7136 ms total, 3.27136e-05 ms per iteration
ratio = 0.419
Test case: %d 0123456789001234567890012345678900123456789001234567890012345678900123456789001234567890012345678900123456789001234567890012345678900123456789001234567890012345678900123456789001234567890012345678900123456789001234567890
snprintf time = 172.349 ms total, 0.000172349 ms per iteration
pg_snprintf time = 412.957 ms total, 0.000412957 ms per iteration
ratio = 2.396
Test case: %1$d 0123456789001234567890012345678900123456789001234567890012345678900123456789001234567890012345678900123456789001234567890012345678900123456789001234567890012345678900123456789001234567890012345678900123456789001234567890
snprintf time = 303.392 ms total, 0.000303392 ms per iteration
pg_snprintf time = 427.246 ms total, 0.000427246 ms per iteration
ratio = 1.408
macOS Mojave vs snprintf.c v5 patch:
Test case: %2$.*3$f %1$d
snprintf time = 212.13 ms total, 0.00021213 ms per iteration
pg_snprintf time = 244.277 ms total, 0.000244277 ms per iteration
ratio = 1.152
Test case: %.*g
snprintf time = 337.137 ms total, 0.000337137 ms per iteration
pg_snprintf time = 371.386 ms total, 0.000371386 ms per iteration
ratio = 1.102
Test case: %d %d
snprintf time = 80.876 ms total, 8.0876e-05 ms per iteration
pg_snprintf time = 64.8164 ms total, 6.48164e-05 ms per iteration
ratio = 0.801
Test case: %10d
snprintf time = 60.1313 ms total, 6.01313e-05 ms per iteration
pg_snprintf time = 40.7794 ms total, 4.07794e-05 ms per iteration
ratio = 0.678
Test case: %s
snprintf time = 61.55 ms total, 6.155e-05 ms per iteration
pg_snprintf time = 24.1078 ms total, 2.41078e-05 ms per iteration
ratio = 0.392
Test case: %sx
snprintf time = 71.2324 ms total, 7.12324e-05 ms per iteration
pg_snprintf time = 35.2292 ms total, 3.52292e-05 ms per iteration
ratio = 0.495
Test case: %d 0123456789001234567890012345678900123456789001234567890012345678900123456789001234567890012345678900123456789001234567890012345678900123456789001234567890012345678900123456789001234567890012345678900123456789001234567890
snprintf time = 149.266 ms total, 0.000149266 ms per iteration
pg_snprintf time = 101.321 ms total, 0.000101321 ms per iteration
ratio = 0.679
Test case: %1$d 0123456789001234567890012345678900123456789001234567890012345678900123456789001234567890012345678900123456789001234567890012345678900123456789001234567890012345678900123456789001234567890012345678900123456789001234567890
snprintf time = 306.861 ms total, 0.000306861 ms per iteration
pg_snprintf time = 122.933 ms total, 0.000122933 ms per iteration
ratio = 0.401
timeprintf.c (text/x-c, 2.2K) download
#include "postgres_fe.h"
#include "portability/instr_time.h"
#include "snprintf.c"
#define TIMETEST(fmtstr,...) \
printf("Test case: %s\n", fmtstr); \
INSTR_TIME_SET_CURRENT(start); \
for (i = 0; i < count; i++) \
snprintf(buffer, sizeof(buffer), fmtstr, __VA_ARGS__); \
INSTR_TIME_SET_CURRENT(stop); \
INSTR_TIME_SUBTRACT(stop, start); \
elapsed = INSTR_TIME_GET_MILLISEC(stop); \
printf("snprintf time = %g ms total, %g ms per iteration\n", \
elapsed, elapsed / count); \
INSTR_TIME_SET_CURRENT(start); \
for (i = 0; i < count; i++) \
pg_snprintf(buffer, sizeof(buffer), fmtstr, __VA_ARGS__); \
INSTR_TIME_SET_CURRENT(stop); \
INSTR_TIME_SUBTRACT(stop, start); \
elapsed2 = INSTR_TIME_GET_MILLISEC(stop); \
printf("pg_snprintf time = %g ms total, %g ms per iteration\n", \
elapsed2, elapsed2 / count); \
printf("ratio = %.3f\n\n", elapsed2 / elapsed)
int
main(int argc, char **argv)
{
int count = 0;
char buffer[1000];
instr_time start;
instr_time stop;
double elapsed;
double elapsed2;
int i;
if (argc > 1)
count = atoi(argv[1]);
if (count <= 0)
count = 1000000;
TIMETEST("%2$.*3$f %1$d", 42, 123.456, 2);
TIMETEST("%.*g", 15, 123.456);
TIMETEST("%d %d", 15, 16);
TIMETEST("%10d", 15);
TIMETEST("%s",
"0123456789001234567890012345678900123456789001234567890012345678900123456789001234567890012345678900123456789001234567890012345678900123456789001234567890012345678900123456789001234567890012345678900123456789001234567890");
TIMETEST("%sx",
"0123456789001234567890012345678900123456789001234567890012345678900123456789001234567890012345678900123456789001234567890012345678900123456789001234567890012345678900123456789001234567890012345678900123456789001234567890");
TIMETEST("%d 0123456789001234567890012345678900123456789001234567890012345678900123456789001234567890012345678900123456789001234567890012345678900123456789001234567890012345678900123456789001234567890012345678900123456789001234567890",
42);
TIMETEST("%1$d 0123456789001234567890012345678900123456789001234567890012345678900123456789001234567890012345678900123456789001234567890012345678900123456789001234567890012345678900123456789001234567890012345678900123456789001234567890",
42);
return 0;
}
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Reply to all the recipients using the --to and --cc options:
reply via email
To: pgsql-hackers@postgresql.org
Cc: tgl@sss.pgh.pa.us, andres@anarazel.de, thomas.munro@enterprisedb.com, andrew@tao11.riddles.org.uk, a.kuzmenkov@postgrespro.ru
Subject: Re: Performance improvements for src/port/snprintf.c
In-Reply-To: <30551.1538517271@sss.pgh.pa.us>
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
This inbox is served by DDX for PostgreSQL; see mirroring instructions
for how to clone and mirror all data and code used for this inbox