Thanks to visit codestin.com
Credit goes to github.com

Skip to content

bpo-37907: Slightly improve performance of PyLong_AsSsize_t() with large longs #15363

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 2 commits into from
Closed
Show file tree
Hide file tree
Changes from all 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,3 @@
Slightly improve performance of :c:func:`PyLong_AsSsize_t`,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is a completely trivial internal change. I don't think it needs a NEWS entry.

:c:func:`PyLong_AsSize_t`, :c:func:`PyLong_AsLong` and
:c:func:`PyLong_AsUnsignedLong` with large longs. Patch by Sergey Fedoseev.
29 changes: 13 additions & 16 deletions Objects/longobject.c
Original file line number Diff line number Diff line change
Expand Up @@ -484,7 +484,7 @@ PyLong_AsLongAndOverflow(PyObject *vv, int *overflow)
{
/* This version by Tim Peters */
PyLongObject *v;
unsigned long x, prev;
unsigned long x;
long res;
Py_ssize_t i;
int sign;
Expand Down Expand Up @@ -527,12 +527,11 @@ PyLong_AsLongAndOverflow(PyObject *vv, int *overflow)
i = -(i);
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would it be possible to start at "x = v->ob_digit[0];" and "i=1;" to avoid starting the loop with x=0 which means one useless if at the first iteration.

With 64-bit unsigned long and PyLong_SHIFT, we can even combine two digits without having to check for overflow, no?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we can even combine two digits without having to check for overflow, no?

Something like:

/* in the default: case, i >= 2 */
assert(i >= 2);
#if ((ULONG_MAX >> PyLong_SHIFT)) >= ((1UL << PyLong_SHIFT) - 1)
  /* use 2 digits */
  --i;
  x= digit[i];
  x <<=PyLong_SHIFT;
  --i;
  x |= digit[i];
#else
  /* use 1 digit */
  --i
  assert(ULONG_MAX >= ((1UL << PyLong_SHIFT) - 1);
  x= digit[i];
#endif
while (--i >= 0) { ... }

while (--i >= 0) {
prev = x;
x = (x << PyLong_SHIFT) | v->ob_digit[i];
if ((x >> PyLong_SHIFT) != prev) {
if (x > ULONG_MAX >> PyLong_SHIFT) {
*overflow = sign;
goto exit;
}
x = (x << PyLong_SHIFT) | v->ob_digit[i];
}
/* Haven't lost any bits, but casting to long requires extra
* care (see comment above).
Expand Down Expand Up @@ -596,7 +595,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 @@ -623,10 +622,10 @@ PyLong_AsSsize_t(PyObject *vv) {
i = -(i);
}
while (--i >= 0) {
prev = x;
x = (x << PyLong_SHIFT) | v->ob_digit[i];
if ((x >> PyLong_SHIFT) != prev)
if (x > SIZE_MAX >> PyLong_SHIFT) {
goto overflow;
}
x = (x << PyLong_SHIFT) | v->ob_digit[i];
}
/* Haven't lost any bits, but casting to a signed type requires
* extra care (see comment above).
Expand All @@ -652,7 +651,7 @@ unsigned long
PyLong_AsUnsignedLong(PyObject *vv)
{
PyLongObject *v;
unsigned long x, prev;
unsigned long x;
Py_ssize_t i;

if (vv == NULL) {
Expand All @@ -677,14 +676,13 @@ PyLong_AsUnsignedLong(PyObject *vv)
case 1: return v->ob_digit[0];
}
while (--i >= 0) {
prev = x;
x = (x << PyLong_SHIFT) | v->ob_digit[i];
if ((x >> PyLong_SHIFT) != prev) {
if (x > ULONG_MAX >> PyLong_SHIFT) {
PyErr_SetString(PyExc_OverflowError,
"Python int too large to convert "
"to C unsigned long");
return (unsigned long) -1;
}
x = (x << PyLong_SHIFT) | v->ob_digit[i];
}
return x;
}
Expand All @@ -696,7 +694,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 @@ -721,13 +719,12 @@ PyLong_AsSize_t(PyObject *vv)
case 1: return v->ob_digit[0];
}
while (--i >= 0) {
prev = x;
x = (x << PyLong_SHIFT) | v->ob_digit[i];
if ((x >> PyLong_SHIFT) != prev) {
if (x > SIZE_MAX >> PyLong_SHIFT) {
PyErr_SetString(PyExc_OverflowError,
"Python int too large to convert to C size_t");
return (size_t) -1;
}
x = (x << PyLong_SHIFT) | v->ob_digit[i];
}
return x;
}
Expand Down