Commit b8dc03f8 authored by Robert Bradshaw's avatar Robert Bradshaw

fast cdef type indexing into lists/tuples via runtime type checking

- 5 times as fast for L[i], list/tuple L, cdef integer type i, 0 <= i < len(L)
- no change for L[a], python object a
- 1-1.5% slowdown for L[i], i negative or out of range
parent ae87153d
...@@ -1035,7 +1035,7 @@ class IndexNode(ExprNode): ...@@ -1035,7 +1035,7 @@ class IndexNode(ExprNode):
# base ExprNode # base ExprNode
# index ExprNode # index ExprNode
subexprs = ['base', 'index'] subexprs = ['base', 'index', 'py_index']
def is_ephemeral(self): def is_ephemeral(self):
return self.base.is_ephemeral() return self.base.is_ephemeral()
...@@ -1047,7 +1047,12 @@ class IndexNode(ExprNode): ...@@ -1047,7 +1047,12 @@ class IndexNode(ExprNode):
self.base.analyse_types(env) self.base.analyse_types(env)
self.index.analyse_types(env) self.index.analyse_types(env)
if self.base.type.is_pyobject: if self.base.type.is_pyobject:
if self.index.type.is_int:
self.index = self.index.coerce_to(PyrexTypes.c_py_ssize_t_type, env).coerce_to_simple(env)
self.py_index = CloneNode(self.index).coerce_to_pyobject(env)
else:
self.index = self.index.coerce_to_pyobject(env) self.index = self.index.coerce_to_pyobject(env)
self.py_index = CloneNode(self.index)
self.type = py_object_type self.type = py_object_type
self.is_temp = 1 self.is_temp = 1
else: else:
...@@ -1077,25 +1082,79 @@ class IndexNode(ExprNode): ...@@ -1077,25 +1082,79 @@ class IndexNode(ExprNode):
return "(%s[%s])" % ( return "(%s[%s])" % (
self.base.result_code, self.index.result_code) self.base.result_code, self.index.result_code)
def generate_subexpr_evaluation_code(self, code):
# do not evaluate self.py_index in case we don't need it
self.base.generate_evaluation_code(code)
self.index.generate_evaluation_code(code)
def generate_subexpr_disposal_code(self, code):
# if we used self.py_index, it will be disposed of manually
self.base.generate_disposal_code(code)
self.index.generate_disposal_code(code)
def generate_result_code(self, code): def generate_result_code(self, code):
if self.type.is_pyobject: if self.type.is_pyobject:
if self.index.type.is_int:
code.putln("if (PyList_CheckExact(%s) && 0 <= %s && %s < PyList_GET_SIZE(%s)) {" % (
self.base.py_result(),
self.index.result_code,
self.index.result_code,
self.base.py_result()))
code.putln("%s = PyList_GET_ITEM(%s, %s); Py_INCREF(%s);" % (
self.result_code,
self.base.py_result(),
self.index.result_code,
self.result_code))
code.putln("} else if (PyTuple_CheckExact(%s) && 0 <= %s && %s < PyTuple_GET_SIZE(%s)) {" % (
self.base.py_result(),
self.index.result_code,
self.index.result_code,
self.base.py_result()))
code.putln("%s = PyTuple_GET_ITEM(%s, %s); Py_INCREF(%s);" % (
self.result_code,
self.base.py_result(),
self.index.result_code,
self.result_code))
code.putln("} else {")
self.generate_generic_code_result(code)
code.putln("}")
else:
self.generate_generic_code_result(code)
def generate_generic_code_result(self, code):
self.py_index.generate_result_code(code)
code.putln( code.putln(
"%s = PyObject_GetItem(%s, %s); if (!%s) %s" % ( "%s = PyObject_GetItem(%s, %s); if (!%s) %s" % (
self.result_code, self.result_code,
self.base.py_result(), self.base.py_result(),
self.index.py_result(), self.py_index.py_result(),
self.result_code, self.result_code,
code.error_goto(self.pos))) code.error_goto(self.pos)))
if self.is_temp:
self.py_index.generate_disposal_code(code)
def generate_assignment_code(self, rhs, code): def generate_assignment_code(self, rhs, code):
self.generate_subexpr_evaluation_code(code) self.generate_subexpr_evaluation_code(code)
if self.type.is_pyobject: if self.type.is_pyobject:
code.putln( if self.index.type.is_int:
"if (PyObject_SetItem(%s, %s, %s) < 0) %s" % ( code.putln("if (PyList_CheckExact(%s) && 0 <= %s && %s < PyList_GET_SIZE(%s)) {" % (
self.base.py_result(), self.base.py_result(),
self.index.py_result(), self.index.result_code,
rhs.py_result(), self.index.result_code,
code.error_goto(self.pos))) self.base.py_result()))
code.putln("Py_DECREF(PyList_GET_ITEM(%s, %s)); Py_INCREF(%s);" % (
self.base.py_result(),
self.index.result_code,
rhs.py_result()))
code.putln("PyList_SET_ITEM(%s, %s, %s);" % (
self.base.py_result(),
self.index.result_code,
rhs.py_result()))
code.putln("} else {")
self.generate_generic_assignment_code(rhs, code)
code.putln("}")
else:
self.generate_generic_assignment_code(rhs, code)
self.generate_subexpr_disposal_code(code) self.generate_subexpr_disposal_code(code)
else: else:
code.putln( code.putln(
...@@ -1103,14 +1162,27 @@ class IndexNode(ExprNode): ...@@ -1103,14 +1162,27 @@ class IndexNode(ExprNode):
self.result_code, rhs.result_code)) self.result_code, rhs.result_code))
rhs.generate_disposal_code(code) rhs.generate_disposal_code(code)
def generate_generic_assignment_code(self, rhs, code):
self.py_index.generate_result_code(code)
code.putln(
"if (PyObject_SetItem(%s, %s, %s) < 0) %s" % (
self.base.py_result(),
self.py_index.py_result(),
rhs.py_result(),
code.error_goto(self.pos)))
if self.is_temp:
self.py_index.generate_disposal_code(code)
def generate_deletion_code(self, code): def generate_deletion_code(self, code):
self.generate_subexpr_evaluation_code(code) self.generate_subexpr_evaluation_code(code)
self.py_index.generate_evaluation_code(code)
code.putln( code.putln(
"if (PyObject_DelItem(%s, %s) < 0) %s" % ( "if (PyObject_DelItem(%s, %s) < 0) %s" % (
self.base.py_result(), self.base.py_result(),
self.index.py_result(), self.py_index.py_result(),
code.error_goto(self.pos))) code.error_goto(self.pos)))
self.generate_subexpr_disposal_code(code) self.generate_subexpr_disposal_code(code)
self.py_index.generate_disposal_code(code)
class SliceIndexNode(ExprNode): class SliceIndexNode(ExprNode):
......
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