gh-129149: Add fast path for medium-sized integers in PyLong_FromSsize_t()
(#129301)
The implementation of `PyLong_FromLong()`, `PyLong_FromLongLong()` and `PyLong_FromSsize_t()` are now handled by a common macro `PYLONG_FROM_INT` which contains fast paths depending on the size of the integer to convert. Consequently, `PyLong_FromSsize_t()` for medium-sized integers is faster by roughly 25%. --------- Co-authored-by: Sergey B Kirpichev <skirpichev@gmail.com>
This commit is contained in:
parent
a005835f69
commit
119bcfad9c
@ -168,9 +168,9 @@ class LongTests(unittest.TestCase):
|
||||
mask=False,
|
||||
negative_value_error=OverflowError):
|
||||
# round trip (object -> C integer -> object)
|
||||
values = (0, 1, 1234, max_val)
|
||||
values = (0, 1, 512, 1234, max_val)
|
||||
if min_val < 0:
|
||||
values += (-1, min_val)
|
||||
values += (-1, -512, -1234, min_val)
|
||||
for value in values:
|
||||
with self.subTest(value=value):
|
||||
self.assertEqual(func(value), value)
|
||||
|
@ -0,0 +1,2 @@
|
||||
Add fast path for medium-size integers in :c:func:`PyLong_FromSsize_t`.
|
||||
Patch by Chris Eibl.
|
@ -332,49 +332,51 @@ _PyLong_Negate(PyLongObject **x_p)
|
||||
Py_DECREF(x);
|
||||
}
|
||||
|
||||
#define PYLONG_FROM_INT(UINT_TYPE, INT_TYPE, ival) \
|
||||
do { \
|
||||
/* Handle small and medium cases. */ \
|
||||
if (IS_SMALL_INT(ival)) { \
|
||||
return get_small_int((sdigit)(ival)); \
|
||||
} \
|
||||
if (-(INT_TYPE)PyLong_MASK <= (ival) && (ival) <= (INT_TYPE)PyLong_MASK) { \
|
||||
return _PyLong_FromMedium((sdigit)(ival)); \
|
||||
} \
|
||||
UINT_TYPE abs_ival = (ival) < 0 ? 0U-(UINT_TYPE)(ival) : (UINT_TYPE)(ival); \
|
||||
/* Do shift in two steps to avoid possible undefined behavior. */ \
|
||||
UINT_TYPE t = abs_ival >> PyLong_SHIFT >> PyLong_SHIFT; \
|
||||
/* Count digits (at least two - smaller cases were handled above). */ \
|
||||
Py_ssize_t ndigits = 2; \
|
||||
while (t) { \
|
||||
++ndigits; \
|
||||
t >>= PyLong_SHIFT; \
|
||||
} \
|
||||
/* Construct output value. */ \
|
||||
PyLongObject *v = long_alloc(ndigits); \
|
||||
if (v == NULL) { \
|
||||
return NULL; \
|
||||
} \
|
||||
digit *p = v->long_value.ob_digit; \
|
||||
_PyLong_SetSignAndDigitCount(v, (ival) < 0 ? -1 : 1, ndigits); \
|
||||
t = abs_ival; \
|
||||
while (t) { \
|
||||
*p++ = (digit)(t & PyLong_MASK); \
|
||||
t >>= PyLong_SHIFT; \
|
||||
} \
|
||||
return (PyObject *)v; \
|
||||
} while(0)
|
||||
|
||||
|
||||
/* Create a new int object from a C long int */
|
||||
|
||||
PyObject *
|
||||
PyLong_FromLong(long ival)
|
||||
{
|
||||
PyLongObject *v;
|
||||
unsigned long abs_ival, t;
|
||||
int ndigits;
|
||||
|
||||
/* Handle small and medium cases. */
|
||||
if (IS_SMALL_INT(ival)) {
|
||||
return get_small_int((sdigit)ival);
|
||||
}
|
||||
if (-(long)PyLong_MASK <= ival && ival <= (long)PyLong_MASK) {
|
||||
return _PyLong_FromMedium((sdigit)ival);
|
||||
}
|
||||
|
||||
/* Count digits (at least two - smaller cases were handled above). */
|
||||
abs_ival = ival < 0 ? 0U-(unsigned long)ival : (unsigned long)ival;
|
||||
/* Do shift in two steps to avoid possible undefined behavior. */
|
||||
t = abs_ival >> PyLong_SHIFT >> PyLong_SHIFT;
|
||||
ndigits = 2;
|
||||
while (t) {
|
||||
++ndigits;
|
||||
t >>= PyLong_SHIFT;
|
||||
}
|
||||
|
||||
/* Construct output value. */
|
||||
v = long_alloc(ndigits);
|
||||
if (v != NULL) {
|
||||
digit *p = v->long_value.ob_digit;
|
||||
_PyLong_SetSignAndDigitCount(v, ival < 0 ? -1 : 1, ndigits);
|
||||
t = abs_ival;
|
||||
while (t) {
|
||||
*p++ = (digit)(t & PyLong_MASK);
|
||||
t >>= PyLong_SHIFT;
|
||||
}
|
||||
}
|
||||
return (PyObject *)v;
|
||||
PYLONG_FROM_INT(unsigned long, long, ival);
|
||||
}
|
||||
|
||||
#define PYLONG_FROM_UINT(INT_TYPE, ival) \
|
||||
do { \
|
||||
/* Handle small and medium cases. */ \
|
||||
if (IS_SMALL_UINT(ival)) { \
|
||||
return get_small_int((sdigit)(ival)); \
|
||||
} \
|
||||
@ -383,12 +385,13 @@ PyLong_FromLong(long ival)
|
||||
} \
|
||||
/* Do shift in two steps to avoid possible undefined behavior. */ \
|
||||
INT_TYPE t = (ival) >> PyLong_SHIFT >> PyLong_SHIFT; \
|
||||
/* Count the number of Python digits. */ \
|
||||
/* Count digits (at least two - smaller cases were handled above). */ \
|
||||
Py_ssize_t ndigits = 2; \
|
||||
while (t) { \
|
||||
++ndigits; \
|
||||
t >>= PyLong_SHIFT; \
|
||||
} \
|
||||
/* Construct output value. */ \
|
||||
PyLongObject *v = long_alloc(ndigits); \
|
||||
if (v == NULL) { \
|
||||
return NULL; \
|
||||
@ -1482,40 +1485,7 @@ PyLong_AsVoidPtr(PyObject *vv)
|
||||
PyObject *
|
||||
PyLong_FromLongLong(long long ival)
|
||||
{
|
||||
PyLongObject *v;
|
||||
unsigned long long abs_ival, t;
|
||||
int ndigits;
|
||||
|
||||
/* Handle small and medium cases. */
|
||||
if (IS_SMALL_INT(ival)) {
|
||||
return get_small_int((sdigit)ival);
|
||||
}
|
||||
if (-(long long)PyLong_MASK <= ival && ival <= (long long)PyLong_MASK) {
|
||||
return _PyLong_FromMedium((sdigit)ival);
|
||||
}
|
||||
|
||||
/* Count digits (at least two - smaller cases were handled above). */
|
||||
abs_ival = ival < 0 ? 0U-(unsigned long long)ival : (unsigned long long)ival;
|
||||
/* Do shift in two steps to avoid possible undefined behavior. */
|
||||
t = abs_ival >> PyLong_SHIFT >> PyLong_SHIFT;
|
||||
ndigits = 2;
|
||||
while (t) {
|
||||
++ndigits;
|
||||
t >>= PyLong_SHIFT;
|
||||
}
|
||||
|
||||
/* Construct output value. */
|
||||
v = long_alloc(ndigits);
|
||||
if (v != NULL) {
|
||||
digit *p = v->long_value.ob_digit;
|
||||
_PyLong_SetSignAndDigitCount(v, ival < 0 ? -1 : 1, ndigits);
|
||||
t = abs_ival;
|
||||
while (t) {
|
||||
*p++ = (digit)(t & PyLong_MASK);
|
||||
t >>= PyLong_SHIFT;
|
||||
}
|
||||
}
|
||||
return (PyObject *)v;
|
||||
PYLONG_FROM_INT(unsigned long long, long long, ival);
|
||||
}
|
||||
|
||||
/* Create a new int object from a C Py_ssize_t. */
|
||||
@ -1523,42 +1493,7 @@ PyLong_FromLongLong(long long ival)
|
||||
PyObject *
|
||||
PyLong_FromSsize_t(Py_ssize_t ival)
|
||||
{
|
||||
PyLongObject *v;
|
||||
size_t abs_ival;
|
||||
size_t t; /* unsigned so >> doesn't propagate sign bit */
|
||||
int ndigits = 0;
|
||||
int negative = 0;
|
||||
|
||||
if (IS_SMALL_INT(ival)) {
|
||||
return get_small_int((sdigit)ival);
|
||||
}
|
||||
|
||||
if (ival < 0) {
|
||||
/* avoid signed overflow when ival = SIZE_T_MIN */
|
||||
abs_ival = (size_t)(-1-ival)+1;
|
||||
negative = 1;
|
||||
}
|
||||
else {
|
||||
abs_ival = (size_t)ival;
|
||||
}
|
||||
|
||||
/* Count the number of Python digits. */
|
||||
t = abs_ival;
|
||||
while (t) {
|
||||
++ndigits;
|
||||
t >>= PyLong_SHIFT;
|
||||
}
|
||||
v = long_alloc(ndigits);
|
||||
if (v != NULL) {
|
||||
digit *p = v->long_value.ob_digit;
|
||||
_PyLong_SetSignAndDigitCount(v, negative ? -1 : 1, ndigits);
|
||||
t = abs_ival;
|
||||
while (t) {
|
||||
*p++ = (digit)(t & PyLong_MASK);
|
||||
t >>= PyLong_SHIFT;
|
||||
}
|
||||
}
|
||||
return (PyObject *)v;
|
||||
PYLONG_FROM_INT(size_t, Py_ssize_t, ival);
|
||||
}
|
||||
|
||||
/* Get a C long long int from an int object or any object that has an
|
||||
|
Loading…
x
Reference in New Issue
Block a user