Issue #1087418: Small performance boost for bitwise operations on longs.

Initial patch by Gregory Smith;  some tweaks added.
This commit is contained in:
Mark Dickinson 2009-10-25 20:39:06 +00:00
parent f1ca811abb
commit 8d87dc0c29
2 changed files with 103 additions and 67 deletions

View File

@ -12,6 +12,8 @@ What's New in Python 2.7 alpha 1
Core and Builtins Core and Builtins
----------------- -----------------
- Issue #1087418: Boost performance of bitwise operations for longs.
- Issue #1722344: threading._shutdown() is now called in Py_Finalize(), which - Issue #1722344: threading._shutdown() is now called in Py_Finalize(), which
fixes the problem of some exceptions being thrown at shutdown when the fixes the problem of some exceptions being thrown at shutdown when the
interpreter is killed. Patch by Adam Olsen. interpreter is killed. Patch by Adam Olsen.

View File

@ -3411,6 +3411,22 @@ lshift_error:
return (PyObject *) z; return (PyObject *) z;
} }
/* Compute two's complement of digit vector a[0:m], writing result to
z[0:m]. The digit vector a need not be normalized, but should not
be entirely zero. a and z may point to the same digit vector. */
static void
v_complement(digit *z, digit *a, Py_ssize_t m)
{
Py_ssize_t i;
digit carry = 1;
for (i = 0; i < m; ++i) {
carry += a[i] ^ PyLong_MASK;
z[i] = carry & PyLong_MASK;
carry >>= PyLong_SHIFT;
}
assert(carry == 0);
}
/* Bitwise and/xor/or operations */ /* Bitwise and/xor/or operations */
@ -3419,60 +3435,49 @@ long_bitwise(PyLongObject *a,
int op, /* '&', '|', '^' */ int op, /* '&', '|', '^' */
PyLongObject *b) PyLongObject *b)
{ {
digit maska, maskb; /* 0 or PyLong_MASK */ int nega, negb, negz;
int negz;
Py_ssize_t size_a, size_b, size_z, i; Py_ssize_t size_a, size_b, size_z, i;
PyLongObject *z; PyLongObject *z;
digit diga, digb;
PyObject *v;
if (Py_SIZE(a) < 0) { /* Bitwise operations for negative numbers operate as though
a = (PyLongObject *) long_invert(a); on a two's complement representation. So convert arguments
if (a == NULL) from sign-magnitude to two's complement, and convert the
result back to sign-magnitude at the end. */
/* If a is negative, replace it by its two's complement. */
size_a = ABS(Py_SIZE(a));
nega = Py_SIZE(a) < 0;
if (nega) {
z = _PyLong_New(size_a);
if (z == NULL)
return NULL; return NULL;
maska = PyLong_MASK; v_complement(z->ob_digit, a->ob_digit, size_a);
a = z;
} }
else { else
/* Keep reference count consistent. */
Py_INCREF(a); Py_INCREF(a);
maska = 0;
} /* Same for b. */
if (Py_SIZE(b) < 0) { size_b = ABS(Py_SIZE(b));
b = (PyLongObject *) long_invert(b); negb = Py_SIZE(b) < 0;
if (b == NULL) { if (negb) {
z = _PyLong_New(size_b);
if (z == NULL) {
Py_DECREF(a); Py_DECREF(a);
return NULL; return NULL;
} }
maskb = PyLong_MASK; v_complement(z->ob_digit, b->ob_digit, size_b);
b = z;
} }
else { else
Py_INCREF(b); Py_INCREF(b);
maskb = 0;
}
negz = 0; /* Swap a and b if necessary to ensure size_a >= size_b. */
switch (op) { if (size_a < size_b) {
case '^': z = a; a = b; b = z;
if (maska != maskb) { size_z = size_a; size_a = size_b; size_b = size_z;
maska ^= PyLong_MASK; negz = nega; nega = negb; negb = negz;
negz = -1;
}
break;
case '&':
if (maska && maskb) {
op = '|';
maska ^= PyLong_MASK;
maskb ^= PyLong_MASK;
negz = -1;
}
break;
case '|':
if (maska || maskb) {
op = '&';
maska ^= PyLong_MASK;
maskb ^= PyLong_MASK;
negz = -1;
}
break;
} }
/* JRH: The original logic here was to allocate the result value (z) /* JRH: The original logic here was to allocate the result value (z)
@ -3480,43 +3485,72 @@ long_bitwise(PyLongObject *a,
where the result is guaranteed to be shorter than that: AND of two where the result is guaranteed to be shorter than that: AND of two
positives, OR of two negatives: use the shorter number. AND with positives, OR of two negatives: use the shorter number. AND with
mixed signs: use the positive number. OR with mixed signs: use the mixed signs: use the positive number. OR with mixed signs: use the
negative number. After the transformations above, op will be '&' negative number.
iff one of these cases applies, and mask will be non-0 for operands
whose length should be ignored.
*/ */
switch (op) {
case '^':
negz = nega ^ negb;
size_z = size_a;
break;
case '&':
negz = nega & negb;
size_z = negb ? size_a : size_b;
break;
case '|':
negz = nega | negb;
size_z = negb ? size_b : size_a;
break;
default:
PyErr_BadArgument();
return NULL;
}
size_a = Py_SIZE(a); /* We allow an extra digit if z is negative, to make sure that
size_b = Py_SIZE(b); the final two's complement of z doesn't overflow. */
size_z = op == '&' z = _PyLong_New(size_z + negz);
? (maska
? size_b
: (maskb ? size_a : MIN(size_a, size_b)))
: MAX(size_a, size_b);
z = _PyLong_New(size_z);
if (z == NULL) { if (z == NULL) {
Py_DECREF(a); Py_DECREF(a);
Py_DECREF(b); Py_DECREF(b);
return NULL; return NULL;
} }
for (i = 0; i < size_z; ++i) { /* Compute digits for overlap of a and b. */
diga = (i < size_a ? a->ob_digit[i] : 0) ^ maska; switch(op) {
digb = (i < size_b ? b->ob_digit[i] : 0) ^ maskb; case '&':
switch (op) { for (i = 0; i < size_b; ++i)
case '&': z->ob_digit[i] = diga & digb; break; z->ob_digit[i] = a->ob_digit[i] & b->ob_digit[i];
case '|': z->ob_digit[i] = diga | digb; break; break;
case '^': z->ob_digit[i] = diga ^ digb; break; case '|':
for (i = 0; i < size_b; ++i)
z->ob_digit[i] = a->ob_digit[i] | b->ob_digit[i];
break;
case '^':
for (i = 0; i < size_b; ++i)
z->ob_digit[i] = a->ob_digit[i] ^ b->ob_digit[i];
break;
default:
PyErr_BadArgument();
return NULL;
} }
/* Copy any remaining digits of a, inverting if necessary. */
if (op == '^' && negb)
for (; i < size_z; ++i)
z->ob_digit[i] = a->ob_digit[i] ^ PyLong_MASK;
else if (i < size_z)
memcpy(&z->ob_digit[i], &a->ob_digit[i],
(size_z-i)*sizeof(digit));
/* Complement result if negative. */
if (negz) {
Py_SIZE(z) = -(Py_SIZE(z));
z->ob_digit[size_z] = PyLong_MASK;
v_complement(z->ob_digit, z->ob_digit, size_z+1);
} }
Py_DECREF(a); Py_DECREF(a);
Py_DECREF(b); Py_DECREF(b);
z = long_normalize(z); return (PyObject *)long_normalize(z);
if (negz == 0)
return (PyObject *) z;
v = long_invert(z);
Py_DECREF(z);
return v;
} }
static PyObject * static PyObject *