Skip to content
Merged
Show file tree
Hide file tree
Changes from 6 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Improve performance of ``PyLongObject`` conversion method ``PyLong_AsLongAndOverflow``.
Comment thread
vstinner marked this conversation as resolved.
Outdated
76 changes: 48 additions & 28 deletions Objects/longobject.c
Original file line number Diff line number Diff line change
Expand Up @@ -498,6 +498,26 @@ PyLong_FromDouble(double dval)
#define PY_ABS_LONG_MIN (0-(unsigned long)LONG_MIN)
#define PY_ABS_SSIZE_T_MIN (0-(size_t)PY_SSIZE_T_MIN)

static inline unsigned long
_unroll_digits(PyLongObject *v, Py_ssize_t *i)
Comment thread
eendebakpt marked this conversation as resolved.
Outdated
{
digit *digits = v->long_value.ob_digit;
Comment thread
vstinner marked this conversation as resolved.
Outdated
assert(*i >= 2);
/* unroll 1 digit */
--(*i);
assert(ULONG_MAX >= ((1UL << PyLong_SHIFT) - 1));
unsigned long x = digits[*i];

#if ((ULONG_MAX >> PyLong_SHIFT)) >= ((1UL << PyLong_SHIFT) - 1)
/* unroll another digit */
x <<= PyLong_SHIFT;
--(*i);
x |= digits[*i];
#endif

return x;
Comment thread
eendebakpt marked this conversation as resolved.
}

/* Get a C long int from an int object or any object that has an __index__
method.

Expand All @@ -507,13 +527,12 @@ PyLong_FromDouble(double dval)
For other errors (e.g., TypeError), return -1 and set an error condition.
In this case *overflow will be 0.
*/

long
PyLong_AsLongAndOverflow(PyObject *vv, int *overflow)
{
/* This version by Tim Peters */
/* This version originally by Tim Peters */
PyLongObject *v;
unsigned long x, prev;
unsigned long x;
long res;
Py_ssize_t i;
int sign;
Expand Down Expand Up @@ -556,14 +575,14 @@ PyLong_AsLongAndOverflow(PyObject *vv, int *overflow)
res = -1;
i = _PyLong_DigitCount(v);
sign = _PyLong_NonCompactSign(v);
x = 0;

x = _unroll_digits(v, &i);
while (--i >= 0) {
prev = x;
x = (x << PyLong_SHIFT) | v->long_value.ob_digit[i];
if ((x >> PyLong_SHIFT) != prev) {
if (x > SIZE_MAX >> PyLong_SHIFT) {
Comment thread
eendebakpt marked this conversation as resolved.
Outdated
*overflow = sign;
goto exit;
}
x = (x << PyLong_SHIFT) | v->long_value.ob_digit[i];
}
/* Haven't lost any bits, but casting to long requires extra
* care (see comment above).
Expand Down Expand Up @@ -627,7 +646,7 @@ PyLong_AsInt(PyObject *obj)
Py_ssize_t
PyLong_AsSsize_t(PyObject *vv) {
PyLongObject *v;
size_t x, prev;
size_t x;
Py_ssize_t i;
int sign;

Expand All @@ -646,12 +665,13 @@ PyLong_AsSsize_t(PyObject *vv) {
}
i = _PyLong_DigitCount(v);
sign = _PyLong_NonCompactSign(v);
x = 0;

x = _unroll_digits(v, &i);
while (--i >= 0) {
prev = x;
x = (x << PyLong_SHIFT) | v->long_value.ob_digit[i];
if ((x >> PyLong_SHIFT) != prev)
if (x > SIZE_MAX >> PyLong_SHIFT) {
Comment thread
eendebakpt marked this conversation as resolved.
Outdated
goto overflow;
}
x = (x << PyLong_SHIFT) | v->long_value.ob_digit[i];
}
/* Haven't lost any bits, but casting to a signed type requires
* extra care (see comment above).
Expand All @@ -677,7 +697,7 @@ unsigned long
PyLong_AsUnsignedLong(PyObject *vv)
{
PyLongObject *v;
unsigned long x, prev;
unsigned long x;
Py_ssize_t i;

if (vv == NULL) {
Expand Down Expand Up @@ -708,13 +728,13 @@ PyLong_AsUnsignedLong(PyObject *vv)
return (unsigned long) -1;
}
i = _PyLong_DigitCount(v);
x = 0;

x = _unroll_digits(v, &i);
while (--i >= 0) {
prev = x;
x = (x << PyLong_SHIFT) | v->long_value.ob_digit[i];
if ((x >> PyLong_SHIFT) != prev) {
if (x > SIZE_MAX >> PyLong_SHIFT) {
goto overflow;
}
x = (x << PyLong_SHIFT) | v->long_value.ob_digit[i];
}
return x;
overflow:
Expand All @@ -731,7 +751,7 @@ size_t
PyLong_AsSize_t(PyObject *vv)
{
PyLongObject *v;
size_t x, prev;
size_t x;
Py_ssize_t i;

if (vv == NULL) {
Expand All @@ -753,16 +773,16 @@ PyLong_AsSize_t(PyObject *vv)
return (size_t) -1;
}
i = _PyLong_DigitCount(v);
x = 0;

x = _unroll_digits(v, &i);
while (--i >= 0) {
prev = x;
x = (x << PyLong_SHIFT) | v->long_value.ob_digit[i];
if ((x >> PyLong_SHIFT) != prev) {
PyErr_SetString(PyExc_OverflowError,
"Python int too large to convert to C size_t");
return (size_t) -1;
if (x > SIZE_MAX >> PyLong_SHIFT) {
Comment thread
eendebakpt marked this conversation as resolved.
Outdated
PyErr_SetString(PyExc_OverflowError,
"Python int too large to convert to C size_t");
return (size_t) -1;
}
x = (x << PyLong_SHIFT) | v->long_value.ob_digit[i];
}
}
return x;
}

Expand Down Expand Up @@ -790,7 +810,7 @@ _PyLong_AsUnsignedLongMask(PyObject *vv)
}
i = _PyLong_DigitCount(v);
int sign = _PyLong_NonCompactSign(v);
x = 0;
x = _unroll_digits(v, &i);
while (--i >= 0) {
x = (x << PyLong_SHIFT) | v->long_value.ob_digit[i];
}
Expand Down Expand Up @@ -1609,7 +1629,7 @@ _PyLong_AsUnsignedLongLongMask(PyObject *vv)
}
i = _PyLong_DigitCount(v);
sign = _PyLong_NonCompactSign(v);
x = 0;
x = _unroll_digits(v, &i);
while (--i >= 0) {
x = (x << PyLong_SHIFT) | v->long_value.ob_digit[i];
}
Expand Down
Loading