Commit aa2387e2 authored by Tom Lane's avatar Tom Lane

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
parent b921aeb1
This diff is collapsed.
......@@ -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;
}
......@@ -290,6 +290,8 @@ extern int32 pg_atoi(const char *s, int size, int c);
extern void pg_itoa(int16 i, char *a);
extern void pg_ltoa(int32 l, char *a);
extern void pg_lltoa(int64 ll, char *a);
extern char *pg_ltostr_zeropad(char *str, int32 value, int32 minwidth);
extern char *pg_ltostr(char *str, int32 value);
/*
* Per-opclass comparison functions for new btrees. These are
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment