Commit 52099c3e authored by Stefan Behnel's avatar Stefan Behnel

speed up Python object calculation of 2**N

parent 39bf23cc
......@@ -9,6 +9,8 @@ Latest
Features added
--------------
* The Python expression "2 ** N" was optimised.
* Simple support for declaring Python object types in Python signature
annotations. Currently requires setting the compiler directive
``annotation_typing=True``.
......
......@@ -8111,7 +8111,7 @@ class UnopNode(ExprNode):
self.generate_py_operation_code(code)
def generate_py_operation_code(self, code):
function = self.py_operation_function()
function = self.py_operation_function(code)
code.putln(
"%s = %s(%s); %s" % (
self.result(),
......@@ -8187,7 +8187,7 @@ class UnaryPlusNode(UnopNode):
self.type = PyrexTypes.widest_numeric_type(
self.operand.type, PyrexTypes.c_int_type)
def py_operation_function(self):
def py_operation_function(self, code):
return "PyNumber_Positive"
def calculate_result_code(self):
......@@ -8213,7 +8213,7 @@ class UnaryMinusNode(UnopNode):
if self.type.is_complex:
self.infix = False
def py_operation_function(self):
def py_operation_function(self, code):
return "PyNumber_Negative"
def calculate_result_code(self):
......@@ -8239,7 +8239,7 @@ class TildeNode(UnopNode):
else:
self.type_error()
def py_operation_function(self):
def py_operation_function(self, code):
return "PyNumber_Invert"
def calculate_result_code(self):
......@@ -8874,6 +8874,7 @@ def get_compile_time_binop(node):
% node.operator)
return func
class BinopNode(ExprNode):
# operator string
# operand1 ExprNode
......@@ -8990,7 +8991,7 @@ class BinopNode(ExprNode):
def generate_result_code(self, code):
#print "BinopNode.generate_result_code:", self.operand1, self.operand2 ###
if self.operand1.type.is_pyobject:
function = self.py_operation_function()
function = self.py_operation_function(code)
if self.operator == '**':
extra_args = ", Py_None"
else:
......@@ -9024,7 +9025,7 @@ class CBinopNode(BinopNode):
node.type = PyrexTypes.error_type
return node
def py_operation_function(self):
def py_operation_function(self, code):
return ""
def calculate_result_code(self):
......@@ -9162,7 +9163,7 @@ class NumBinopNode(BinopNode):
type2.is_unicode_char or
BinopNode.is_py_operation_types(self, type1, type2))
def py_operation_function(self):
def py_operation_function(self, code):
function_name = self.py_functions[self.operator]
if self.inplace:
function_name = function_name.replace('PyNumber_', 'PyNumber_InPlace')
......@@ -9228,7 +9229,7 @@ class AddNode(NumBinopNode):
return NumBinopNode.compute_c_result_type(
self, type1, type2)
def py_operation_function(self):
def py_operation_function(self, code):
type1, type2 = self.operand1.type, self.operand2.type
if type1 is unicode_type or type2 is unicode_type:
if type1.is_builtin_type and type2.is_builtin_type:
......@@ -9236,7 +9237,7 @@ class AddNode(NumBinopNode):
return '__Pyx_PyUnicode_ConcatSafe'
else:
return '__Pyx_PyUnicode_Concat'
return super(AddNode, self).py_operation_function()
return super(AddNode, self).py_operation_function(code)
class SubNode(NumBinopNode):
......@@ -9499,7 +9500,7 @@ class ModNode(DivNode):
self.operand1.result(),
self.operand2.result())
def py_operation_function(self):
def py_operation_function(self, code):
if self.operand1.type is unicode_type:
if self.operand1.may_be_none():
return '__Pyx_PyUnicode_FormatSafe'
......@@ -9510,7 +9511,7 @@ class ModNode(DivNode):
return '__Pyx_PyString_FormatSafe'
else:
return '__Pyx_PyString_Format'
return super(ModNode, self).py_operation_function()
return super(ModNode, self).py_operation_function(code)
class PowNode(NumBinopNode):
......@@ -9551,6 +9552,17 @@ class PowNode(NumBinopNode):
typecast(self.operand1),
typecast(self.operand2))
def py_operation_function(self, code):
if (self.type.is_pyobject and
self.operand1.constant_result == 2 and
self.operand2.type is py_object_type):
code.globalstate.use_utility_code(UtilityCode.load_cached('PyNumberPow2', 'Optimize.c'))
if self.inplace:
return '__Pyx_PyNumber_InPlacePowerOf2'
else:
return '__Pyx_PyNumber_PowerOf2'
return super(PowNode, self).py_operation_function(code)
# Note: This class is temporarily "shut down" into an ineffective temp
# allocation mode.
......
......@@ -421,3 +421,44 @@ static double __Pyx__PyObject_AsDouble(PyObject* obj) {
bad:
return (double)-1;
}
/////////////// PyNumberPow2.proto ///////////////
#define __Pyx_PyNumber_InPlacePowerOf2(a, b, c) __Pyx__PyNumber_PowerOf2(a, b, c, 1)
#define __Pyx_PyNumber_PowerOf2(a, b, c) __Pyx__PyNumber_PowerOf2(a, b, c, 0)
static PyObject* __Pyx__PyNumber_PowerOf2(PyObject *two, PyObject *exp, PyObject *none, int inplace); /*proto*/
/////////////// PyNumberPow2 ///////////////
static PyObject* __Pyx__PyNumber_PowerOf2(PyObject *two, PyObject *exp, PyObject *none, int inplace) {
// in CPython, 1<<N is substantially faster than 2**N
// TODO: disable this in Py3.5 if http://bugs.python.org/issue21420 gets accepted
#if CYTHON_COMPILING_IN_CPYTHON
Py_ssize_t shiftby;
if (likely(PyLong_Check(exp))) {
shiftby = PyLong_AsSsize_t(exp);
#if PY_MAJOR_VERSION < 3
} else if (likely(PyInt_Check(exp))) {
shiftby = PyInt_AsLong(exp);
#endif
} else {
goto fallback;
}
if (likely(shiftby >= 0)) {
if ((size_t)shiftby <= sizeof(long) * 8 - 2) {
long value = 1L << shiftby;
return PyInt_FromLong(value);
} else {
PyObject *one = PyInt_FromLong(1L);
if (unlikely(!one)) return NULL;
return PyNumber_Lshift(one, exp);
}
} else if (shiftby == -1 && PyErr_Occurred()) {
PyErr_Clear();
}
fallback:
#endif
return (inplace ? PyNumber_InPlacePower : PyNumber_Power)(two, exp, none);
}
......@@ -10,6 +10,7 @@ def f(obj2, obj3):
obj1 = obj2 ** obj3
return flt1, obj1
def g(i):
"""
>>> g(4)
......@@ -17,6 +18,7 @@ def g(i):
"""
return i ** 5
def h(i):
"""
>>> h(4)
......@@ -24,6 +26,7 @@ def h(i):
"""
return 5 ** i
def constant_py():
"""
>>> constant_py() == 2 ** 10
......@@ -32,6 +35,7 @@ def constant_py():
result = (<object>2) ** 10
return result
def constant_long():
"""
>>> constant_long() == 2 ** 36
......@@ -40,6 +44,7 @@ def constant_long():
result = (<object>2L) ** 36
return result
def small_int_pow(long s):
"""
>>> small_int_pow(3)
......@@ -49,6 +54,7 @@ def small_int_pow(long s):
"""
return s**0, s**1, s**2, s**3, s**4
def int_pow(short a, short b):
"""
>>> int_pow(7, 2)
......@@ -59,3 +65,30 @@ def int_pow(short a, short b):
1024
"""
return a**b
def optimised_pow2(n):
"""
>>> optimised_pow2(0)
1
>>> optimised_pow2(1)
2
>>> optimised_pow2(10)
1024
>>> optimised_pow2(30)
1073741824
>>> print(repr(optimised_pow2(32)).rstrip('L'))
4294967296
>>> print(repr(optimised_pow2(100)).rstrip('L'))
1267650600228229401496703205376
>>> optimised_pow2(30000) == 2 ** 30000
True
>>> optimised_pow2(-1)
0.5
>>> optimised_pow2(0.5) == 2 ** 0.5
True
>>> optimised_pow2('test')
Traceback (most recent call last):
TypeError: unsupported operand type(s) for ** or pow(): 'int' and 'str'
"""
return 2 ** n
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