Commit 99d47bff authored by Stefan Behnel's avatar Stefan Behnel

optimise division with constant floats

parent 20a10e73
...@@ -17,7 +17,7 @@ Features added ...@@ -17,7 +17,7 @@ Features added
* Optimisations for PyLong are enabled in Py2.7 (not only Py3.x). * Optimisations for PyLong are enabled in Py2.7 (not only Py3.x).
* Adding/subtracting constant Python floats and small integers is faster. * Adding/subtracting/dividing constant Python floats and small integers is faster.
* Binary and/or/xor/rshift operations and division/modulus with small * Binary and/or/xor/rshift operations and division/modulus with small
constant Python integers are faster. constant Python integers are faster.
......
...@@ -2831,6 +2831,9 @@ class OptimizeBuiltinCalls(Visitor.NodeRefCleanupMixin, ...@@ -2831,6 +2831,9 @@ class OptimizeBuiltinCalls(Visitor.NodeRefCleanupMixin,
def _handle_simple_method_object___truediv__(self, node, function, args, is_unbound_method): def _handle_simple_method_object___truediv__(self, node, function, args, is_unbound_method):
return self._optimise_num_div('TrueDivide', node, function, args, is_unbound_method) return self._optimise_num_div('TrueDivide', node, function, args, is_unbound_method)
def _handle_simple_method_object___div__(self, node, function, args, is_unbound_method):
return self._optimise_num_div('Divide', node, function, args, is_unbound_method)
def _optimise_num_div(self, operator, node, function, args, is_unbound_method): def _optimise_num_div(self, operator, node, function, args, is_unbound_method):
if len(args) != 2 or not args[1].has_constant_result() or args[1].constant_result == 0: if len(args) != 2 or not args[1].has_constant_result() or args[1].constant_result == 0:
return node return node
...@@ -2850,6 +2853,12 @@ class OptimizeBuiltinCalls(Visitor.NodeRefCleanupMixin, ...@@ -2850,6 +2853,12 @@ class OptimizeBuiltinCalls(Visitor.NodeRefCleanupMixin,
def _handle_simple_method_float___sub__(self, node, function, args, is_unbound_method): def _handle_simple_method_float___sub__(self, node, function, args, is_unbound_method):
return self._optimise_num_binop('Subtract', node, function, args, is_unbound_method) return self._optimise_num_binop('Subtract', node, function, args, is_unbound_method)
def _handle_simple_method_float___truediv__(self, node, function, args, is_unbound_method):
return self._optimise_num_binop('TrueDivide', node, function, args, is_unbound_method)
def _handle_simple_method_float___div__(self, node, function, args, is_unbound_method):
return self._optimise_num_binop('Divide', node, function, args, is_unbound_method)
def _optimise_num_binop(self, operator, node, function, args, is_unbound_method): def _optimise_num_binop(self, operator, node, function, args, is_unbound_method):
""" """
Optimise math operators for (likely) float or small integer operations. Optimise math operators for (likely) float or small integer operations.
...@@ -2880,7 +2889,7 @@ class OptimizeBuiltinCalls(Visitor.NodeRefCleanupMixin, ...@@ -2880,7 +2889,7 @@ class OptimizeBuiltinCalls(Visitor.NodeRefCleanupMixin,
is_float = isinstance(numval, ExprNodes.FloatNode) is_float = isinstance(numval, ExprNodes.FloatNode)
if is_float: if is_float:
if operator not in ('Add', 'Subtract', 'TrueDivide'): if operator not in ('Add', 'Subtract', 'TrueDivide', 'Divide'):
return node return node
elif abs(numval.constant_result) > 2**30: elif abs(numval.constant_result) > 2**30:
return node return node
......
...@@ -490,7 +490,7 @@ fallback: ...@@ -490,7 +490,7 @@ fallback:
static PyObject* __Pyx_PyInt_{{op}}{{order}}(PyObject *op1, PyObject *op2, long intval, int inplace); /*proto*/ static PyObject* __Pyx_PyInt_{{op}}{{order}}(PyObject *op1, PyObject *op2, long intval, int inplace); /*proto*/
#else #else
#define __Pyx_PyInt_{{op}}{{order}}(op1, op2, intval, inplace) \ #define __Pyx_PyInt_{{op}}{{order}}(op1, op2, intval, inplace) \
((inplace ? PyNumber_InPlace{{op}} : PyNumber_{{op}})(op1, op2)) (inplace ? PyNumber_InPlace{{op}}(op1, op2) : PyNumber_{{op}}(op1, op2))
#endif #endif
/////////////// PyIntBinop /////////////// /////////////// PyIntBinop ///////////////
...@@ -639,7 +639,9 @@ static PyObject* __Pyx_PyInt_{{op}}{{order}}(PyObject *op1, PyObject *op2, CYTHO ...@@ -639,7 +639,9 @@ static PyObject* __Pyx_PyInt_{{op}}{{order}}(PyObject *op1, PyObject *op2, CYTHO
static PyObject* __Pyx_PyFloat_{{op}}{{order}}(PyObject *op1, PyObject *op2, double floatval, int inplace); /*proto*/ static PyObject* __Pyx_PyFloat_{{op}}{{order}}(PyObject *op1, PyObject *op2, double floatval, int inplace); /*proto*/
#else #else
#define __Pyx_PyFloat_{{op}}{{order}}(op1, op2, floatval, inplace) \ #define __Pyx_PyFloat_{{op}}{{order}}(op1, op2, floatval, inplace) \
((inplace ? PyNumber_InPlace{{op}} : PyNumber_{{op}})(op1, op2)) {{if op == 'Divide'}}((inplace ? __Pyx_PyNumber_InPlaceDivide(op1, op2) : __Pyx_PyNumber_Divide(op1, op2)))
{{else}}(inplace ? PyNumber_InPlace{{op}}(op1, op2) : PyNumber_{{op}}(op1, op2))
{{endif}}
#endif #endif
/////////////// PyFloatBinop /////////////// /////////////// PyFloatBinop ///////////////
...@@ -648,7 +650,7 @@ static PyObject* __Pyx_PyFloat_{{op}}{{order}}(PyObject *op1, PyObject *op2, dou ...@@ -648,7 +650,7 @@ static PyObject* __Pyx_PyFloat_{{op}}{{order}}(PyObject *op1, PyObject *op2, dou
#if CYTHON_COMPILING_IN_CPYTHON #if CYTHON_COMPILING_IN_CPYTHON
{{py: from Cython.Utility import pylong_join }} {{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 = {'Add': '+', 'Subtract': '-', 'TrueDivide': '/'}[op] }} {{py: c_op = {'Add': '+', 'Subtract': '-', 'TrueDivide': '/', 'Divide': '/'}[op] }}
static PyObject* __Pyx_PyFloat_{{op}}{{order}}(PyObject *op1, PyObject *op2, double floatval, int inplace) { static PyObject* __Pyx_PyFloat_{{op}}{{order}}(PyObject *op1, PyObject *op2, double floatval, int inplace) {
const double {{'a' if order == 'CObj' else 'b'}} = floatval; const double {{'a' if order == 'CObj' else 'b'}} = floatval;
...@@ -699,7 +701,11 @@ static PyObject* __Pyx_PyFloat_{{op}}{{order}}(PyObject *op1, PyObject *op2, dou ...@@ -699,7 +701,11 @@ static PyObject* __Pyx_PyFloat_{{op}}{{order}}(PyObject *op1, PyObject *op2, dou
if (unlikely({{fval}} == -1.0 && PyErr_Occurred())) return NULL; if (unlikely({{fval}} == -1.0 && PyErr_Occurred())) return NULL;
#endif #endif
} else { } else {
{{if op == 'Divide'}}
return (inplace ? __Pyx_PyNumber_InPlaceDivide(op1, op2) : __Pyx_PyNumber_Divide(op1, op2));
{{else}}
return (inplace ? PyNumber_InPlace{{op}} : PyNumber_{{op}})(op1, op2); return (inplace ? PyNumber_InPlace{{op}} : PyNumber_{{op}})(op1, op2);
{{endif}}
} }
// copied from floatobject.c in Py3.5: // copied from floatobject.c in Py3.5:
......
# mode: run
# tag: division
def int_by_float():
"""
>>> int_by_float()
0.5
"""
return 1 / 2.0
def float_by_int():
"""
>>> float_by_int()
2.0
"""
return 2.0 / 1
def float_by_float():
"""
>>> float_by_float()
1.5
"""
return 3.0 / 2.0
def div_1_by(x):
"""
>>> div_1_by(1.0)
1.0
>>> div_1_by(2.0)
0.5
>>> div_1_by(0.5)
2.0
>>> 1.0 / float('inf')
0.0
>>> div_1_by(float('inf'))
0.0
>>> div_1_by(float('-inf'))
-0.0
>>> div_1_by(float('nan'))
nan
"""
return 1.0 / x
def div_by_2(x):
"""
>>> div_by_2(1.0)
0.5
>>> float('inf') / 2.0
inf
>>> div_by_2(float('inf'))
inf
>>> div_by_2(float('-inf'))
-inf
>>> float('nan') / 2.0
nan
>>> div_by_2(float('nan'))
nan
"""
return x / 2.0
def div_by_neg_2(x):
"""
>>> div_by_neg_2(1.0)
-0.5
>>> div_by_neg_2(-1.0)
0.5
>>> (-2**14) / (-2.0)
8192.0
>>> div_by_neg_2(-2**14)
8192.0
>>> (-2**52) / (-2.0)
2251799813685248.0
>>> div_by_neg_2(-2**52)
2251799813685248.0
>>> (-2**53-1) / (-2.0)
4503599627370496.0
>>> div_by_neg_2(-2**53-1)
4503599627370496.0
>>> float('inf') / -2.0
-inf
>>> div_by_neg_2(float('inf'))
-inf
>>> div_by_neg_2(float('-inf'))
inf
>>> float('nan') / -2.0
nan
>>> div_by_neg_2(float('nan'))
nan
"""
return x / -2.0
def div_neg_2_by(x):
"""
>>> div_neg_2_by(1.0)
-2.0
>>> div_neg_2_by(-1)
2.0
>>> div_neg_2_by(-2.0)
1.0
>>> div_neg_2_by(-2)
1.0
>>> -2.0 / float('inf')
-0.0
>>> div_neg_2_by(float('inf'))
-0.0
>>> div_neg_2_by(float('-inf'))
0.0
>>> float('nan') / -2.0
nan
>>> div_neg_2_by(float('nan'))
nan
"""
return (-2.0) / x
def div_by_nan(x):
"""
>>> 1.0 / float('nan')
nan
>>> div_by_nan(1.0)
nan
>>> float('nan') / float('nan')
nan
>>> div_by_nan(float('nan'))
nan
>>> float('inf') / float('nan')
nan
>>> div_by_nan(float('inf'))
nan
"""
return x / float("nan")
def div_nan_by(x):
"""
>>> float('nan') / 1.0
nan
>>> div_nan_by(1.0)
nan
>>> float('nan') / float('nan')
nan
>>> div_nan_by(float('nan'))
nan
"""
return float("nan") / x
def div_by_inf(x):
"""
>>> 1 / float('inf')
0.0
>>> div_by_inf(1)
0.0
>>> 1.0 / float('inf')
0.0
>>> div_by_inf(1.0)
0.0
>>> div_by_inf(float('inf'))
nan
"""
return x / float("inf")
def div_inf_by(x):
"""
>>> float('inf') / 1.0
inf
>>> div_inf_by(1.0)
inf
>>> float('inf') / float('nan')
nan
>>> div_inf_by(float('nan'))
nan
>>> float('inf') / float('-inf')
nan
>>> div_inf_by(float('-inf'))
nan
"""
return float("inf") / x
def div_neg_inf_by(x):
"""
>>> float('-inf') / 1.0
-inf
>>> div_neg_inf_by(1.0)
-inf
>>> float('-inf') / -1.0
inf
>>> div_neg_inf_by(-1.0)
inf
"""
return float("-inf") / x
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