Commit bf03077f authored by Stefan Behnel's avatar Stefan Behnel

extend PyLong optimisations to everything that fits into a C long (Py2.7 uses...

extend PyLong optimisations to everything that fits into a C long (Py2.7 uses 15 bit digits even on 64 bit platforms, i.e. C long covers 4 digits)
parent 2acc6660
...@@ -1000,7 +1000,7 @@ class GlobalState(object): ...@@ -1000,7 +1000,7 @@ class GlobalState(object):
# utility_code_def # utility_code_def
# #
code = self.parts['utility_code_def'] code = self.parts['utility_code_def']
code.put(UtilityCode.load_as_string("TypeConversions", "TypeConversion.c")[1]) code.put(TempitaUtilityCode.load_cached("TypeConversions", "TypeConversion.c").impl)
code.putln("") code.putln("")
def __getitem__(self, key): def __getitem__(self, key):
......
...@@ -493,6 +493,7 @@ static PyObject* __Pyx_PyInt_{{op}}{{order}}(PyObject *op1, PyObject *op2, long ...@@ -493,6 +493,7 @@ static PyObject* __Pyx_PyInt_{{op}}{{order}}(PyObject *op1, PyObject *op2, long
//@requires: TypeConversion.c::PyLongInternals //@requires: TypeConversion.c::PyLongInternals
#if CYTHON_COMPILING_IN_CPYTHON #if CYTHON_COMPILING_IN_CPYTHON
{{py: from Cython.Utility import pylong_join }}
{{py: pyval, ival = ('op2', 'b') if order == 'CObj' else ('op1', 'a') }} {{py: pyval, ival = ('op2', 'b') if order == 'CObj' else ('op1', 'a') }}
{{py: c_op = {'Add': '+', 'Subtract': '-', 'Remainder': '%', 'Or': '|', 'Xor': '^', 'And': '&', 'Rshift': '>>'}[op] }} {{py: c_op = {'Add': '+', 'Subtract': '-', 'Remainder': '%', 'Or': '|', 'Xor': '^', 'And': '&', 'Rshift': '>>'}[op] }}
...@@ -532,27 +533,26 @@ static PyObject* __Pyx_PyInt_{{op}}{{order}}(PyObject *op1, PyObject *op2, CYTHO ...@@ -532,27 +533,26 @@ static PyObject* __Pyx_PyInt_{{op}}{{order}}(PyObject *op1, PyObject *op2, CYTHO
if (likely(PyLong_CheckExact({{pyval}}))) { if (likely(PyLong_CheckExact({{pyval}}))) {
const long {{'a' if order == 'CObj' else 'b'}} = intval; const long {{'a' if order == 'CObj' else 'b'}} = intval;
long x, {{ival}}; long x, {{ival}};
const digit* digits = ((PyLongObject*){{pyval}})->ob_digit;
const Py_ssize_t size = Py_SIZE({{pyval}}); const Py_ssize_t size = Py_SIZE({{pyval}});
switch (size) { switch (size) {
case 0: {{ival}} = 0; break; case 0: {{ival}} = 0; break;
case -1: {{if c_op != '%'}} case -1: {{if c_op != '%'}}
{{ival}} = -(sdigit)((PyLongObject*){{pyval}})->ob_digit[0]; break; {{ival}} = -(sdigit)digits[0]; break;
{{endif}} {{endif}}
// fall through to positive calculation for '%' // fall through to positive calculation for '%'
case 1: {{ival}} = ((PyLongObject*){{pyval}})->ob_digit[0]; break; case 1: {{ival}} = digits[0]; break;
case -2: {{if c_op != '%'}} {{for _size in (2, 3, 4)}}
if (8 * sizeof(long) - 1 > 2 * PyLong_SHIFT) { {{for _case in (-_size, _size)}}
{{ival}} = -(long) ((((unsigned long)((PyLongObject*){{pyval}})->ob_digit[1]) << PyLong_SHIFT) | ((PyLongObject*){{pyval}})->ob_digit[0]); case {{_case}}: {{if c_op != '%' or _case > 0}}
if (8 * sizeof(long) - 1 > {{_size}} * PyLong_SHIFT) {
{{ival}} = {{'-' if _case < 0 else ''}}(long) {{pylong_join(_size, 'digits')}};
break; break;
} }
{{endif}} {{endif}}
// fall through to positive calculation for '%' // in negative case, fall through to positive calculation for '%'
case 2: {{endfor}}
if (8 * sizeof(long) - 1 > 2 * PyLong_SHIFT) { {{endfor}}
{{ival}} = (long) ((((unsigned long)((PyLongObject*){{pyval}})->ob_digit[1]) << PyLong_SHIFT) | ((PyLongObject*){{pyval}})->ob_digit[0]);
break;
}
// fall through if two platform digits don't fit into a long
default: return PyLong_Type.tp_as_number->nb_{{op.lower()}}(op1, op2); default: return PyLong_Type.tp_as_number->nb_{{op.lower()}}(op1, op2);
} }
{{if c_op == '%'}} {{if c_op == '%'}}
...@@ -596,6 +596,7 @@ static PyObject* __Pyx_PyFloat_{{op}}{{order}}(PyObject *op1, PyObject *op2, dou ...@@ -596,6 +596,7 @@ static PyObject* __Pyx_PyFloat_{{op}}{{order}}(PyObject *op1, PyObject *op2, dou
//@requires: TypeConversion.c::PyLongInternals //@requires: TypeConversion.c::PyLongInternals
#if CYTHON_COMPILING_IN_CPYTHON #if CYTHON_COMPILING_IN_CPYTHON
{{py: from Cython.Utility import pylong_join }}
{{py: pyval, fval = ('op2', 'b') if order == 'CObj' else ('op1', 'a') }} {{py: pyval, fval = ('op2', 'b') if order == 'CObj' else ('op1', 'a') }}
{{py: c_op = '+' if op == 'Add' else '-' }} {{py: c_op = '+' if op == 'Add' else '-' }}
...@@ -615,22 +616,25 @@ static PyObject* __Pyx_PyFloat_{{op}}{{order}}(PyObject *op1, PyObject *op2, dou ...@@ -615,22 +616,25 @@ static PyObject* __Pyx_PyFloat_{{op}}{{order}}(PyObject *op1, PyObject *op2, dou
if (likely(PyLong_CheckExact({{pyval}}))) { if (likely(PyLong_CheckExact({{pyval}}))) {
#if CYTHON_USE_PYLONG_INTERNALS #if CYTHON_USE_PYLONG_INTERNALS
const digit* digits = ((PyLongObject*){{pyval}})->ob_digit;
const Py_ssize_t size = Py_SIZE({{pyval}}); const Py_ssize_t size = Py_SIZE({{pyval}});
switch (size) { switch (size) {
case -1: {{fval}} = -(double)((PyLongObject*){{pyval}})->ob_digit[0]; break;
case 0: {{fval}} = 0.0; break; case 0: {{fval}} = 0.0; break;
case 1: {{fval}} = (double)((PyLongObject*){{pyval}})->ob_digit[0]; break; case -1: {{fval}} = -(double) digits[0]; break;
case 2: case 1: {{fval}} = (double) digits[0]; break;
case -2: {{for _size in (2, 3, 4)}}
if (8 * sizeof(unsigned long) > 2 * PyLong_SHIFT) { case -{{_size}}:
{{fval}} = (double) ((((unsigned long)((PyLongObject*){{pyval}})->ob_digit[1]) << PyLong_SHIFT) | ((PyLongObject*){{pyval}})->ob_digit[0]); case {{_size}}:
if (8 * sizeof(unsigned long) > {{_size}} * PyLong_SHIFT && ((8 * sizeof(unsigned long) < 53) || ({{_size-1}} * PyLong_SHIFT < 53))) {
{{fval}} = (double) {{pylong_join(_size, 'digits')}};
// let CPython do its own float rounding from 2**53 on (max. consecutive integer in double float) // let CPython do its own float rounding from 2**53 on (max. consecutive integer in double float)
if ((8 * sizeof(unsigned long) < 53) || (2 * PyLong_SHIFT < 53) || ({{fval}} < (double) (1L<<53))) { if ((8 * sizeof(unsigned long) < 53) || ({{_size}} * PyLong_SHIFT < 53) || ({{fval}} < (double) (1L<<53))) {
if (size == -2) if (size == {{-_size}})
{{fval}} = -{{fval}}; {{fval}} = -{{fval}};
break; break;
} }
} }
{{endfor}}
// fall through if two platform digits don't fit into a double // fall through if two platform digits don't fit into a double
default: {{fval}} = PyLong_AsDouble({{pyval}}); default: {{fval}} = PyLong_AsDouble({{pyval}});
if (unlikely({{fval}} == -1 && PyErr_Occurred())) return NULL; if (unlikely({{fval}} == -1 && PyErr_Occurred())) return NULL;
......
...@@ -280,6 +280,8 @@ static CYTHON_INLINE PyObject* __Pyx_PyNumber_Int(PyObject* x) { ...@@ -280,6 +280,8 @@ static CYTHON_INLINE PyObject* __Pyx_PyNumber_Int(PyObject* x) {
return res; return res;
} }
{{py: from Cython.Utility import pylong_join }}
static CYTHON_INLINE Py_ssize_t __Pyx_PyIndex_AsSsize_t(PyObject* b) { static CYTHON_INLINE Py_ssize_t __Pyx_PyIndex_AsSsize_t(PyObject* b) {
Py_ssize_t ival; Py_ssize_t ival;
PyObject *x; PyObject *x;
...@@ -293,20 +295,25 @@ static CYTHON_INLINE Py_ssize_t __Pyx_PyIndex_AsSsize_t(PyObject* b) { ...@@ -293,20 +295,25 @@ static CYTHON_INLINE Py_ssize_t __Pyx_PyIndex_AsSsize_t(PyObject* b) {
#endif #endif
if (likely(PyLong_CheckExact(b))) { if (likely(PyLong_CheckExact(b))) {
#if CYTHON_USE_PYLONG_INTERNALS #if CYTHON_USE_PYLONG_INTERNALS
switch (Py_SIZE(b)) { const digit* digits = ((PyLongObject*)b)->ob_digit;
const Py_ssize_t size = Py_SIZE(b);
switch (size) {
case 0: return 0; case 0: return 0;
case 1: return ((PyLongObject*)b)->ob_digit[0]; case 1: return digits[0];
case -1: return -(sdigit)((PyLongObject*)b)->ob_digit[0]; case -1: return -(sdigit) digits[0];
case 2: default:
if (8 * sizeof(Py_ssize_t) > 2 * PyLong_SHIFT) { // move the substantially less common cases out of the way
return (Py_ssize_t) ((((size_t)((PyLongObject*)b)->ob_digit[1]) << PyLong_SHIFT) | ((PyLongObject*)b)->ob_digit[0]); switch (size) {
{{for _size in (2, 3, 4)}}
{{for _case in (_size, -_size)}}
case {{_case}}:
if (8 * sizeof(Py_ssize_t) > {{_size}} * PyLong_SHIFT) {
return (Py_ssize_t) {{pylong_join(_size, 'digits', 'size_t')}};
} }
break; break;
case -2: {{endfor}}
if (8 * sizeof(Py_ssize_t) > 2 * PyLong_SHIFT) { {{endfor}}
return -(Py_ssize_t) ((((size_t)((PyLongObject*)b)->ob_digit[1]) << PyLong_SHIFT) | ((PyLongObject*)b)->ob_digit[0]);
} }
break;
} }
#endif #endif
return PyLong_AsSsize_t(b); return PyLong_AsSsize_t(b);
...@@ -540,6 +547,8 @@ static CYTHON_INLINE {{TYPE}} {{FROM_PY_FUNCTION}}(PyObject *); ...@@ -540,6 +547,8 @@ static CYTHON_INLINE {{TYPE}} {{FROM_PY_FUNCTION}}(PyObject *);
//@requires: CIntFromPyVerify //@requires: CIntFromPyVerify
//@requires: PyLongInternals //@requires: PyLongInternals
{{py: from Cython.Utility import pylong_join }}
static CYTHON_INLINE {{TYPE}} {{FROM_PY_FUNCTION}}(PyObject *x) { static CYTHON_INLINE {{TYPE}} {{FROM_PY_FUNCTION}}(PyObject *x) {
const {{TYPE}} neg_one = ({{TYPE}}) -1, const_zero = 0; const {{TYPE}} neg_one = ({{TYPE}}) -1, const_zero = 0;
const int is_unsigned = neg_one > const_zero; const int is_unsigned = neg_one > const_zero;
...@@ -559,15 +568,17 @@ static CYTHON_INLINE {{TYPE}} {{FROM_PY_FUNCTION}}(PyObject *x) { ...@@ -559,15 +568,17 @@ static CYTHON_INLINE {{TYPE}} {{FROM_PY_FUNCTION}}(PyObject *x) {
if (likely(PyLong_Check(x))) { if (likely(PyLong_Check(x))) {
if (is_unsigned) { if (is_unsigned) {
#if CYTHON_USE_PYLONG_INTERNALS #if CYTHON_USE_PYLONG_INTERNALS
const digit* digits = ((PyLongObject*)x)->ob_digit;
switch (Py_SIZE(x)) { switch (Py_SIZE(x)) {
case 0: return 0; case 0: return 0;
case 1: __PYX_VERIFY_RETURN_INT({{TYPE}}, digit, ((PyLongObject*)x)->ob_digit[0]) case 1: __PYX_VERIFY_RETURN_INT({{TYPE}}, digit, digits[0])
case 2: {{for _size in (2, 3, 4)}}
if ((8 * sizeof({{TYPE}}) > PyLong_SHIFT) && (8 * sizeof(unsigned long) > 2 * PyLong_SHIFT)) { case {{_size}}:
__PYX_VERIFY_RETURN_INT({{TYPE}}, unsigned long, if ((8 * sizeof({{TYPE}}) > {{_size-1}} * PyLong_SHIFT) && (8 * sizeof(unsigned long) > {{_size}} * PyLong_SHIFT)) {
(((unsigned long)((PyLongObject*)x)->ob_digit[1]) << PyLong_SHIFT) | ((PyLongObject*)x)->ob_digit[0]) __PYX_VERIFY_RETURN_INT({{TYPE}}, unsigned long, {{pylong_join(_size, 'digits')}})
} }
break; break;
{{endfor}}
} }
#endif #endif
#if CYTHON_COMPILING_IN_CPYTHON #if CYTHON_COMPILING_IN_CPYTHON
...@@ -583,22 +594,20 @@ static CYTHON_INLINE {{TYPE}} {{FROM_PY_FUNCTION}}(PyObject *x) { ...@@ -583,22 +594,20 @@ static CYTHON_INLINE {{TYPE}} {{FROM_PY_FUNCTION}}(PyObject *x) {
} else { } else {
// signed // signed
#if CYTHON_USE_PYLONG_INTERNALS #if CYTHON_USE_PYLONG_INTERNALS
const digit* digits = ((PyLongObject*)x)->ob_digit;
switch (Py_SIZE(x)) { switch (Py_SIZE(x)) {
case 0: return 0; case 0: return 0;
case -1: __PYX_VERIFY_RETURN_INT({{TYPE}}, sdigit, -(sdigit) ((PyLongObject*)x)->ob_digit[0]) case -1: __PYX_VERIFY_RETURN_INT({{TYPE}}, sdigit, -(sdigit) digits[0])
case 1: __PYX_VERIFY_RETURN_INT({{TYPE}}, digit, +(((PyLongObject*)x)->ob_digit[0])) case 1: __PYX_VERIFY_RETURN_INT({{TYPE}}, digit, +digits[0])
case 2: {{for _size in (2, 3, 4)}}
if ((8 * sizeof({{TYPE}}) > PyLong_SHIFT) && (8 * sizeof(unsigned long) > 2 * PyLong_SHIFT)) { {{for _case in (-_size, _size)}}
__PYX_VERIFY_RETURN_INT({{TYPE}}, unsigned long, case {{_case}}:
(((unsigned long)((PyLongObject*)x)->ob_digit[1]) << PyLong_SHIFT) | ((PyLongObject*)x)->ob_digit[0]) if ((8 * sizeof({{TYPE}}) > {{_size if _case < 0 else _size-1}} * PyLong_SHIFT) && (8 * sizeof(unsigned long) > {{_size}} * PyLong_SHIFT)) {
} __PYX_VERIFY_RETURN_INT({{TYPE}}, {{'long' if _case < 0 else 'unsigned long'}}, {{'-(long) ' if _case < 0 else ''}}{{pylong_join(_size, 'digits')}})
break;
case -2:
if ((8 * sizeof({{TYPE}}) > 2 * PyLong_SHIFT) && (8 * sizeof(unsigned long) > 2 * PyLong_SHIFT)) {
__PYX_VERIFY_RETURN_INT({{TYPE}}, long,
-(long) (((unsigned long)((PyLongObject*)x)->ob_digit[1]) << PyLong_SHIFT) | ((PyLongObject*)x)->ob_digit[0])
} }
break; break;
{{endfor}}
{{endfor}}
} }
#endif #endif
if (sizeof({{TYPE}}) <= sizeof(long)) { if (sizeof({{TYPE}}) <= sizeof(long)) {
......
def pylong_join(count, digits_ptr='digits', join_type='unsigned long'):
"""
Generate an unrolled shift-then-or loop over the first 'count' digits.
Assumes that they fit into 'join_type'.
"""
return ('(' * (count * 2) + "(%s)" % join_type + ' | '.join(
"%s[%d])%s)" % (digits_ptr, _i, " << PyLong_SHIFT" if _i else '')
for _i in range(count-1, -1, -1)))
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment