Commit f37f7bd3 authored by Mark Florisson's avatar Mark Florisson

Optimize indexing when slicing memviews & move to utility

parent a54b1631
...@@ -1811,7 +1811,6 @@ class NameNode(AtomicExprNode): ...@@ -1811,7 +1811,6 @@ class NameNode(AtomicExprNode):
lhs_pos=self.pos, lhs_pos=self.pos,
rhs=rhs, rhs=rhs,
code=code, code=code,
incref_rhs=rhs.is_name,
have_gil=not self.in_nogil_context) have_gil=not self.in_nogil_context)
def generate_acquire_buffer(self, rhs, code): def generate_acquire_buffer(self, rhs, code):
...@@ -2571,7 +2570,6 @@ class IndexNode(ExprNode): ...@@ -2571,7 +2570,6 @@ class IndexNode(ExprNode):
# All indices with all start/stop/step for slices. # All indices with all start/stop/step for slices.
# We need to keep this around # We need to keep this around
self.indices = new_indices self.indices = new_indices
self.env = env self.env = env
elif self.base.type.is_buffer: elif self.base.type.is_buffer:
...@@ -4525,8 +4523,7 @@ class AttributeNode(ExprNode): ...@@ -4525,8 +4523,7 @@ class AttributeNode(ExprNode):
elif self.type.is_memoryviewslice: elif self.type.is_memoryviewslice:
import MemoryView import MemoryView
MemoryView.put_assign_to_memviewslice( MemoryView.put_assign_to_memviewslice(
select_code, rhs.result(), self.type, code, select_code, rhs, rhs.result(), self.type, code)
incref_rhs=rhs.is_name)
if not self.type.is_memoryviewslice: if not self.type.is_memoryviewslice:
code.putln( code.putln(
......
...@@ -79,7 +79,7 @@ def mangle_dtype_name(dtype): ...@@ -79,7 +79,7 @@ def mangle_dtype_name(dtype):
# return "".join([access[0].upper()+packing[0] for (access, packing) in axes]) # return "".join([access[0].upper()+packing[0] for (access, packing) in axes])
def put_acquire_memoryviewslice(lhs_cname, lhs_type, lhs_pos, rhs, code, def put_acquire_memoryviewslice(lhs_cname, lhs_type, lhs_pos, rhs, code,
incref_rhs=False, have_gil=False): have_gil=False):
assert rhs.type.is_memoryviewslice assert rhs.type.is_memoryviewslice
pretty_rhs = isinstance(rhs, NameNode) or rhs.result_in_temp() pretty_rhs = isinstance(rhs, NameNode) or rhs.result_in_temp()
...@@ -91,16 +91,16 @@ def put_acquire_memoryviewslice(lhs_cname, lhs_type, lhs_pos, rhs, code, ...@@ -91,16 +91,16 @@ def put_acquire_memoryviewslice(lhs_cname, lhs_type, lhs_pos, rhs, code,
# Allow uninitialized assignment # Allow uninitialized assignment
#code.putln(code.put_error_if_unbound(lhs_pos, rhs.entry)) #code.putln(code.put_error_if_unbound(lhs_pos, rhs.entry))
put_assign_to_memviewslice(lhs_cname, rhstmp, lhs_type, code, incref_rhs, put_assign_to_memviewslice(lhs_cname, rhs, rhstmp, lhs_type, code,
have_gil=have_gil) have_gil=have_gil)
if not pretty_rhs: if not pretty_rhs:
code.funcstate.release_temp(rhstmp) code.funcstate.release_temp(rhstmp)
def put_assign_to_memviewslice(lhs_cname, rhs_cname, memviewslicetype, code, def put_assign_to_memviewslice(lhs_cname, rhs, rhs_cname, memviewslicetype, code,
incref_rhs=False, have_gil=False): have_gil=False):
code.put_xdecref_memoryviewslice(lhs_cname, have_gil=have_gil) code.put_xdecref_memoryviewslice(lhs_cname, have_gil=have_gil)
if incref_rhs: if rhs.is_name:
code.put_incref_memoryviewslice(rhs_cname, have_gil=have_gil) code.put_incref_memoryviewslice(rhs_cname, have_gil=have_gil)
code.putln("%s = %s;" % (lhs_cname, rhs_cname)) code.putln("%s = %s;" % (lhs_cname, rhs_cname))
...@@ -269,59 +269,52 @@ class MemoryViewSliceBufferEntry(Buffer.BufferEntry): ...@@ -269,59 +269,52 @@ class MemoryViewSliceBufferEntry(Buffer.BufferEntry):
Simply call __pyx_memoryview_slice_memviewslice with the right Simply call __pyx_memoryview_slice_memviewslice with the right
arguments. arguments.
""" """
slicefunc = "__pyx_memoryview_slice_memviewslice"
new_ndim = 0 new_ndim = 0
cname = self.cname src = self.cname
suboffset_dim = code.funcstate.allocate_temp(PyrexTypes.c_int_type, def load_slice_util(name, dict):
False) proto, impl = TempitaUtilityCode.load_as_string(
name, "MemoryView_C.c", context=dict)
index_code = ("%(slicefunc)s(&%(cname)s, &%(dst)s, %(have_gil)d, " return impl
"%(dim)d, %(new_ndim)d, &%(suboffset_dim)s, "
"%(idx)s, 0, 0, 0, 0, 0, 0)") all_dimensions_direct = True
for access, packing in self.type.axes:
slice_code = ("%(slicefunc)s(&%(cname)s, &%(dst)s, %(have_gil)d, " if access != 'direct':
"/* dim */ %(dim)d, " all_dimensions_direct = False
"/* new_ndim */ %(new_ndim)d, " break
"/* suboffset_dim */ &%(suboffset_dim)s, "
"/* start */ %(start)s, " have_slices = False
"/* stop */ %(stop)s, " for index in indices:
"/* step */ %(step)s, " if isinstance(index, ExprNodes.SliceNode):
"/* have_start */ %(have_start)d, " have_slices = True
"/* have_stop */ %(have_stop)d, " break
"/* have_step */ %(have_step)d, "
"/* is_slice */ 1)") no_suboffset_dim = all_dimensions_direct and not have_slices
if not no_suboffset_dim:
def generate_slice_call(expr): suboffset_dim = code.funcstate.allocate_temp(
pos = index.pos PyrexTypes.c_int_type, False)
code.putln("%s = -1;" % suboffset_dim)
if have_gil:
code.putln(code.error_goto_if(expr, pos)) code.putln("%(dst)s.data = %(src)s.data;" % locals())
else: code.putln("%(dst)s.memview = %(src)s.memview;" % locals())
code.putln("{")
code.putln( "const char *__pyx_t_result = %s;" % expr)
code.putln( "if (unlikely(__pyx_t_result)) {")
code.put_ensure_gil()
code.putln( "PyErr_Format(PyExc_IndexError, "
"__pyx_t_result, %d);" % dim)
code.put_release_ensured_gil()
code.putln(code.error_goto(pos))
code.putln( "}")
code.putln("}")
code.putln("%s = -1;" % suboffset_dim)
code.putln("%(dst)s.data = %(cname)s.data;" % locals())
code.putln("%(dst)s.memview = %(cname)s.memview;" % locals())
code.put_incref_memoryviewslice(dst) code.put_incref_memoryviewslice(dst)
for dim, index in enumerate(indices): for dim, index in enumerate(indices):
error_goto = code.error_goto(index.pos)
if not isinstance(index, ExprNodes.SliceNode): if not isinstance(index, ExprNodes.SliceNode):
# normal index
idx = index.result() idx = index.result()
generate_slice_call(index_code % locals()) d = locals()
access, packing = self.type.axes[dim]
if access != 'direct':
return error(index.pos,
"Dimension cannot be indexed away, "
"must be direct")
code.put(load_slice_util("SliceIndex", d))
else: else:
d = {} # slice, unspecified dimension, or part of ellipsis
d = locals()
for s in "start stop step".split(): for s in "start stop step".split():
idx = getattr(index, s) idx = getattr(index, s)
have_idx = d['have_' + s] = not idx.is_none have_idx = d['have_' + s] = not idx.is_none
...@@ -330,11 +323,21 @@ class MemoryViewSliceBufferEntry(Buffer.BufferEntry): ...@@ -330,11 +323,21 @@ class MemoryViewSliceBufferEntry(Buffer.BufferEntry):
else: else:
d[s] = "0" d[s] = "0"
d.update(locals()) if (not d['have_start'] and
generate_slice_call(slice_code % d) not d['have_stop'] and
not d['have_step']):
# full slice (:), simply copy over the extent, stride
# and suboffset. Also update suboffset_dim if needed
access, packing = self.type.axes[dim]
d['access'] = access
code.put(load_slice_util("SimpleSlice", d))
else:
code.put(load_slice_util("ToughSlice", d))
new_ndim += 1 new_ndim += 1
code.funcstate.release_temp(suboffset_dim) if not no_suboffset_dim:
code.funcstate.release_temp(suboffset_dim)
def empty_slice(pos): def empty_slice(pos):
......
...@@ -5168,7 +5168,6 @@ class ReturnStatNode(StatNode): ...@@ -5168,7 +5168,6 @@ class ReturnStatNode(StatNode):
lhs_pos=self.value.pos, lhs_pos=self.value.pos,
rhs=self.value, rhs=self.value,
code=code, code=code,
incref_rhs=self.value.is_name,
have_gil=self.in_nogil_context) have_gil=self.in_nogil_context)
else: else:
self.value.make_owned_reference(code) self.value.make_owned_reference(code)
......
...@@ -583,7 +583,7 @@ cdef memoryview memview_slice(memoryview memview, object indices): ...@@ -583,7 +583,7 @@ cdef memoryview memview_slice(memoryview memview, object indices):
for dim, index in enumerate(indices): for dim, index in enumerate(indices):
if PyIndex_Check(index): if PyIndex_Check(index):
slice_memviewslice(p_src, p_dst, True, dim, new_ndim, p_suboffset_dim, slice_memviewslice(p_src, p_dst, dim, new_ndim, p_suboffset_dim,
index, 0, 0, 0, 0, 0, False) index, 0, 0, 0, 0, 0, False)
else: else:
start = index.start or 0 start = index.start or 0
...@@ -594,7 +594,7 @@ cdef memoryview memview_slice(memoryview memview, object indices): ...@@ -594,7 +594,7 @@ cdef memoryview memview_slice(memoryview memview, object indices):
have_stop = index.stop is not None have_stop = index.stop is not None
have_step = index.step is not None have_step = index.step is not None
slice_memviewslice(p_src, p_dst, True, dim, new_ndim, p_suboffset_dim, slice_memviewslice(p_src, p_dst, dim, new_ndim, p_suboffset_dim,
start, stop, step, have_start, have_stop, have_step, start, stop, step, have_start, have_stop, have_step,
True) True)
new_ndim += 1 new_ndim += 1
...@@ -628,9 +628,8 @@ cdef extern from "pystate.h": ...@@ -628,9 +628,8 @@ cdef extern from "pystate.h":
PyObject *PyErr_Format(PyObject *exc, char *msg, ...) nogil PyObject *PyErr_Format(PyObject *exc, char *msg, ...) nogil
@cname('__pyx_memoryview_slice_memviewslice') @cname('__pyx_memoryview_slice_memviewslice')
cdef char *slice_memviewslice({{memviewslice_name}} *src, cdef int slice_memviewslice({{memviewslice_name}} *src,
{{memviewslice_name}} *dst, {{memviewslice_name}} *dst,
bint have_gil,
int dim, int dim,
int new_ndim, int new_ndim,
int *suboffset_dim, int *suboffset_dim,
...@@ -640,11 +639,10 @@ cdef char *slice_memviewslice({{memviewslice_name}} *src, ...@@ -640,11 +639,10 @@ cdef char *slice_memviewslice({{memviewslice_name}} *src,
int have_start, int have_start,
int have_stop, int have_stop,
int have_step, int have_step,
bint is_slice) nogil except *: bint is_slice) nogil except -1:
""" """
Create a new slice dst given slice src. Create a new slice dst given slice src.
have_gil - if true, the GIL must be held and exceptions may be raised
dim - the current src dimension (indexing will make dimensions dim - the current src dimension (indexing will make dimensions
disappear) disappear)
new_dim - the new dst dimension new_dim - the new dst dimension
...@@ -657,19 +655,6 @@ cdef char *slice_memviewslice({{memviewslice_name}} *src, ...@@ -657,19 +655,6 @@ cdef char *slice_memviewslice({{memviewslice_name}} *src,
Py_ssize_t new_shape Py_ssize_t new_shape
bint negative_step bint negative_step
# Somehow these pointers are NULL when set as globals... this needs investigation
char *ERR_OOB = "Index out of bounds (axis %d)"
char *ERR_STEP = "Step must not be zero (axis %d)"
char *ERR_INDIRECT_GIL = ("Cannot make indirect dimension %d disappear "
"through indexing, consider slicing with %d:%d")
char *ERR_INDIRECT_NOGIL = ("Cannot make indirect dimension %d disappear "
"through indexing")
PyObject *exc = <PyObject *> IndexError
if have_gil:
# Assert the GIL
PyThreadState_Get()
shape = src.shape[dim] shape = src.shape[dim]
stride = src.strides[dim] stride = src.strides[dim]
suboffset = src.suboffsets[dim] suboffset = src.suboffsets[dim]
...@@ -679,19 +664,15 @@ cdef char *slice_memviewslice({{memviewslice_name}} *src, ...@@ -679,19 +664,15 @@ cdef char *slice_memviewslice({{memviewslice_name}} *src,
if start < 0: if start < 0:
start += shape start += shape
if not 0 <= start < shape: if not 0 <= start < shape:
if have_gil: with gil:
PyErr_Format(exc, ERR_OOB, dim) raise IndexError("Index out of bounds (axis %d)")
return ERR_OOB
else: else:
# index is a slice # index is a slice
negative_step = have_step != 0 and step < 0 negative_step = have_step != 0 and step < 0
if have_step and step == 0: if have_step and step == 0:
if have_gil: with gil:
# ValueError might be more appropriate, but this will make it consistent raise ValueError("Step may not be zero (axis %d)" % dim)
# with nogil slicing
PyErr_SetString(exc, ERR_STEP)
return ERR_STEP
# check our bounds and set defaults # check our bounds and set defaults
if have_start: if have_start:
...@@ -749,13 +730,16 @@ cdef char *slice_memviewslice({{memviewslice_name}} *src, ...@@ -749,13 +730,16 @@ cdef char *slice_memviewslice({{memviewslice_name}} *src,
if suboffset >= 0: if suboffset >= 0:
if not is_slice: if not is_slice:
if have_gil: with gil:
PyErr_Format(exc, ERR_INDIRECT_GIL, dim, start, start + 1) raise IndexError(
return ERR_INDIRECT_NOGIL "Cannot make indirect dimension %d disappear "
"through indexing, consider slicing with %d:%d" %
(dim, start, start + 1))
else: else:
suboffset_dim[0] = new_ndim suboffset_dim[0] = new_ndim
return NULL return 0
# #
### Index a memoryview ### Index a memoryview
......
...@@ -688,3 +688,70 @@ int {{set_function}}(const char *itemp, PyObject *obj) { ...@@ -688,3 +688,70 @@ int {{set_function}}(const char *itemp, PyObject *obj) {
*(PyObject **) itemp = obj; *(PyObject **) itemp = obj;
return 1; return 1;
} }
/////////// ToughSlice //////////
/* Dimension is indexed with 'start:stop:step' */
if (unlikely(__pyx_memoryview_slice_memviewslice(
&{{src}},
&{{dst}},
{{dim}},
{{new_ndim}},
&{{suboffset_dim}},
{{start}},
{{stop}},
{{step}},
{{int(have_start)}},
{{int(have_stop)}},
{{int(have_step)}},
1) < 0))
{
{{error_goto}}
}
////////// SimpleSlice //////////
/* Dimension is indexed with ':' only */
{{dst}}.shape[{{new_ndim}}] = {{src}}.shape[{{dim}}];
{{dst}}.strides[{{new_ndim}}] = {{src}}.strides[{{dim}}];
{{if access == 'direct'}}
{{dst}}.suboffsets[{{new_ndim}}] = -1;
{{else}}
{{dst}}.suboffsets[{{new_ndim}}] = {{src}}.suboffsets[{{dim}}];
if ({{src}}.suboffsets[{{dim}}] >= 0)
{{suboffset_dim}} = {{new_ndim}};
{{endif}}
////////// SliceIndex //////////
/* Dimension is indexed with an integer, we could use the ToughSlice */
/* approach, but this is faster */
{
Py_ssize_t __pyx_tmp_idx = {{idx}};
if (__pyx_tmp_idx < 0)
__pyx_tmp_idx += {{src}}.shape[{{dim}}];
if (__pyx_tmp_idx < 0 || __pyx_tmp_idx >= {{src}}.shape[{{dim}}]) {
{{if not have_gil}}
#ifdef WITH_THREAD
PyGILState_STATE __pyx_gilstate_save = PyGILState_Ensure();
#endif
{{endif}}
PyErr_SetString(PyExc_IndexError, "Index out of bounds (axis {{dim}})");
{{if not have_gil}}
#ifdef WITH_THREAD
PyGILState_Release(__pyx_gilstate_save);
#endif
{{endif}}
{{error_goto}}
}
{{if all_dimensions_direct}}
{{dst}}.data += __pyx_tmp_idx * {{src}}.strides[{{dim}}];
{{else}}
if ({{suboffset_dim}} < 0) {{dst}}.data += __pyx_tmp_idx * {{src}}.strides[{{dim}}];
else {{dst}}.suboffsets[{{suboffset_dim}}] = __pyx_tmp_idx * {{src}}.strides[{{dim}}];
{{endif}}
}
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