summaryrefslogtreecommitdiff
path: root/numpy
diff options
context:
space:
mode:
authorSebastian Berg <sebastian@sipsolutions.net>2022-04-06 13:54:30 -0700
committerSebastian Berg <sebastian@sipsolutions.net>2022-06-13 09:36:57 -0700
commitd53fe91e551f6318e004665995456b838429e4ab (patch)
tree70810f6729ec1b519ef789a1750ccbf3e6a61408 /numpy
parent54d6ac5bf42d671ce7aa79e13dbdda1b9b2175a8 (diff)
downloadnumpy-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.src24
-rw-r--r--numpy/core/src/multiarray/mapping.c35
-rw-r--r--numpy/core/src/multiarray/nditer_api.c22
-rw-r--r--numpy/core/src/multiarray/nditer_constr.c11
-rw-r--r--numpy/core/src/multiarray/nditer_impl.h41
-rw-r--r--numpy/core/src/umath/ufunc_object.c23
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,