Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
17 commits
Select commit Hold shift + click to select a range
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
1 change: 1 addition & 0 deletions Misc/ACKS
Original file line number Diff line number Diff line change
Expand Up @@ -1858,6 +1858,7 @@ Kurt Vile
Norman Vine
Pauli Virtanen
Frank Visser
Jeremiah Vivian (Pascual)
Johannes Vogel
Michael Vogt
Radu Voicilas
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Optimize some modulo operations in ``Objects/longobject.c``. Patch by Jeremiah Vivian.
115 changes: 106 additions & 9 deletions Objects/longobject.c
Original file line number Diff line number Diff line change
Expand Up @@ -1653,6 +1653,35 @@ divrem1(PyLongObject *a, digit n, digit *prem)
return long_normalize(z);
}

/* Remainder of long pin, w/ size digits, by non-zero digit n,
returning the remainder. pin points at the LSD. */

static digit
inplace_rem1(digit *pin, Py_ssize_t size, digit n)
{
twodigits rem = 0;

assert(n > 0 && n <= PyLong_MASK);
while (--size >= 0)
rem = ((rem << PyLong_SHIFT) | pin[size]) % n;
return (digit)rem;
}

/* Get the remainder of an integer divided by a digit, returning
the remainder as the result of the function. The sign of a is
ignored; n should not be zero. */

static PyLongObject *
rem1(PyLongObject *a, digit n)
{
const Py_ssize_t size = Py_ABS(Py_SIZE(a));

assert(n > 0 && n <= PyLong_MASK);
return (PyLongObject *)PyLong_FromLong(
(long)inplace_rem1(a->ob_digit, size, n)
);
}

/* Convert an 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.) */
Expand Down Expand Up @@ -2672,6 +2701,47 @@ long_divrem(PyLongObject *a, PyLongObject *b,
return 0;
}

/* Int remainder, top-level routine */

static int
long_rem(PyLongObject *a, PyLongObject *b, PyLongObject **prem)
{
Py_ssize_t size_a = Py_ABS(Py_SIZE(a)), size_b = Py_ABS(Py_SIZE(b));

if (size_b == 0) {
PyErr_SetString(PyExc_ZeroDivisionError,
"integer modulo by zero");
return -1;
}
if (size_a < size_b ||
(size_a == size_b &&
a->ob_digit[size_a-1] < b->ob_digit[size_b-1])) {
/* |a| < |b|. */
*prem = (PyLongObject *)long_long((PyObject *)a);
return -(*prem == NULL);
}
if (size_b == 1) {
*prem = rem1(a, b->ob_digit[0]);
if (*prem == NULL)
return -1;
}
else {
/* Slow path using divrem. */
x_divrem(a, b, prem);
if (*prem == NULL)
return -1;
}
/* Set the sign. */
if (Py_SIZE(a) < 0 && Py_SIZE(*prem) != 0) {
_PyLong_Negate(prem);
if (*prem == NULL) {
Py_CLEAR(*prem);
return -1;
}
}
return 0;
}

/* Unsigned int division with remainder -- the algorithm. The arguments v1
and w1 should satisfy 2 <= Py_ABS(Py_SIZE(w1)) <= Py_ABS(Py_SIZE(v1)). */

Expand Down Expand Up @@ -3790,6 +3860,37 @@ l_divmod(PyLongObject *v, PyLongObject *w,
return 0;
}

/* Compute
* *pmod = v % w
* pmod cannot be NULL. The caller owns a reference to pmod.
*/
static int
l_mod(PyLongObject *v, PyLongObject *w, PyLongObject **pmod)
{
PyLongObject *mod;

assert(pmod);
if (Py_ABS(Py_SIZE(v)) == 1 && Py_ABS(Py_SIZE(w)) == 1) {
/* Fast path for single-digit longs */
*pmod = (PyLongObject *)fast_mod(v, w);
return -(*pmod == NULL);
}
if (long_rem(v, w, &mod) < 0)
return -1;
if ((Py_SIZE(mod) < 0 && Py_SIZE(w) > 0) ||
(Py_SIZE(mod) > 0 && Py_SIZE(w) < 0)) {
PyLongObject *temp;
temp = (PyLongObject *) long_add(mod, w);
Py_DECREF(mod);
mod = temp;
if (mod == NULL)
return -1;
}
*pmod = mod;

return 0;
}

static PyObject *
long_div(PyObject *a, PyObject *b)
{
Expand Down Expand Up @@ -4076,11 +4177,7 @@ long_mod(PyObject *a, PyObject *b)

CHECK_BINOP(a, b);

if (Py_ABS(Py_SIZE(a)) == 1 && Py_ABS(Py_SIZE(b)) == 1) {
return fast_mod((PyLongObject*)a, (PyLongObject*)b);
}

if (l_divmod((PyLongObject*)a, (PyLongObject*)b, NULL, &mod) < 0)
if (l_mod((PyLongObject*)a, (PyLongObject*)b, &mod) < 0)
mod = NULL;
return (PyObject *)mod;
}
Expand Down Expand Up @@ -4309,10 +4406,10 @@ long_pow(PyObject *v, PyObject *w, PyObject *x)
while the "large exponent" case multiplies directly by base 31
times. It can be unboundedly faster to multiply by
base % modulus instead.
We could _always_ do this reduction, but l_divmod() isn't cheap,
We could _always_ do this reduction, but l_mod() isn't cheap,
so we only do it when it buys something. */
if (Py_SIZE(a) < 0 || Py_SIZE(a) > Py_SIZE(c)) {
if (l_divmod(a, c, NULL, &temp) < 0)
if (l_mod(a, c, &temp) < 0)
goto Error;
Py_DECREF(a);
a = temp;
Expand All @@ -4333,7 +4430,7 @@ long_pow(PyObject *v, PyObject *w, PyObject *x)
#define REDUCE(X) \
do { \
if (c != NULL) { \
if (l_divmod(X, c, NULL, &temp) < 0) \
if (l_mod(X, c, &temp) < 0) \
goto Error; \
Py_XDECREF(X); \
X = temp; \
Expand Down Expand Up @@ -4998,7 +5095,7 @@ _PyLong_GCD(PyObject *aarg, PyObject *barg)

if (k == 0) {
/* no progress; do a Euclidean step */
if (l_divmod(a, b, NULL, &r) < 0)
if (l_mod(a, b, &r) < 0)
goto error;
Py_DECREF(a);
a = b;
Expand Down