Commit b74716a2 authored by scoder's avatar scoder Committed by GitHub

Merge pull request #2188 from scoder/faster_int_comparison

Further optimise int/float comparison
parents 440c51b2 55cdc9f0
......@@ -3159,21 +3159,17 @@ class OptimizeBuiltinCalls(Visitor.NodeRefCleanupMixin,
may_return_none=True,
utility_code=load_c_utility('py_dict_pop'))
Pyx_PyInt_BinopInt_func_type = PyrexTypes.CFuncType(
PyrexTypes.py_object_type, [
PyrexTypes.CFuncTypeArg("op1", PyrexTypes.py_object_type, None),
PyrexTypes.CFuncTypeArg("op2", PyrexTypes.py_object_type, None),
PyrexTypes.CFuncTypeArg("intval", PyrexTypes.c_long_type, None),
PyrexTypes.CFuncTypeArg("inplace", PyrexTypes.c_bint_type, None),
])
Pyx_PyFloat_BinopInt_func_type = PyrexTypes.CFuncType(
PyrexTypes.py_object_type, [
PyrexTypes.CFuncTypeArg("op1", PyrexTypes.py_object_type, None),
PyrexTypes.CFuncTypeArg("op2", PyrexTypes.py_object_type, None),
PyrexTypes.CFuncTypeArg("fval", PyrexTypes.c_double_type, None),
PyrexTypes.CFuncTypeArg("inplace", PyrexTypes.c_bint_type, None),
])
Pyx_BinopInt_func_types = dict(
((ctype, ret_type), PyrexTypes.CFuncType(
ret_type, [
PyrexTypes.CFuncTypeArg("op1", PyrexTypes.py_object_type, None),
PyrexTypes.CFuncTypeArg("op2", PyrexTypes.py_object_type, None),
PyrexTypes.CFuncTypeArg("cval", ctype, None),
PyrexTypes.CFuncTypeArg("inplace", PyrexTypes.c_bint_type, None),
], exception_value=None if ret_type.is_pyobject else ret_type.exception_value))
for ctype in (PyrexTypes.c_long_type, PyrexTypes.c_double_type)
for ret_type in (PyrexTypes.py_object_type, PyrexTypes.c_bint_type)
)
def _handle_simple_method_object___add__(self, node, function, args, is_unbound_method):
return self._optimise_num_binop('Add', node, function, args, is_unbound_method)
......@@ -3184,7 +3180,7 @@ class OptimizeBuiltinCalls(Visitor.NodeRefCleanupMixin,
def _handle_simple_method_object___eq__(self, node, function, args, is_unbound_method):
return self._optimise_num_binop('Eq', node, function, args, is_unbound_method)
def _handle_simple_method_object___neq__(self, node, function, args, is_unbound_method):
def _handle_simple_method_object___ne__(self, node, function, args, is_unbound_method):
return self._optimise_num_binop('Ne', node, function, args, is_unbound_method)
def _handle_simple_method_object___and__(self, node, function, args, is_unbound_method):
......@@ -3253,7 +3249,7 @@ class OptimizeBuiltinCalls(Visitor.NodeRefCleanupMixin,
def _handle_simple_method_float___eq__(self, node, function, args, is_unbound_method):
return self._optimise_num_binop('Eq', node, function, args, is_unbound_method)
def _handle_simple_method_float___neq__(self, node, function, args, is_unbound_method):
def _handle_simple_method_float___ne__(self, node, function, args, is_unbound_method):
return self._optimise_num_binop('Ne', node, function, args, is_unbound_method)
def _optimise_num_binop(self, operator, node, function, args, is_unbound_method):
......@@ -3262,7 +3258,15 @@ class OptimizeBuiltinCalls(Visitor.NodeRefCleanupMixin,
"""
if len(args) != 2:
return node
if not node.type.is_pyobject:
if node.type.is_pyobject:
if operator in ('Eq', 'Ne'):
ret_type = PyrexTypes.c_bint_type
else:
ret_type = PyrexTypes.py_object_type
elif node.type is PyrexTypes.c_bint_type and operator in ('Eq', 'Ne'):
ret_type = PyrexTypes.c_bint_type
else:
return node
# When adding IntNode/FloatNode to something else, assume other operand is also numeric.
......@@ -3285,6 +3289,7 @@ class OptimizeBuiltinCalls(Visitor.NodeRefCleanupMixin,
return node
is_float = isinstance(numval, ExprNodes.FloatNode)
num_type = PyrexTypes.c_double_type if is_float else PyrexTypes.c_long_type
if is_float:
if operator not in ('Add', 'Subtract', 'Remainder', 'TrueDivide', 'Divide', 'Eq', 'Ne'):
return node
......@@ -3292,27 +3297,38 @@ class OptimizeBuiltinCalls(Visitor.NodeRefCleanupMixin,
# mixed old-/new-style division is not currently optimised for integers
return node
elif abs(numval.constant_result) > 2**30:
# Cut off at an integer border that is still safe for all operations.
return node
args = list(args)
args.append((ExprNodes.FloatNode if is_float else ExprNodes.IntNode)(
numval.pos, value=numval.value, constant_result=numval.constant_result,
type=PyrexTypes.c_double_type if is_float else PyrexTypes.c_long_type))
type=num_type))
inplace = node.inplace if isinstance(node, ExprNodes.NumBinopNode) else False
args.append(ExprNodes.BoolNode(node.pos, value=inplace, constant_result=inplace))
utility_code = TempitaUtilityCode.load_cached(
"PyFloatBinop" if is_float else "PyIntBinop", "Optimize.c",
context=dict(op=operator, order=arg_order))
"PyFloatBinop" if is_float else "PyIntCompare" if operator in ('Eq', 'Ne') else "PyIntBinop",
"Optimize.c",
context=dict(op=operator, order=arg_order, ret_type=ret_type))
return self._substitute_method_call(
node, function, "__Pyx_Py%s_%s%s" % ('Float' if is_float else 'Int', operator, arg_order),
self.Pyx_PyFloat_BinopInt_func_type if is_float else self.Pyx_PyInt_BinopInt_func_type,
call_node = self._substitute_method_call(
node, function,
"__Pyx_Py%s_%s%s%s" % (
'Float' if is_float else 'Int',
'' if ret_type.is_pyobject else 'Bool',
operator,
arg_order),
self.Pyx_BinopInt_func_types[(num_type, ret_type)],
'__%s__' % operator[:3].lower(), is_unbound_method, args,
may_return_none=True,
with_none_check=False,
utility_code=utility_code)
if node.type.is_pyobject and not ret_type.is_pyobject:
call_node = ExprNodes.CoerceToPyTypeNode(call_node, self.current_env(), node.type)
return call_node
### unicode type methods
PyUnicode_uchar_predicate_func_type = PyrexTypes.CFuncType(
......
This diff is collapsed.
......@@ -86,6 +86,7 @@ static CYTHON_INLINE size_t __Pyx_Py_UNICODE_strlen(const Py_UNICODE *u) {
#define __Pyx_Owned_Py_None(b) __Pyx_NewRef(Py_None)
static CYTHON_INLINE PyObject * __Pyx_PyBool_FromLong(long b);
static CYTHON_INLINE int __Pyx_PyObject_IsTrue(PyObject*);
static CYTHON_INLINE int __Pyx_PyObject_IsTrueAndDecref(PyObject*);
static CYTHON_INLINE PyObject* __Pyx_PyNumber_IntOrLong(PyObject* x);
#define __Pyx_PySequence_Tuple(obj) \
......@@ -285,6 +286,14 @@ static CYTHON_INLINE int __Pyx_PyObject_IsTrue(PyObject* x) {
else return PyObject_IsTrue(x);
}
static CYTHON_INLINE int __Pyx_PyObject_IsTrueAndDecref(PyObject* x) {
int retval;
if (unlikely(!x)) return -1;
retval = __Pyx_PyObject_IsTrue(x);
Py_DECREF(x);
return retval;
}
static PyObject* __Pyx_PyNumber_IntOrLongWrongResultType(PyObject* result, const char* type_name) {
#if PY_MAJOR_VERSION >= 3
if (PyLong_Check(result)) {
......
"""
Non-test that prints debug information about the current build environment.
"""
from __future__ import print_function
import sys
cdef extern from *:
"""
#ifndef PyLong_SHIFT
#define PyLong_SHIFT 0
typedef int digit;
typedef int sdigit;
#endif
#ifndef PyLong_BASE
#define PyLong_BASE 0
#endif
#ifndef PyLong_MASK
#define PyLong_MASK 0
#endif
"""
# Python runtime
cdef long PY_VERSION_HEX
# Cython config
cdef int CYTHON_COMPILING_IN_CPYTHON
cdef int CYTHON_COMPILING_IN_PYPY
cdef int CYTHON_COMPILING_IN_PYSTON
cdef int CYTHON_USE_PYLONG_INTERNALS
cdef int CYTHON_USE_PYLIST_INTERNALS
cdef int CYTHON_USE_UNICODE_INTERNALS
cdef int CYTHON_USE_UNICODE_WRITER
cdef int CYTHON_AVOID_BORROWED_REFS
cdef int CYTHON_ASSUME_SAFE_MACROS
cdef int CYTHON_UNPACK_METHODS
cdef int CYTHON_FAST_THREAD_STATE
cdef int CYTHON_FAST_PYCALL
cdef int CYTHON_PEP489_MULTI_PHASE_INIT
cdef int CYTHON_USE_TP_FINALIZE
# C and platform specifics
cdef int SIZEOF_INT
cdef int SIZEOF_LONG
cdef int SIZEOF_SIZE_T
cdef int SIZEOF_LONG_LONG
cdef int SIZEOF_VOID_P
# PyLong internals
cdef long PyLong_BASE
cdef long PyLong_MASK
cdef int PyLong_SHIFT
cdef int digit
cdef int sdigit
print(f"""Python build environment:
Python {sys.version_info}
PY_VERSION_HEX 0x{PY_VERSION_HEX:X}
CYTHON_COMPILING_IN_CPYTHON {CYTHON_COMPILING_IN_CPYTHON}
CYTHON_COMPILING_IN_PYPY {CYTHON_COMPILING_IN_PYPY}
CYTHON_COMPILING_IN_PYSTON {CYTHON_COMPILING_IN_PYSTON}
CYTHON_USE_PYLONG_INTERNALS {CYTHON_USE_PYLONG_INTERNALS}
CYTHON_USE_PYLIST_INTERNALS {CYTHON_USE_PYLIST_INTERNALS}
CYTHON_USE_UNICODE_INTERNALS {CYTHON_USE_UNICODE_INTERNALS}
CYTHON_USE_UNICODE_WRITER {CYTHON_USE_UNICODE_WRITER}
CYTHON_AVOID_BORROWED_REFS {CYTHON_AVOID_BORROWED_REFS}
CYTHON_ASSUME_SAFE_MACROS {CYTHON_ASSUME_SAFE_MACROS}
CYTHON_UNPACK_METHODS {CYTHON_UNPACK_METHODS}
CYTHON_FAST_THREAD_STATE {CYTHON_FAST_THREAD_STATE}
CYTHON_FAST_PYCALL {CYTHON_FAST_PYCALL}
CYTHON_PEP489_MULTI_PHASE_INIT {CYTHON_PEP489_MULTI_PHASE_INIT}
CYTHON_USE_TP_FINALIZE {CYTHON_USE_TP_FINALIZE}
PyLong_BASE 0x{PyLong_BASE:X}
PyLong_MASK {PyLong_MASK:X}
PyLong_SHIFT {PyLong_SHIFT}
sizeof(digit) {sizeof(digit)}
sizeof(sdigit) {sizeof(sdigit)}
SIZEOF_INT {SIZEOF_INT} ({sizeof(int)})
SIZEOF_LONG {SIZEOF_LONG} ({sizeof(long)})
SIZEOF_SIZE_T {SIZEOF_SIZE_T} ({sizeof(Py_ssize_t)}, {getattr(sys, 'maxsize', getattr(sys, 'maxint', None))})
SIZEOF_LONG_LONG {SIZEOF_LONG_LONG} ({sizeof(long long)})
SIZEOF_VOID_P {SIZEOF_VOID_P} ({sizeof(void*)})
""")
This diff is collapsed.
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