Commit 1e9b546a authored by Stefan Behnel's avatar Stefan Behnel

speed up binary and/or/xor operations with constant Python integers

parent 304f0a87
......@@ -17,6 +17,8 @@ Features added
* Adding/subtracting constant Python floats and small integers is faster.
* Binary and/or/xor operations with small constant Python integers are faster.
Bugs fixed
----------
......
......@@ -2802,6 +2802,15 @@ class OptimizeBuiltinCalls(Visitor.NodeRefCleanupMixin,
def _handle_simple_method_object___sub__(self, node, function, args, is_unbound_method):
return self._optimise_num_binop('Subtract', node, function, args, is_unbound_method)
def _handle_simple_method_object___and__(self, node, function, args, is_unbound_method):
return self._optimise_num_binop('And', node, function, args, is_unbound_method)
def _handle_simple_method_object___or__(self, node, function, args, is_unbound_method):
return self._optimise_num_binop('Or', node, function, args, is_unbound_method)
def _handle_simple_method_object___xor__(self, node, function, args, is_unbound_method):
return self._optimise_num_binop('Xor', node, function, args, is_unbound_method)
def _handle_simple_method_float___add__(self, node, function, args, is_unbound_method):
return self._optimise_num_binop('Add', node, function, args, is_unbound_method)
......@@ -2832,8 +2841,14 @@ class OptimizeBuiltinCalls(Visitor.NodeRefCleanupMixin,
else:
return node
if not numval.has_constant_result():
return node
is_float = isinstance(numval, ExprNodes.FloatNode)
if not numval.has_constant_result() or (not is_float and abs(numval.constant_result) > 2**30):
if is_float:
if operator not in ('Add', 'Subtract'):
return node
elif abs(numval.constant_result) > 2**30:
return node
args = list(args)
......
......@@ -493,20 +493,30 @@ static PyObject* __Pyx_PyInt_{{op}}{{order}}(PyObject *op1, PyObject *op2, long
#if CYTHON_COMPILING_IN_CPYTHON
{{py: pyval, ival = ('op2', 'b') if order == 'CObj' else ('op1', 'a') }}
{{py: c_op = '+' if op == 'Add' else '-' }}
{{py: c_op = {'Add': '+', 'Subtract': '-', 'Or': '|', 'Xor': '^', 'And': '&'}[op] }}
static PyObject* __Pyx_PyInt_{{op}}{{order}}(PyObject *op1, PyObject *op2, long intval, int inplace) {
const long {{'a' if order == 'CObj' else 'b'}} = intval;
#if PY_MAJOR_VERSION < 3
if (likely(PyInt_CheckExact({{pyval}}))) {
long x, {{ival}};
{{ival}} = PyInt_AS_LONG({{pyval}});
{{if c_op in '+-'}}
long x;
{{endif}}
long {{ival}} = PyInt_AS_LONG({{pyval}});
{{if c_op not in '+-'}}
// binary operators are safe, no overflow
return PyInt_FromLong(a {{c_op}} b);
{{else}}
// adapted from intobject.c in Py2.7:
// casts in the line below avoid undefined behaviour on overflow
x = (long)((unsigned long)a {{c_op}} b);
if (likely((x^a) >= 0 || (x^{{ '~' if op == 'Subtract' else '' }}b) >= 0))
return PyInt_FromLong(x);
{{endif}}
return PyLong_Type.tp_as_number->nb_{{op.lower()}}(op1, op2);
}
#endif
......@@ -524,6 +534,7 @@ static PyObject* __Pyx_PyInt_{{op}}{{order}}(PyObject *op1, PyObject *op2, long
}
#endif
{{if c_op in '+-'}}
if (PyFloat_CheckExact({{pyval}})) {
double result;
double {{ival}} = PyFloat_AS_DOUBLE({{pyval}});
......@@ -533,6 +544,7 @@ static PyObject* __Pyx_PyInt_{{op}}{{order}}(PyObject *op1, PyObject *op2, long
PyFPE_END_PROTECT(result)
return PyFloat_FromDouble(result);
}
{{endif}}
return (inplace ? PyNumber_InPlace{{op}} : PyNumber_{{op}})(op1, op2);
}
#endif
......
def f(obj1, obj2, obj3):
# mode: run
def or_obj(obj2, obj3):
"""
>>> f(1,2,3)
>>> or_obj(2, 3)
3
"""
obj1 = obj2 | obj3
return obj1
def g(obj1, obj2, obj3):
def or_int(obj2):
"""
>>> or_int(1)
17
>>> or_int(16)
16
"""
obj1 = obj2 | 0x10
return obj1
def xor_obj(obj2, obj3):
"""
>>> g(1,2,3)
>>> xor_obj(2, 3)
1
"""
obj1 = obj2 ^ obj3
return obj1
def h(obj1, obj2, obj3):
def xor_int(obj2):
"""
>>> xor_int(2)
18
>>> xor_int(16)
0
"""
obj1 = obj2 ^ 0x10
return obj1
def and_obj(obj2, obj3):
"""
>>> h(1,2,3)
>>> and_obj(2, 3)
2
"""
obj1 = obj2 & obj3
return obj1
def j(obj1, obj2, obj3):
def and_int(obj2):
"""
>>> j(1,2,3)
>>> and_int(1)
0
>>> and_int(18)
16
"""
obj1 = obj2 & 0x10
return obj1
def lshift_obj(obj2, obj3):
"""
>>> lshift_obj(2, 3)
16
"""
obj1 = obj2 << obj3
return obj1
def k(obj1, obj2, obj3):
def rshift_obj(obj2, obj3):
"""
>>> k(1,2,3)
>>> rshift_obj(2, 3)
0
"""
obj1 = obj2 >> obj3
return obj1
def l(obj1, obj2, obj3):
def mixed_obj(obj2, obj3):
"""
>>> l(1,2,3)
>>> mixed_obj(2, 3)
16
"""
obj1 = obj2 << obj3 | obj2 >> obj3
return obj1
def mixed_int(obj2):
"""
>>> mixed_int(2)
18
>>> mixed_int(16)
0
>>> mixed_int(17)
1
"""
obj1 = (obj2 ^ 0x10) | (obj2 & 0x01)
return obj1
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