diff options
author | da-woods <dw-git@d-woods.co.uk> | 2023-03-20 09:38:44 +0000 |
---|---|---|
committer | GitHub <noreply@github.com> | 2023-03-20 10:38:44 +0100 |
commit | 7bb7ff04cac4047cb425acbf4e53cc94bc396c0a (patch) | |
tree | 8c87d595f1ec8c40114a7c135ae2e5de4661fb4b | |
parent | c8b434c6041de474998fa3a9e779b8340c5b1f71 (diff) | |
download | cython-7bb7ff04cac4047cb425acbf4e53cc94bc396c0a.tar.gz |
Fix exception handling in memoryview utilitycode (GH-5325)
The new exception-handling semantics meant that the utility
code always had to check for exceptions on some void nogil functions
(requiring us to re-acquire the GIL). This made performance
noticeably worse under some circumstances.
Fix this by making the exception specifications noexcept
for quite a few cdef functions in the memoryview utility code.
Fixes https://github.com/cython/cython/issues/5324
-rw-r--r-- | Cython/Utility/MemoryView.pyx | 29 | ||||
-rw-r--r-- | tests/compile/fused_redeclare_T3111.pyx | 12 | ||||
-rw-r--r-- | tests/errors/pure_warnings.py | 6 | ||||
-rw-r--r-- | tests/memoryview/memoryview_no_withgil_check.pyx | 11 |
4 files changed, 35 insertions, 23 deletions
diff --git a/Cython/Utility/MemoryView.pyx b/Cython/Utility/MemoryView.pyx index f9ab51663..7ffcbb58e 100644 --- a/Cython/Utility/MemoryView.pyx +++ b/Cython/Utility/MemoryView.pyx @@ -318,7 +318,7 @@ cdef indirect_contiguous = Enum("<contiguous and indirect>") @cname('__pyx_align_pointer') -cdef void *align_pointer(void *memory, size_t alignment) nogil: +cdef void *align_pointer(void *memory, size_t alignment) noexcept nogil: "Align pointer memory on a given boundary" cdef Py_intptr_t aligned_p = <Py_intptr_t> memory cdef size_t offset @@ -679,7 +679,7 @@ cdef memoryview_cwrapper(object o, int flags, bint dtype_is_object, __Pyx_TypeIn return result @cname('__pyx_memoryview_check') -cdef inline bint memoryview_check(object o): +cdef inline bint memoryview_check(object o) noexcept: return isinstance(o, memoryview) cdef tuple _unellipsify(object index, int ndim): @@ -711,10 +711,11 @@ cdef tuple _unellipsify(object index, int ndim): nslices = ndim - idx return have_slices or nslices, tuple(result) -cdef assert_direct_dimensions(Py_ssize_t *suboffsets, int ndim): +cdef int assert_direct_dimensions(Py_ssize_t *suboffsets, int ndim) except -1: for suboffset in suboffsets[:ndim]: if suboffset >= 0: raise ValueError, "Indirect dimensions not supported" + return 0 # return type just used as an error flag # ### Slicing a memoryview @@ -1073,7 +1074,7 @@ cdef {{memviewslice_name}} *get_slice_from_memview(memoryview memview, return mslice @cname('__pyx_memoryview_slice_copy') -cdef void slice_copy(memoryview memview, {{memviewslice_name}} *dst): +cdef void slice_copy(memoryview memview, {{memviewslice_name}} *dst) noexcept: cdef int dim cdef (Py_ssize_t*) shape, strides, suboffsets @@ -1119,11 +1120,11 @@ cdef memoryview_copy_from_slice(memoryview memview, {{memviewslice_name}} *memvi # ### Copy the contents of a memoryview slices # -cdef Py_ssize_t abs_py_ssize_t(Py_ssize_t arg) nogil: +cdef Py_ssize_t abs_py_ssize_t(Py_ssize_t arg) noexcept nogil: return -arg if arg < 0 else arg @cname('__pyx_get_best_slice_order') -cdef char get_best_order({{memviewslice_name}} *mslice, int ndim) nogil: +cdef char get_best_order({{memviewslice_name}} *mslice, int ndim) noexcept nogil: """ Figure out the best memory access order for a given slice. """ @@ -1150,7 +1151,7 @@ cdef char get_best_order({{memviewslice_name}} *mslice, int ndim) nogil: cdef void _copy_strided_to_strided(char *src_data, Py_ssize_t *src_strides, char *dst_data, Py_ssize_t *dst_strides, Py_ssize_t *src_shape, Py_ssize_t *dst_shape, - int ndim, size_t itemsize) nogil: + int ndim, size_t itemsize) noexcept nogil: # Note: src_extent is 1 if we're broadcasting # dst_extent always >= src_extent as we don't do reductions cdef Py_ssize_t i @@ -1179,7 +1180,7 @@ cdef void _copy_strided_to_strided(char *src_data, Py_ssize_t *src_strides, cdef void copy_strided_to_strided({{memviewslice_name}} *src, {{memviewslice_name}} *dst, - int ndim, size_t itemsize) nogil: + int ndim, size_t itemsize) noexcept nogil: _copy_strided_to_strided(src.data, src.strides, dst.data, dst.strides, src.shape, dst.shape, ndim, itemsize) @@ -1196,7 +1197,7 @@ cdef Py_ssize_t slice_get_size({{memviewslice_name}} *src, int ndim) noexcept no @cname('__pyx_fill_contig_strides_array') cdef Py_ssize_t fill_contig_strides_array( Py_ssize_t *shape, Py_ssize_t *strides, Py_ssize_t stride, - int ndim, char order) nogil: + int ndim, char order) noexcept nogil: """ Fill the strides array for a slice with C or F contiguous strides. This is like PyBuffer_FillContiguousStrides, but compatible with py < 2.6 @@ -1349,7 +1350,7 @@ cdef int memoryview_copy_contents({{memviewslice_name}} src, @cname('__pyx_memoryview_broadcast_leading') cdef void broadcast_leading({{memviewslice_name}} *mslice, int ndim, - int ndim_other) nogil: + int ndim_other) noexcept nogil: cdef int i cdef int offset = ndim_other - ndim @@ -1369,7 +1370,7 @@ cdef void broadcast_leading({{memviewslice_name}} *mslice, # @cname('__pyx_memoryview_refcount_copying') -cdef void refcount_copying({{memviewslice_name}} *dst, bint dtype_is_object, int ndim, bint inc) nogil: +cdef void refcount_copying({{memviewslice_name}} *dst, bint dtype_is_object, int ndim, bint inc) noexcept nogil: # incref or decref the objects in the destination slice if the dtype is object if dtype_is_object: refcount_objects_in_slice_with_gil(dst.data, dst.shape, dst.strides, ndim, inc) @@ -1377,7 +1378,7 @@ cdef void refcount_copying({{memviewslice_name}} *dst, bint dtype_is_object, int @cname('__pyx_memoryview_refcount_objects_in_slice_with_gil') cdef void refcount_objects_in_slice_with_gil(char *data, Py_ssize_t *shape, Py_ssize_t *strides, int ndim, - bint inc) with gil: + bint inc) noexcept with gil: refcount_objects_in_slice(data, shape, strides, ndim, inc) @cname('__pyx_memoryview_refcount_objects_in_slice') @@ -1403,7 +1404,7 @@ cdef void refcount_objects_in_slice(char *data, Py_ssize_t *shape, @cname('__pyx_memoryview_slice_assign_scalar') cdef void slice_assign_scalar({{memviewslice_name}} *dst, int ndim, size_t itemsize, void *item, - bint dtype_is_object) nogil: + bint dtype_is_object) noexcept nogil: refcount_copying(dst, dtype_is_object, ndim, inc=False) _slice_assign_scalar(dst.data, dst.shape, dst.strides, ndim, itemsize, item) refcount_copying(dst, dtype_is_object, ndim, inc=True) @@ -1412,7 +1413,7 @@ cdef void slice_assign_scalar({{memviewslice_name}} *dst, int ndim, @cname('__pyx_memoryview__slice_assign_scalar') cdef void _slice_assign_scalar(char *data, Py_ssize_t *shape, Py_ssize_t *strides, int ndim, - size_t itemsize, void *item) nogil: + size_t itemsize, void *item) noexcept nogil: cdef Py_ssize_t i cdef Py_ssize_t stride = strides[0] cdef Py_ssize_t extent = shape[0] diff --git a/tests/compile/fused_redeclare_T3111.pyx b/tests/compile/fused_redeclare_T3111.pyx index 5d1672198..53f087717 100644 --- a/tests/compile/fused_redeclare_T3111.pyx +++ b/tests/compile/fused_redeclare_T3111.pyx @@ -27,10 +27,10 @@ _WARNINGS = """ 36:10: 'cpdef_cname_method' redeclared # from MemoryView.pyx -979:29: Ambiguous exception value, same as default return value: 0 -979:29: Ambiguous exception value, same as default return value: 0 -1020:46: Ambiguous exception value, same as default return value: 0 -1020:46: Ambiguous exception value, same as default return value: 0 -1110:29: Ambiguous exception value, same as default return value: 0 -1110:29: Ambiguous exception value, same as default return value: 0 +980:29: Ambiguous exception value, same as default return value: 0 +980:29: Ambiguous exception value, same as default return value: 0 +1021:46: Ambiguous exception value, same as default return value: 0 +1021:46: Ambiguous exception value, same as default return value: 0 +1111:29: Ambiguous exception value, same as default return value: 0 +1111:29: Ambiguous exception value, same as default return value: 0 """ diff --git a/tests/errors/pure_warnings.py b/tests/errors/pure_warnings.py index 40104bc48..3109439cb 100644 --- a/tests/errors/pure_warnings.py +++ b/tests/errors/pure_warnings.py @@ -49,9 +49,9 @@ _WARNINGS = """ # Spurious warnings from utility code - not part of the core test 25:10: 'cpdef_method' redeclared 36:10: 'cpdef_cname_method' redeclared -979:29: Ambiguous exception value, same as default return value: 0 -1020:46: Ambiguous exception value, same as default return value: 0 -1110:29: Ambiguous exception value, same as default return value: 0 +980:29: Ambiguous exception value, same as default return value: 0 +1021:46: Ambiguous exception value, same as default return value: 0 +1111:29: Ambiguous exception value, same as default return value: 0 """ _ERRORS = """ diff --git a/tests/memoryview/memoryview_no_withgil_check.pyx b/tests/memoryview/memoryview_no_withgil_check.pyx new file mode 100644 index 000000000..4753bab12 --- /dev/null +++ b/tests/memoryview/memoryview_no_withgil_check.pyx @@ -0,0 +1,11 @@ +# mode: compile + +# cython: test_fail_if_c_code_has = __Pyx_ErrOccurredWithGIL + +# cython-generated memoryview code shouldn't resort to +# __Pyx_ErrOccurredWithGIL for error checking (because it's inefficient +# inside a nogil block) + +def assign(double[:] a, double[:] b): + with nogil: + a[:] = b[:] |