mirror of https://github.com/python/cpython
Issue #6713: Improve performance of str(n) and repr(n) for integers n
(up to 3.1 times faster in tests), by special-casing base 10 in _PyLong_Format.
This commit is contained in:
parent
3360401980
commit
0a1efd0e4c
|
@ -50,12 +50,16 @@ typedef PY_INT32_T sdigit; /* signed variant of digit */
|
||||||
typedef PY_UINT64_T twodigits;
|
typedef PY_UINT64_T twodigits;
|
||||||
typedef PY_INT64_T stwodigits; /* signed variant of twodigits */
|
typedef PY_INT64_T stwodigits; /* signed variant of twodigits */
|
||||||
#define PyLong_SHIFT 30
|
#define PyLong_SHIFT 30
|
||||||
|
#define _PyLong_DECIMAL_SHIFT 9 /* max(e such that 10**e fits in a digit) */
|
||||||
|
#define _PyLong_DECIMAL_BASE ((digit)1000000000) /* 10 ** DECIMAL_SHIFT */
|
||||||
#elif PYLONG_BITS_IN_DIGIT == 15
|
#elif PYLONG_BITS_IN_DIGIT == 15
|
||||||
typedef unsigned short digit;
|
typedef unsigned short digit;
|
||||||
typedef short sdigit; /* signed variant of digit */
|
typedef short sdigit; /* signed variant of digit */
|
||||||
typedef unsigned long twodigits;
|
typedef unsigned long twodigits;
|
||||||
typedef long stwodigits; /* signed variant of twodigits */
|
typedef long stwodigits; /* signed variant of twodigits */
|
||||||
#define PyLong_SHIFT 15
|
#define PyLong_SHIFT 15
|
||||||
|
#define _PyLong_DECIMAL_SHIFT 4 /* max(e such that 10**e fits in a digit) */
|
||||||
|
#define _PyLong_DECIMAL_BASE ((digit)10000) /* 10 ** DECIMAL_SHIFT */
|
||||||
#else
|
#else
|
||||||
#error "PYLONG_BITS_IN_DIGIT should be 15 or 30"
|
#error "PYLONG_BITS_IN_DIGIT should be 15 or 30"
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -12,6 +12,8 @@ What's New in Python 3.2 Alpha 1?
|
||||||
Core and Builtins
|
Core and Builtins
|
||||||
-----------------
|
-----------------
|
||||||
|
|
||||||
|
- Issue #6713: Improve performance of integer -> string conversions.
|
||||||
|
|
||||||
- Issue #6846: Fix bug where bytearray.pop() returns negative integers.
|
- Issue #6846: Fix bug where bytearray.pop() returns negative integers.
|
||||||
|
|
||||||
- Issue #6750: A text file opened with io.open() could duplicate its output
|
- Issue #6750: A text file opened with io.open() could duplicate its output
|
||||||
|
|
|
@ -1650,6 +1650,119 @@ divrem1(PyLongObject *a, digit n, digit *prem)
|
||||||
return long_normalize(z);
|
return long_normalize(z);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Convert a long integer to a base 10 string. Returns a new non-shared
|
||||||
|
string. (Return value is non-shared so that callers can modify the
|
||||||
|
returned value if necessary.) */
|
||||||
|
|
||||||
|
static PyObject *
|
||||||
|
long_to_decimal_string(PyObject *aa)
|
||||||
|
{
|
||||||
|
PyLongObject *scratch, *a;
|
||||||
|
PyObject *str;
|
||||||
|
Py_ssize_t size, strlen, size_a, i, j;
|
||||||
|
digit *pout, *pin, rem, tenpow;
|
||||||
|
Py_UNICODE *p;
|
||||||
|
int negative;
|
||||||
|
|
||||||
|
a = (PyLongObject *)aa;
|
||||||
|
if (a == NULL || !PyLong_Check(a)) {
|
||||||
|
PyErr_BadInternalCall();
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
size_a = ABS(Py_SIZE(a));
|
||||||
|
negative = Py_SIZE(a) < 0;
|
||||||
|
|
||||||
|
/* quick and dirty upper bound for the number of digits
|
||||||
|
required to express a in base _PyLong_DECIMAL_BASE:
|
||||||
|
|
||||||
|
#digits = 1 + floor(log2(a) / log2(_PyLong_DECIMAL_BASE))
|
||||||
|
|
||||||
|
But log2(a) < size_a * PyLong_SHIFT, and
|
||||||
|
log2(_PyLong_DECIMAL_BASE) = log2(10) * _PyLong_DECIMAL_SHIFT
|
||||||
|
> 3 * _PyLong_DECIMAL_SHIFT
|
||||||
|
*/
|
||||||
|
if (size_a > PY_SSIZE_T_MAX / PyLong_SHIFT) {
|
||||||
|
PyErr_SetString(PyExc_OverflowError,
|
||||||
|
"long is too large to format");
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
/* the expression size_a * PyLong_SHIFT is now safe from overflow */
|
||||||
|
size = 1 + size_a * PyLong_SHIFT / (3 * _PyLong_DECIMAL_SHIFT);
|
||||||
|
scratch = _PyLong_New(size);
|
||||||
|
if (scratch == NULL)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
/* convert array of base _PyLong_BASE digits in pin to an array of
|
||||||
|
base _PyLong_DECIMAL_BASE digits in pout, following Knuth (TAOCP,
|
||||||
|
Volume 2 (3rd edn), section 4.4, Method 1b). */
|
||||||
|
pin = a->ob_digit;
|
||||||
|
pout = scratch->ob_digit;
|
||||||
|
size = 0;
|
||||||
|
for (i = size_a; --i >= 0; ) {
|
||||||
|
digit hi = pin[i];
|
||||||
|
for (j = 0; j < size; j++) {
|
||||||
|
twodigits z = (twodigits)pout[j] << PyLong_SHIFT | hi;
|
||||||
|
hi = z / _PyLong_DECIMAL_BASE;
|
||||||
|
pout[j] = z - (twodigits)hi * _PyLong_DECIMAL_BASE;
|
||||||
|
}
|
||||||
|
while (hi) {
|
||||||
|
pout[size++] = hi % _PyLong_DECIMAL_BASE;
|
||||||
|
hi /= _PyLong_DECIMAL_BASE;
|
||||||
|
}
|
||||||
|
/* check for keyboard interrupt */
|
||||||
|
SIGCHECK({
|
||||||
|
Py_DECREF(scratch);
|
||||||
|
return NULL;
|
||||||
|
})
|
||||||
|
}
|
||||||
|
/* pout should have at least one digit, so that the case when a = 0
|
||||||
|
works correctly */
|
||||||
|
if (size == 0)
|
||||||
|
pout[size++] = 0;
|
||||||
|
|
||||||
|
/* calculate exact length of output string, and allocate */
|
||||||
|
strlen = negative + 1 + (size - 1) * _PyLong_DECIMAL_SHIFT;
|
||||||
|
tenpow = 10;
|
||||||
|
rem = pout[size-1];
|
||||||
|
while (rem >= tenpow) {
|
||||||
|
tenpow *= 10;
|
||||||
|
strlen++;
|
||||||
|
}
|
||||||
|
str = PyUnicode_FromUnicode(NULL, strlen);
|
||||||
|
if (str == NULL) {
|
||||||
|
Py_DECREF(scratch);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* fill the string right-to-left */
|
||||||
|
p = PyUnicode_AS_UNICODE(str) + strlen;
|
||||||
|
*p = '\0';
|
||||||
|
/* pout[0] through pout[size-2] contribute exactly
|
||||||
|
_PyLong_DECIMAL_SHIFT digits each */
|
||||||
|
for (i=0; i < size - 1; i++) {
|
||||||
|
rem = pout[i];
|
||||||
|
for (j = 0; j < _PyLong_DECIMAL_SHIFT; j++) {
|
||||||
|
*--p = '0' + rem % 10;
|
||||||
|
rem /= 10;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/* pout[size-1]: always produce at least one decimal digit */
|
||||||
|
rem = pout[i];
|
||||||
|
do {
|
||||||
|
*--p = '0' + rem % 10;
|
||||||
|
rem /= 10;
|
||||||
|
} while (rem != 0);
|
||||||
|
|
||||||
|
/* and sign */
|
||||||
|
if (negative)
|
||||||
|
*--p = '-';
|
||||||
|
|
||||||
|
/* check we've counted correctly */
|
||||||
|
assert(p == PyUnicode_AS_UNICODE(str));
|
||||||
|
Py_DECREF(scratch);
|
||||||
|
return (PyObject *)str;
|
||||||
|
}
|
||||||
|
|
||||||
/* Convert a long int object to a string, using a given conversion base.
|
/* Convert a long int object to a string, using a given conversion base.
|
||||||
Return a string object.
|
Return a string object.
|
||||||
If base is 2, 8 or 16, add the proper prefix '0b', '0o' or '0x'. */
|
If base is 2, 8 or 16, add the proper prefix '0b', '0o' or '0x'. */
|
||||||
|
@ -1665,6 +1778,9 @@ _PyLong_Format(PyObject *aa, int base)
|
||||||
int bits;
|
int bits;
|
||||||
char sign = '\0';
|
char sign = '\0';
|
||||||
|
|
||||||
|
if (base == 10)
|
||||||
|
return long_to_decimal_string((PyObject *)a);
|
||||||
|
|
||||||
if (a == NULL || !PyLong_Check(a)) {
|
if (a == NULL || !PyLong_Check(a)) {
|
||||||
PyErr_BadInternalCall();
|
PyErr_BadInternalCall();
|
||||||
return NULL;
|
return NULL;
|
||||||
|
|
Loading…
Reference in New Issue