diff options
| author | Sebastian Berg <sebastian@sipsolutions.net> | 2022-04-06 13:54:30 -0700 |
|---|---|---|
| committer | Sebastian Berg <sebastian@sipsolutions.net> | 2022-06-13 09:36:57 -0700 |
| commit | d53fe91e551f6318e004665995456b838429e4ab (patch) | |
| tree | 70810f6729ec1b519ef789a1750ccbf3e6a61408 /numpy | |
| parent | 54d6ac5bf42d671ce7aa79e13dbdda1b9b2175a8 (diff) | |
| download | numpy-d53fe91e551f6318e004665995456b838429e4ab.tar.gz | |
WIP: Threads things into nditer for ufuncs and advanced indexing
Diffstat (limited to 'numpy')
| -rw-r--r-- | numpy/core/src/multiarray/lowlevel_strided_loops.c.src | 24 | ||||
| -rw-r--r-- | numpy/core/src/multiarray/mapping.c | 35 | ||||
| -rw-r--r-- | numpy/core/src/multiarray/nditer_api.c | 22 | ||||
| -rw-r--r-- | numpy/core/src/multiarray/nditer_constr.c | 11 | ||||
| -rw-r--r-- | numpy/core/src/multiarray/nditer_impl.h | 41 | ||||
| -rw-r--r-- | numpy/core/src/umath/ufunc_object.c | 23 |
6 files changed, 102 insertions, 54 deletions
diff --git a/numpy/core/src/multiarray/lowlevel_strided_loops.c.src b/numpy/core/src/multiarray/lowlevel_strided_loops.c.src index a9f95886f..c46f733f3 100644 --- a/numpy/core/src/multiarray/lowlevel_strided_loops.c.src +++ b/numpy/core/src/multiarray/lowlevel_strided_loops.c.src @@ -1717,30 +1717,6 @@ mapiter_@name@(PyArrayMapIterObject *mit) int is_subiter_trivial = 0; /* has three states */ npy_intp reset_offsets[2] = {0, 0}; - /* Use strided transfer functions for the inner loop */ - npy_intp fixed_strides[2]; - - /* - * Get a dtype transfer function, since there are no - * buffers, this is safe. - */ - NpyIter_GetInnerFixedStrideArray(mit->subspace_iter, fixed_strides); - - NPY_cast_info cast_info; - NPY_ARRAYMETHOD_FLAGS flags; - if (PyArray_GetDTypeTransferFunction(is_aligned, -#if @isget@ - fixed_strides[0], fixed_strides[1], - PyArray_DESCR(array), PyArray_DESCR(mit->extra_op), -#else - fixed_strides[1], fixed_strides[0], - PyArray_DESCR(mit->extra_op), PyArray_DESCR(array), -#endif - 0, - &cast_info, - &flags) != NPY_SUCCEED) { - return -1; - } /* Note: it may make sense to refactor `needs_api` out in this branch */ if (flags & NPY_METH_REQUIRES_PYAPI) { needs_api = 1; diff --git a/numpy/core/src/multiarray/mapping.c b/numpy/core/src/multiarray/mapping.c index 6cf5eab96..558844227 100644 --- a/numpy/core/src/multiarray/mapping.c +++ b/numpy/core/src/multiarray/mapping.c @@ -2610,13 +2610,14 @@ PyArray_MapIterNew(npy_index_info *indices , int index_num, int index_type, } /* create new MapIter object */ - mit = (PyArrayMapIterObject *)PyArray_malloc(sizeof(PyArrayMapIterObject)); + mit = (PyArrayMapIterObject *)PyArray_malloc( + sizeof(PyArrayMapIterObject) + sizeof(NPY_cast_info)); if (mit == NULL) { Py_DECREF(intp_descr); return NULL; } /* set all attributes of mapiter to zero */ - memset(mit, 0, sizeof(PyArrayMapIterObject)); + memset(mit, 0, sizeof(PyArrayMapIterObject) + sizeof(NPY_cast_info)); PyObject_Init((PyObject *)mit, &PyArrayMapIter_Type); Py_INCREF(arr); @@ -3079,7 +3080,34 @@ PyArray_MapIterNew(npy_index_info *indices , int index_num, int index_type, mit->subspace_ptrs = NpyIter_GetDataPtrArray(mit->subspace_iter); mit->subspace_strides = NpyIter_GetInnerStrideArray(mit->subspace_iter); - if (NpyIter_IterationNeedsAPI(mit->outer)) { + NPY_cast_info *cast_info = (NPY_cast_info *)&mit->subspace_castinfo; + NPY_ARRAYMETHOD_FLAGS flags; + npy_intp fixed_strides[2]; + + /* + * Get a dtype transfer function, since there are no + * buffers, this is safe. + */ + NpyIter_GetInnerFixedStrideArray(mit->subspace_iter, fixed_strides); + + if (PyArray_GetDTypeTransferFunction(is_aligned, +#if @isget@ + fixed_strides[0], fixed_strides[1], + PyArray_DESCR(array), PyArray_DESCR(mit->extra_op), +#else + fixed_strides[1], fixed_strides[0], + PyArray_DESCR(mit->extra_op), PyArray_DESCR(array), +#endif + 0, + &cast_info, + &flags) != NPY_SUCCEED) { + return -1; + } + + + + + if (NpyIter_IterationNeedsAPI(mit->subspace_iter)) { mit->needs_api = 1; /* * NOTE: In this case, need to call PyErr_Occurred() after @@ -3286,6 +3314,7 @@ arraymapiter_dealloc(PyArrayMapIterObject *mit) } if (mit->subspace_iter != NULL) { NpyIter_Deallocate(mit->subspace_iter); + NPY_cast_info_xfree((NPY_cast_info *)(&(mit->subspace_castinfo))); } if (mit->extra_op_iter != NULL) { NpyIter_Deallocate(mit->extra_op_iter); diff --git a/numpy/core/src/multiarray/nditer_api.c b/numpy/core/src/multiarray/nditer_api.c index 860c8c1f6..b80312e06 100644 --- a/numpy/core/src/multiarray/nditer_api.c +++ b/numpy/core/src/multiarray/nditer_api.c @@ -857,6 +857,13 @@ NpyIter_RequiresBuffering(NpyIter *iter) * Whether the iteration loop, and in particular the iternext() * function, needs API access. If this is true, the GIL must * be retained while iterating. + * + * NOTE: Internally (currently), `NpyIter_GetTransferFlags` will + * additionally provide information on whether floating point errors + * may be given during casts. The flags only require the API use + * necessary for buffering though. So an iterate which does not require + * buffering may indicate `NpyIter_IterationNeedsAPI`, but not include + * the flag in `NpyIter_GetTransferFlags`. */ NPY_NO_EXPORT npy_bool NpyIter_IterationNeedsAPI(NpyIter *iter) @@ -864,6 +871,21 @@ NpyIter_IterationNeedsAPI(NpyIter *iter) return (NIT_ITFLAGS(iter)&NPY_ITFLAG_NEEDSAPI) != 0; } + +/* + * Fetch the ArrayMethod (runtime) flags for all "transfer functions' (i.e. + * copy to buffer/casts). + * + * TODO: This should be public API, but that only makes sense when the + * ArrayMethod API is made public. + */ +NPY_NO_EXPORT int +NpyIter_GetTransferFlags(NpyIter *iter) +{ + return NIT_ITFLAGS(iter) >> NPY_ITFLAG_TRANSFERFLAGS_SHIFT; +} + + /*NUMPY_API * Gets the number of dimensions being iterated */ diff --git a/numpy/core/src/multiarray/nditer_constr.c b/numpy/core/src/multiarray/nditer_constr.c index bd0763b19..a383c63e8 100644 --- a/numpy/core/src/multiarray/nditer_constr.c +++ b/numpy/core/src/multiarray/nditer_constr.c @@ -3250,12 +3250,11 @@ npyiter_allocate_transfer_functions(NpyIter *iter) } } - /* - * If any of the dtype transfer functions needed the API, flag it. - * TODO: We ignore other flags (currently only if floating point errors are - * possible). This is incorrect, the combined flags should be - * exposed explicitly. - */ + /* Store the combined transfer flags on the iterator */ + NIT_ITFLAGS(iter) |= cflags << NPY_ITFLAG_TRANSFERFLAGS_SHIFT; + assert(NIT_ITFLAGS(iter) >> NPY_ITFLAG_TRANSFERFLAGS_SHIFT == cflags); + + /* If any of the dtype transfer functions needed the API, flag it. */ if (cflags & NPY_METH_REQUIRES_PYAPI) { NIT_ITFLAGS(iter) |= NPY_ITFLAG_NEEDSAPI; } diff --git a/numpy/core/src/multiarray/nditer_impl.h b/numpy/core/src/multiarray/nditer_impl.h index 2a82b7e54..459675ea8 100644 --- a/numpy/core/src/multiarray/nditer_impl.h +++ b/numpy/core/src/multiarray/nditer_impl.h @@ -76,33 +76,38 @@ /* Internal iterator flags */ /* The perm is the identity */ -#define NPY_ITFLAG_IDENTPERM 0x0001 +#define NPY_ITFLAG_IDENTPERM (1 << 0) /* The perm has negative entries (indicating flipped axes) */ -#define NPY_ITFLAG_NEGPERM 0x0002 +#define NPY_ITFLAG_NEGPERM (1 << 1) /* The iterator is tracking an index */ -#define NPY_ITFLAG_HASINDEX 0x0004 +#define NPY_ITFLAG_HASINDEX (1 << 2) /* The iterator is tracking a multi-index */ -#define NPY_ITFLAG_HASMULTIINDEX 0x0008 +#define NPY_ITFLAG_HASMULTIINDEX (1 << 3) /* The iteration order was forced on construction */ -#define NPY_ITFLAG_FORCEDORDER 0x0010 +#define NPY_ITFLAG_FORCEDORDER (1 << 4) /* The inner loop is handled outside the iterator */ -#define NPY_ITFLAG_EXLOOP 0x0020 +#define NPY_ITFLAG_EXLOOP (1 << 5) /* The iterator is ranged */ -#define NPY_ITFLAG_RANGE 0x0040 +#define NPY_ITFLAG_RANGE (1 << 6) /* The iterator is buffered */ -#define NPY_ITFLAG_BUFFER 0x0080 +#define NPY_ITFLAG_BUFFER (1 << 7) /* The iterator should grow the buffered inner loop when possible */ -#define NPY_ITFLAG_GROWINNER 0x0100 +#define NPY_ITFLAG_GROWINNER (1 << 8) /* There is just one iteration, can specialize iternext for that */ -#define NPY_ITFLAG_ONEITERATION 0x0200 +#define NPY_ITFLAG_ONEITERATION (1 << 9) /* Delay buffer allocation until first Reset* call */ -#define NPY_ITFLAG_DELAYBUF 0x0400 +#define NPY_ITFLAG_DELAYBUF (1 << 10) /* Iteration needs API access during iternext */ -#define NPY_ITFLAG_NEEDSAPI 0x0800 +#define NPY_ITFLAG_NEEDSAPI (1 << 11) /* Iteration includes one or more operands being reduced */ -#define NPY_ITFLAG_REDUCE 0x1000 +#define NPY_ITFLAG_REDUCE (1 << 12) /* Reduce iteration doesn't need to recalculate reduce loops next time */ -#define NPY_ITFLAG_REUSE_REDUCE_LOOPS 0x2000 +#define NPY_ITFLAG_REUSE_REDUCE_LOOPS (1 << 13) +/* + * Offset of (combined) ArrayMethod flags for all transfer functions. + * For now, we use the top 8 bits. + */ +#define NPY_ITFLAG_TRANSFERFLAGS_SHIFT 24 /* Internal iterator per-operand iterator flags */ @@ -356,4 +361,12 @@ npyiter_copy_to_buffers(NpyIter *iter, char **prev_dataptrs); NPY_NO_EXPORT void npyiter_clear_buffers(NpyIter *iter); +/* + * Function to get the ArrayMethod flags of the transfer functions. + * TODO: This function should be public and removed from `nditer_impl.h`, but + * this requires making the ArrayMethod flags public API first. + */ +NPY_NO_EXPORT int +NpyIter_GetTransferFlags(NpyIter *iter); + #endif /* NUMPY_CORE_SRC_MULTIARRAY_NDITER_IMPL_H_ */ diff --git a/numpy/core/src/umath/ufunc_object.c b/numpy/core/src/umath/ufunc_object.c index fce7d61de..2636396d3 100644 --- a/numpy/core/src/umath/ufunc_object.c +++ b/numpy/core/src/umath/ufunc_object.c @@ -57,6 +57,10 @@ #include "legacy_array_method.h" #include "abstractdtypes.h" +/* TODO: Only for `NpyIter_GetTransferFlags` until it is public */ +#define NPY_ITERATOR_IMPLEMENTATION_CODE +#include "nditer_impl.h" + /********** PRINTF DEBUG TRACING **************/ #define NPY_UF_DBG_TRACING 0 @@ -1544,10 +1548,6 @@ execute_ufunc_loop(PyArrayMethod_Context *context, int masked, if (masked) { baseptrs[nop] = PyArray_BYTES(op_it[nop]); } - if (NpyIter_ResetBasePointers(iter, baseptrs, NULL) != NPY_SUCCEED) { - NpyIter_Deallocate(iter); - return -1; - } /* * Get the inner loop, with the possibility of specialization @@ -1584,17 +1584,25 @@ execute_ufunc_loop(PyArrayMethod_Context *context, int masked, char **dataptr = NpyIter_GetDataPtrArray(iter); npy_intp *strides = NpyIter_GetInnerStrideArray(iter); npy_intp *countptr = NpyIter_GetInnerLoopSizePtr(iter); - int needs_api = NpyIter_IterationNeedsAPI(iter); NPY_BEGIN_THREADS_DEF; + flags = PyArrayMethod_COMBINED_FLAGS(flags, NpyIter_GetTransferFlags(iter)); + if (!(flags & NPY_METH_NO_FLOATINGPOINT_ERRORS)) { npy_clear_floatstatus_barrier((char *)context); } - if (!needs_api && !(flags & NPY_METH_REQUIRES_PYAPI)) { + if (!(flags & NPY_METH_REQUIRES_PYAPI)) { NPY_BEGIN_THREADS_THRESHOLDED(full_size); } + /* The reset may copy the first buffer chunk, which could cause FPEs */ + if (NpyIter_ResetBasePointers(iter, baseptrs, NULL) != NPY_SUCCEED) { + NPY_AUXDATA_FREE(auxdata); + NpyIter_Deallocate(iter); + return -1; + } + NPY_UF_DBG_PRINT("Actual inner loop:\n"); /* Execute the loop */ int res; @@ -2388,7 +2396,8 @@ PyUFunc_GeneralizedFunctionInternal(PyUFuncObject *ufunc, NPY_ITER_MULTI_INDEX | NPY_ITER_REFS_OK | NPY_ITER_ZEROSIZE_OK | - NPY_ITER_COPY_IF_OVERLAP; + NPY_ITER_COPY_IF_OVERLAP | + NPY_ITER_DELAY_BUFALLOC; /* Create the iterator */ iter = NpyIter_AdvancedNew(nop, op, iter_flags, |
