Commit a6bf4856 authored by Stefan Behnel's avatar Stefan Behnel

speed up (x % pyint)

parent 001ed48f
...@@ -17,7 +17,8 @@ Features added ...@@ -17,7 +17,8 @@ Features added
* Adding/subtracting constant Python floats and small integers is faster. * Adding/subtracting constant Python floats and small integers is faster.
* Binary and/or/xor operations with small constant Python integers are faster. * Binary and/or/xor operations and modulus with small constant Python integers
are faster.
Bugs fixed Bugs fixed
---------- ----------
......
...@@ -2811,6 +2811,13 @@ class OptimizeBuiltinCalls(Visitor.NodeRefCleanupMixin, ...@@ -2811,6 +2811,13 @@ class OptimizeBuiltinCalls(Visitor.NodeRefCleanupMixin,
def _handle_simple_method_object___xor__(self, 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) return self._optimise_num_binop('Xor', node, function, args, is_unbound_method)
def _handle_simple_method_object___mod__(self, node, function, args, is_unbound_method):
if len(args) != 2 or not isinstance(args[1], ExprNodes.IntNode):
return node
if not args[1].has_constant_result() or not (2 <= args[1].constant_result <= 2**30):
return node
return self._optimise_num_binop('Remainder', node, function, args, is_unbound_method)
def _handle_simple_method_float___add__(self, 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) return self._optimise_num_binop('Add', node, function, args, is_unbound_method)
......
...@@ -493,28 +493,32 @@ static PyObject* __Pyx_PyInt_{{op}}{{order}}(PyObject *op1, PyObject *op2, long ...@@ -493,28 +493,32 @@ static PyObject* __Pyx_PyInt_{{op}}{{order}}(PyObject *op1, PyObject *op2, long
#if CYTHON_COMPILING_IN_CPYTHON #if CYTHON_COMPILING_IN_CPYTHON
{{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': '-', 'Or': '|', 'Xor': '^', 'And': '&'}[op] }} {{py: c_op = {'Add': '+', 'Subtract': '-', 'Remainder': '%', '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;
static PyObject* __Pyx_PyInt_{{op}}{{order}}(PyObject *op1, PyObject *op2, CYTHON_UNUSED long intval, int inplace) {
#if PY_MAJOR_VERSION < 3 #if PY_MAJOR_VERSION < 3
if (likely(PyInt_CheckExact({{pyval}}))) { if (likely(PyInt_CheckExact({{pyval}}))) {
const long {{'a' if order == 'CObj' else 'b'}} = intval;
{{if c_op in '+-'}} {{if c_op in '+-'}}
long x; long x;
{{endif}} {{endif}}
long {{ival}} = PyInt_AS_LONG({{pyval}}); long {{ival}} = PyInt_AS_LONG({{pyval}});
{{if c_op not in '+-'}} {{if c_op in '+-'}}
// binary operators are safe, no overflow // adapted from intobject.c in Py2.7:
return PyInt_FromLong(a {{c_op}} b); // 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);
{{else}} {{else}}
// adapted from intobject.c in Py2.7: {{if c_op == '%'}}
// casts in the line below avoid undefined behaviour on overflow // modulus with differing signs isn't safely portable
x = (long)((unsigned long)a {{c_op}} b); if (unlikely({{ival}} < 0))
if (likely((x^a) >= 0 || (x^{{ '~' if op == 'Subtract' else '' }}b) >= 0)) return (inplace ? PyNumber_InPlace{{op}} : PyNumber_{{op}})(op1, op2);
return PyInt_FromLong(x); {{endif}}
// other operations are safe, no overflow
return PyInt_FromLong(a {{c_op}} b);
{{endif}} {{endif}}
return PyLong_Type.tp_as_number->nb_{{op.lower()}}(op1, op2); return PyLong_Type.tp_as_number->nb_{{op.lower()}}(op1, op2);
...@@ -523,11 +527,20 @@ static PyObject* __Pyx_PyInt_{{op}}{{order}}(PyObject *op1, PyObject *op2, long ...@@ -523,11 +527,20 @@ static PyObject* __Pyx_PyInt_{{op}}{{order}}(PyObject *op1, PyObject *op2, long
#if PY_MAJOR_VERSION >= 3 && CYTHON_USE_PYLONG_INTERNALS #if PY_MAJOR_VERSION >= 3 && CYTHON_USE_PYLONG_INTERNALS
if (likely(PyLong_CheckExact({{pyval}}))) { if (likely(PyLong_CheckExact({{pyval}}))) {
const long {{'a' if order == 'CObj' else 'b'}} = intval;
long {{ival}}; long {{ival}};
switch (Py_SIZE({{pyval}})) { switch (Py_SIZE({{pyval}})) {
{{if c_op != '%'}}
case -1: {{ival}} = -(sdigit)((PyLongObject*){{pyval}})->ob_digit[0]; break; case -1: {{ival}} = -(sdigit)((PyLongObject*){{pyval}})->ob_digit[0]; break;
{{endif}}
case 0: {{ival}} = 0; break; case 0: {{ival}} = 0; break;
case 1: {{ival}} = ((PyLongObject*){{pyval}})->ob_digit[0]; break; case 1: {{ival}} = ((PyLongObject*){{pyval}})->ob_digit[0]; break;
case 2:
if (8 * sizeof(long) > 2 * PyLong_SHIFT) {
{{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);
} }
return PyLong_FromLong(a {{c_op}} b); return PyLong_FromLong(a {{c_op}} b);
...@@ -536,6 +549,7 @@ static PyObject* __Pyx_PyInt_{{op}}{{order}}(PyObject *op1, PyObject *op2, long ...@@ -536,6 +549,7 @@ static PyObject* __Pyx_PyInt_{{op}}{{order}}(PyObject *op1, PyObject *op2, long
{{if c_op in '+-'}} {{if c_op in '+-'}}
if (PyFloat_CheckExact({{pyval}})) { if (PyFloat_CheckExact({{pyval}})) {
const long {{'a' if order == 'CObj' else 'b'}} = intval;
double result; double result;
double {{ival}} = PyFloat_AS_DOUBLE({{pyval}}); double {{ival}} = PyFloat_AS_DOUBLE({{pyval}});
// copied from floatobject.c in Py3.5: // copied from floatobject.c in Py3.5:
......
cimport cython cimport cython
def bigint(x):
print(str(x).rstrip('L'))
def mixed_test(): def mixed_test():
""" """
>>> mixed_test() >>> mixed_test()
...@@ -29,6 +33,10 @@ def add_x_1(x): ...@@ -29,6 +33,10 @@ def add_x_1(x):
2 2
>>> add_x_1(-1) >>> add_x_1(-1)
0 0
>>> bigint(2**50 + 1)
1125899906842625
>>> bigint(add_x_1(2**50))
1125899906842625
>>> add_x_1(1.5) >>> add_x_1(1.5)
2.5 2.5
>>> add_x_1(-1.5) >>> add_x_1(-1.5)
...@@ -73,13 +81,17 @@ def add_x_large(x): ...@@ -73,13 +81,17 @@ def add_x_large(x):
-1073741824.0 -1073741824.0
>>> add_x_large(2**30 + 1) >>> add_x_large(2**30 + 1)
2147483649 2147483649
>>> bigint(2**50 + 1 + 2**30)
1125900980584449
>>> bigint(add_x_large(2**50 + 1))
1125900980584449
>>> 2**31 + 2**30 >>> 2**31 + 2**30
3221225472 3221225472
>>> add_x_large(2**31) >>> add_x_large(2**31)
3221225472 3221225472
>>> print(2**66 + 2**30) >>> bigint(2**66 + 2**30)
73786976295911948288 73786976295911948288
>>> print(add_x_large(2**66)) >>> bigint(add_x_large(2**66))
73786976295911948288 73786976295911948288
>>> try: add_x_large("abc") >>> try: add_x_large("abc")
... except TypeError: pass ... except TypeError: pass
...@@ -96,6 +108,10 @@ def add_1_x(x): ...@@ -96,6 +108,10 @@ def add_1_x(x):
2 2
>>> add_1_x(-1) >>> add_1_x(-1)
0 0
>>> bigint(2**50 + 1)
1125899906842625
>>> bigint(add_1_x(2**50))
1125899906842625
>>> add_1_x(1.5) >>> add_1_x(1.5)
2.5 2.5
>>> add_1_x(-1.5) >>> add_1_x(-1.5)
......
import sys import sys
if sys.version_info[0] < 3:
__doc__ = u"""
>>> modptr()
'spameggs'
"""
def modobj(obj2, obj3): def modobj(obj2, obj3):
""" """
...@@ -15,6 +11,73 @@ def modobj(obj2, obj3): ...@@ -15,6 +11,73 @@ def modobj(obj2, obj3):
obj1 = obj2 % obj3 obj1 = obj2 % obj3
return obj1 return obj1
def mod_obj_10(int2):
"""
>>> 0 % 10
0
>>> mod_obj_10(0)
0
>>> 1 % 10
1
>>> mod_obj_10(1)
1
>>> 9 % 10
9
>>> mod_obj_10(9)
9
>>> 10 % 10
0
>>> mod_obj_10(10)
0
>>> 10002 % 10
2
>>> mod_obj_10(10002)
2
>>> int((2**50) % 10)
4
>>> int(mod_obj_10(2**50))
4
>>> int((2**200) % 10)
6
>>> int(mod_obj_10(2**200))
6
>>> (-1) % 10
9
>>> mod_obj_10(-1)
9
>>> (-10) % 10
0
>>> mod_obj_10(-10)
0
>>> (-12) % 10
8
>>> mod_obj_10(-12)
8
"""
int1 = int2 % 10
return int1
def mod_obj_m2(int2):
"""
>>> 0 % -2
0
>>> mod_obj_m2(0)
0
>>> 1 % -2
-1
>>> mod_obj_m2(1)
-1
>>> 9 % -2
-1
>>> mod_obj_m2(9)
-1
"""
int1 = int2 % -2
return int1
def modint(int int2, int int3): def modint(int int2, int int3):
""" """
>>> modint(9,2) >>> modint(9,2)
...@@ -24,9 +87,14 @@ def modint(int int2, int int3): ...@@ -24,9 +87,14 @@ def modint(int int2, int int3):
int1 = int2 % int3 int1 = int2 % int3
return int1 return int1
def modptr(): def modptr():
"""
>>> print(modptr() if sys.version_info[0] < 3 else 'spameggs')
spameggs
"""
cdef char *str2, *str3 cdef char *str2, *str3
str2 = "spam%s" str2 = "spam%s"
str3 = "eggs" str3 = "eggs"
obj1 = str2 % str3 # '%' operator doesn't work on byte strings in Py3 obj1 = str2 % str3 # '%' operator doesn't work on byte strings in Py3
return obj1 return obj1
cimport cython cimport cython
def bigint(x):
print(str(x).rstrip('L'))
def mixed_test(): def mixed_test():
""" """
>>> mixed_test() >>> mixed_test()
...@@ -44,6 +48,10 @@ def sub_x_1(x): ...@@ -44,6 +48,10 @@ def sub_x_1(x):
0 0
>>> sub_x_1(-1) >>> sub_x_1(-1)
-2 -2
>>> bigint(2**50 - 1)
1125899906842623
>>> bigint(sub_x_1(2**50))
1125899906842623
>>> sub_x_1(1.5) >>> sub_x_1(1.5)
0.5 0.5
>>> sub_x_1(-1.5) >>> sub_x_1(-1.5)
...@@ -63,6 +71,10 @@ def sub_x_1f(x): ...@@ -63,6 +71,10 @@ def sub_x_1f(x):
0.0 0.0
>>> sub_x_1f(-1) >>> sub_x_1f(-1)
-2.0 -2.0
>>> 2**50 - 1.0
1125899906842623.0
>>> sub_x_1f(2**50)
1125899906842623.0
>>> sub_x_1f(1.5) >>> sub_x_1f(1.5)
0.5 0.5
>>> sub_x_1f(-1.5) >>> sub_x_1f(-1.5)
...@@ -82,6 +94,10 @@ def sub_x_large(x): ...@@ -82,6 +94,10 @@ def sub_x_large(x):
-1073741823 -1073741823
>>> sub_x_large(-1) >>> sub_x_large(-1)
-1073741825 -1073741825
>>> bigint(2**50 - 2**30)
1125898833100800
>>> bigint(sub_x_large(2**50))
1125898833100800
>>> sub_x_large(2.0**30) >>> sub_x_large(2.0**30)
0.0 0.0
>>> sub_x_large(2.0**30 + 1) >>> sub_x_large(2.0**30 + 1)
...@@ -107,6 +123,10 @@ def sub_1_x(x): ...@@ -107,6 +123,10 @@ def sub_1_x(x):
2 2
>>> sub_1_x(1) >>> sub_1_x(1)
0 0
>>> bigint(1 - 2**50)
-1125899906842623
>>> bigint(sub_1_x(2**50))
-1125899906842623
>>> sub_1_x(1.5) >>> sub_1_x(1.5)
-0.5 -0.5
>>> sub_1_x(-1.5) >>> sub_1_x(-1.5)
......
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