Re: Performance improvements for src/port/snprintf.c - Mailing list pgsql-hackers
From | Tom Lane |
---|---|
Subject | Re: Performance improvements for src/port/snprintf.c |
Date | |
Msg-id | 30551.1538517271@sss.pgh.pa.us Whole thread Raw |
In response to | Re: Performance improvements for src/port/snprintf.c (Andres Freund <andres@anarazel.de>) |
Responses |
Re: Performance improvements for src/port/snprintf.c
Re: Performance improvements for src/port/snprintf.c |
List | pgsql-hackers |
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 diff --git a/configure b/configure index 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 ppollpstat pthread_is_threaded_np readlink setproctitle setproctitle_fast setsid shm_open symlink sync_file_range utime utimeswcstombs_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 ppollpstat pthread_is_threaded_np readlink setproctitle setproctitle_fast setsid shm_open strchrnul symlink sync_file_rangeutime 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.in index 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_fallocateppoll pstat pthread_is_threaded_np readlink setproctitle setproctitle_fast setsid shm_open symlink sync_file_rangeutime 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_fallocateppoll pstat pthread_is_threaded_np readlink setproctitle setproctitle_fast setsid shm_open strchrnul symlinksync_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.in index 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.win32 index 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.c index 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); } diff --git a/src/backend/utils/adt/float.c b/src/backend/utils/adt/float.c index 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 } } 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 #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; }
pgsql-hackers by date: