cython-devel

changeset 2653:2e3dda4a7d23

Optimized list pop.
author Robert Bradshaw <robertwb@math.washington.edu>
date Tue Nov 03 01:01:54 2009 -0800 (2 years ago)
parents 9e89fd2338d8
children 76e7121cbdc7
files Cython/Compiler/Optimize.py tests/run/list_pop.pyx
line diff
1.1 --- a/Cython/Compiler/Optimize.py Tue Nov 03 00:56:50 2009 -0800 1.2 +++ b/Cython/Compiler/Optimize.py Tue Nov 03 01:01:54 2009 -0800 1.3 @@ -1104,6 +1104,40 @@ 1.4 utility_code = append_utility_code 1.5 ) 1.6 1.7 + PyObject_Pop_func_type = PyrexTypes.CFuncType( 1.8 + PyrexTypes.py_object_type, [ 1.9 + PyrexTypes.CFuncTypeArg("list", PyrexTypes.py_object_type, None), 1.10 + ]) 1.11 + 1.12 + PyObject_PopIndex_func_type = PyrexTypes.CFuncType( 1.13 + PyrexTypes.py_object_type, [ 1.14 + PyrexTypes.CFuncTypeArg("list", PyrexTypes.py_object_type, None), 1.15 + PyrexTypes.CFuncTypeArg("index", PyrexTypes.c_long_type, None), 1.16 + ]) 1.17 + 1.18 + def _handle_simple_method_object_pop(self, node, args, is_unbound_method): 1.19 + # X.pop([n]) is almost always referring to a list 1.20 + if len(args) == 1: 1.21 + return ExprNodes.PythonCapiCallNode( 1.22 + node.pos, "__Pyx_PyObject_Pop", self.PyObject_Pop_func_type, 1.23 + args = args, 1.24 + is_temp = node.is_temp, 1.25 + utility_code = pop_utility_code 1.26 + ) 1.27 + elif len(args) == 2: 1.28 + if isinstance(args[1], ExprNodes.CoerceToPyTypeNode) and args[1].arg.type.is_int: 1.29 + original_type = args[1].arg.type 1.30 + if PyrexTypes.widest_numeric_type(original_type, PyrexTypes.c_py_ssize_t_type) == PyrexTypes.c_py_ssize_t_type: 1.31 + args[1] = args[1].arg 1.32 + return ExprNodes.PythonCapiCallNode( 1.33 + node.pos, "__Pyx_PyObject_PopIndex", self.PyObject_PopIndex_func_type, 1.34 + args = args, 1.35 + is_temp = node.is_temp, 1.36 + utility_code = pop_index_utility_code 1.37 + ) 1.38 + 1.39 + return node 1.40 + 1.41 PyList_Append_func_type = PyrexTypes.CFuncType( 1.42 PyrexTypes.c_int_type, [ 1.43 PyrexTypes.CFuncTypeArg("list", PyrexTypes.py_object_type, None), 1.44 @@ -1360,6 +1394,76 @@ 1.45 ) 1.46 1.47 1.48 +pop_utility_code = UtilityCode( 1.49 +proto = """ 1.50 +static INLINE PyObject* __Pyx_PyObject_Pop(PyObject* L) { 1.51 + if (likely(PyList_CheckExact(L)) 1.52 + /* Check that both the size is positive and no reallocation shrinking needs to be done. */ 1.53 + && likely(PyList_GET_SIZE(L) > (((PyListObject*)L)->allocated >> 1))) { 1.54 + Py_SIZE(L) -= 1; 1.55 + return PyList_GET_ITEM(L, PyList_GET_SIZE(L)); 1.56 + } 1.57 + else { 1.58 + PyObject *r, *m; 1.59 + m = __Pyx_GetAttrString(L, "pop"); 1.60 + if (!m) return NULL; 1.61 + r = PyObject_CallObject(m, NULL); 1.62 + Py_DECREF(m); 1.63 + return r; 1.64 + } 1.65 +} 1.66 +""", 1.67 +impl = "" 1.68 +) 1.69 + 1.70 +pop_index_utility_code = UtilityCode( 1.71 +proto = """ 1.72 +static PyObject* __Pyx_PyObject_PopIndex(PyObject* L, Py_ssize_t ix); 1.73 +""", 1.74 +impl = """ 1.75 +static PyObject* __Pyx_PyObject_PopIndex(PyObject* L, Py_ssize_t ix) { 1.76 + PyObject *r, *m, *t, *py_ix; 1.77 + if (likely(PyList_CheckExact(L))) { 1.78 + Py_ssize_t size = PyList_GET_SIZE(L); 1.79 + if (likely(size > (((PyListObject*)L)->allocated >> 1))) { 1.80 + if (ix < 0) { 1.81 + ix += size; 1.82 + } 1.83 + if (likely(0 <= ix && ix < size)) { 1.84 + Py_ssize_t i; 1.85 + PyObject* v = PyList_GET_ITEM(L, ix); 1.86 + Py_SIZE(L) -= 1; 1.87 + size -= 1; 1.88 + for(i=ix; i<size; i++) { 1.89 + PyList_SET_ITEM(L, i, PyList_GET_ITEM(L, i+1)); 1.90 + } 1.91 + return v; 1.92 + } 1.93 + } 1.94 + } 1.95 + py_ix = t = NULL; 1.96 + m = __Pyx_GetAttrString(L, "pop"); 1.97 + if (!m) goto bad; 1.98 + py_ix = PyInt_FromSsize_t(ix); 1.99 + if (!py_ix) goto bad; 1.100 + t = PyTuple_New(1); 1.101 + if (!t) goto bad; 1.102 + PyTuple_SET_ITEM(t, 0, py_ix); 1.103 + py_ix = NULL; 1.104 + r = PyObject_CallObject(m, t); 1.105 + Py_DECREF(m); 1.106 + Py_DECREF(t); 1.107 + return r; 1.108 +bad: 1.109 + Py_XDECREF(m); 1.110 + Py_XDECREF(t); 1.111 + Py_XDECREF(py_ix); 1.112 + return NULL; 1.113 +} 1.114 +""" 1.115 +) 1.116 + 1.117 + 1.118 pytype_utility_code = UtilityCode( 1.119 proto = """ 1.120 static INLINE PyObject* __Pyx_Type(PyObject* o) {
2.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 2.2 +++ b/tests/run/list_pop.pyx Tue Nov 03 01:01:54 2009 -0800 2.3 @@ -0,0 +1,81 @@ 2.4 +cimport cython 2.5 + 2.6 +class A: 2.7 + def pop(self, *args): 2.8 + print args 2.9 + return None 2.10 + 2.11 + 2.12 +@cython.test_assert_path_exists('//PythonCapiCallNode') 2.13 +@cython.test_fail_if_path_exists('//SimpleCallNode/AttributeNode') 2.14 +def simple_pop(L): 2.15 + """ 2.16 + >>> L = range(10) 2.17 + >>> simple_pop(L) 2.18 + 9 2.19 + >>> simple_pop(L) 2.20 + 8 2.21 + >>> L 2.22 + [0, 1, 2, 3, 4, 5, 6, 7] 2.23 + >>> while L: 2.24 + ... _ = simple_pop(L) 2.25 + 2.26 + >>> L 2.27 + [] 2.28 + >>> simple_pop(L) 2.29 + Traceback (most recent call last): 2.30 + ... 2.31 + IndexError: pop from empty list 2.32 + 2.33 + >>> simple_pop(A()) 2.34 + () 2.35 + """ 2.36 + return L.pop() 2.37 + 2.38 +@cython.test_assert_path_exists('//PythonCapiCallNode') 2.39 +@cython.test_fail_if_path_exists('//SimpleCallNode/AttributeNode') 2.40 +def index_pop(L, int i): 2.41 + """ 2.42 + >>> L = range(10) 2.43 + >>> index_pop(L, 2) 2.44 + 2 2.45 + >>> index_pop(L, -2) 2.46 + 8 2.47 + >>> L 2.48 + [0, 1, 3, 4, 5, 6, 7, 9] 2.49 + >>> index_pop(L, 100) 2.50 + Traceback (most recent call last): 2.51 + ... 2.52 + IndexError: pop index out of range 2.53 + >>> index_pop(L, -100) 2.54 + Traceback (most recent call last): 2.55 + ... 2.56 + IndexError: pop index out of range 2.57 + 2.58 + >>> while L: 2.59 + ... _ = index_pop(L, 0) 2.60 + 2.61 + >>> L 2.62 + [] 2.63 + 2.64 + >>> index_pop(L, 0) 2.65 + Traceback (most recent call last): 2.66 + ... 2.67 + IndexError: pop from empty list 2.68 + 2.69 + >>> index_pop(A(), 3) 2.70 + (3,) 2.71 + """ 2.72 + return L.pop(i) 2.73 + 2.74 +@cython.test_fail_if_path_exists('//PythonCapiCallNode') 2.75 +def crazy_pop(L): 2.76 + """ 2.77 + >>> crazy_pop(range(10)) 2.78 + Traceback (most recent call last): 2.79 + ... 2.80 + TypeError: pop() takes at most 1 argument (3 given) 2.81 + >>> crazy_pop(A()) 2.82 + (1, 2, 3) 2.83 + """ 2.84 + return L.pop(1, 2, 3)