From aa2387e2fd532954e88dfd8546ab894b9305123d Mon Sep 17 00:00:00 2001 From: Tom Lane Date: Sat, 6 Feb 2016 23:11:28 -0500 Subject: Improve speed of timestamp/time/date output functions. It seems that sprintf(), at least in glibc's version, is unreasonably slow compared to hand-rolled code for printing integers. Replacing most uses of sprintf() in the datetime.c output functions with special-purpose code turns out to give more than a 2X speedup in COPY of a table with a single timestamp column; which is pretty impressive considering all the other logic in that code path. David Rowley and Andres Freund, reviewed by Peter Geoghegan and myself --- src/backend/utils/adt/numutils.c | 161 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 161 insertions(+) (limited to 'src/backend/utils/adt/numutils.c') diff --git a/src/backend/utils/adt/numutils.c b/src/backend/utils/adt/numutils.c index 880d304fb52..6b105964bd1 100644 --- a/src/backend/utils/adt/numutils.c +++ b/src/backend/utils/adt/numutils.c @@ -227,3 +227,164 @@ pg_lltoa(int64 value, char *a) *a-- = swap; } } + + +/* + * pg_ltostr_zeropad + * Converts 'value' into a decimal string representation stored at 'str'. + * 'minwidth' specifies the minimum width of the result; any extra space + * is filled up by prefixing the number with zeros. + * + * Returns the ending address of the string result (the last character written + * plus 1). Note that no NUL terminator is written. + * + * The intended use-case for this function is to build strings that contain + * multiple individual numbers, for example: + * + * str = pg_ltostr_zeropad(str, hours, 2); + * *str++ = ':'; + * str = pg_ltostr_zeropad(str, mins, 2); + * *str++ = ':'; + * str = pg_ltostr_zeropad(str, secs, 2); + * *str = '\0'; + * + * Note: Caller must ensure that 'str' points to enough memory to hold the + * result. + */ +char * +pg_ltostr_zeropad(char *str, int32 value, int32 minwidth) +{ + char *start = str; + char *end = &str[minwidth]; + int32 num = value; + + Assert(minwidth > 0); + + /* + * Handle negative numbers in a special way. We can't just write a '-' + * prefix and reverse the sign as that would overflow for INT32_MIN. + */ + if (num < 0) + { + *start++ = '-'; + minwidth--; + + /* + * Build the number starting at the last digit. Here remainder will + * be a negative number, so we must reverse the sign before adding '0' + * in order to get the correct ASCII digit. + */ + while (minwidth--) + { + int32 oldval = num; + int32 remainder; + + num /= 10; + remainder = oldval - num * 10; + start[minwidth] = '0' - remainder; + } + } + else + { + /* Build the number starting at the last digit */ + while (minwidth--) + { + int32 oldval = num; + int32 remainder; + + num /= 10; + remainder = oldval - num * 10; + start[minwidth] = '0' + remainder; + } + } + + /* + * If minwidth was not high enough to fit the number then num won't have + * been divided down to zero. We punt the problem to pg_ltostr(), which + * will generate a correct answer in the minimum valid width. + */ + if (num != 0) + return pg_ltostr(str, value); + + /* Otherwise, return last output character + 1 */ + return end; +} + +/* + * pg_ltostr + * Converts 'value' into a decimal string representation stored at 'str'. + * + * Returns the ending address of the string result (the last character written + * plus 1). Note that no NUL terminator is written. + * + * The intended use-case for this function is to build strings that contain + * multiple individual numbers, for example: + * + * str = pg_ltostr(str, a); + * *str++ = ' '; + * str = pg_ltostr(str, b); + * *str = '\0'; + * + * Note: Caller must ensure that 'str' points to enough memory to hold the + * result. + */ +char * +pg_ltostr(char *str, int32 value) +{ + char *start; + char *end; + + /* + * Handle negative numbers in a special way. We can't just write a '-' + * prefix and reverse the sign as that would overflow for INT32_MIN. + */ + if (value < 0) + { + *str++ = '-'; + + /* Mark the position we must reverse the string from. */ + start = str; + + /* Compute the result string backwards. */ + do + { + int32 oldval = value; + int32 remainder; + + value /= 10; + remainder = oldval - value * 10; + /* As above, we expect remainder to be negative. */ + *str++ = '0' - remainder; + } while (value != 0); + } + else + { + /* Mark the position we must reverse the string from. */ + start = str; + + /* Compute the result string backwards. */ + do + { + int32 oldval = value; + int32 remainder; + + value /= 10; + remainder = oldval - value * 10; + *str++ = '0' + remainder; + } while (value != 0); + } + + /* Remember the end+1 and back up 'str' to the last character. */ + end = str--; + + /* Reverse string. */ + while (start < str) + { + char swap = *start; + + *start++ = *str; + *str-- = swap; + } + + return end; +} -- cgit v1.2.3