From 6e4016fb251ffc20d3f1f57165b20735f368a2e2 Mon Sep 17 00:00:00 2001 From: David Warde-Farley Date: Sun, 18 Sep 2011 03:29:03 -0400 Subject: ENH: release the GIL in some C library functions. Sandwich certain potentially long running for loops that don't touch any Python objects between NPY_BEGIN_ALLOW_THREADS and NPY_END_ALLOW_THREADS so that the interpreter can potentially schedule another Python thread. --- numpy/lib/src/_compiled_base.c | 14 ++++++++++++++ 1 file changed, 14 insertions(+) (limited to 'numpy/lib/src') diff --git a/numpy/lib/src/_compiled_base.c b/numpy/lib/src/_compiled_base.c index dd8cea16b..fcb39784c 100644 --- a/numpy/lib/src/_compiled_base.c +++ b/numpy/lib/src/_compiled_base.c @@ -159,8 +159,10 @@ arr_bincount(PyObject *NPY_UNUSED(self), PyObject *args, PyObject *kwds) goto fail; } ians = (npy_intp *)(PyArray_DATA(ans)); + NPY_BEGIN_ALLOW_THREADS; for (i = 0; i < len; i++) ians [numbers [i]] += 1; + NPY_END_ALLOW_THREADS; Py_DECREF(lst); } else { @@ -181,9 +183,11 @@ arr_bincount(PyObject *NPY_UNUSED(self), PyObject *args, PyObject *kwds) goto fail; } dans = (double *)PyArray_DATA(ans); + NPY_BEGIN_ALLOW_THREADS; for (i = 0; i < len; i++) { dans[numbers[i]] += weights[i]; } + NPY_END_ALLOW_THREADS; Py_DECREF(lst); Py_DECREF(wts); } @@ -250,6 +254,7 @@ arr_digitize(PyObject *NPY_UNUSED(self), PyObject *args, PyObject *kwds) } if (lbins == 1) { + NPY_BEGIN_ALLOW_THREADS; for (i = 0; i < lx; i++) { if (dx [i] >= dbins[0]) { iret[i] = 1; @@ -258,18 +263,25 @@ arr_digitize(PyObject *NPY_UNUSED(self), PyObject *args, PyObject *kwds) iret[i] = 0; } } + NPY_END_ALLOW_THREADS; } else { + NPY_BEGIN_ALLOW_THREADS; m = monotonic_ (dbins, lbins); + NPY_END_ALLOW_THREADS; if ( m == -1 ) { + NPY_BEGIN_ALLOW_THREADS; for ( i = 0; i < lx; i ++ ) { iret [i] = decr_slot_ ((double)dx[i], dbins, lbins); } + NPY_END_ALLOW_THREADS; } else if ( m == 1 ) { + NPY_BEGIN_ALLOW_THREADS; for ( i = 0; i < lx; i ++ ) { iret [i] = incr_slot_ ((double)dx[i], dbins, lbins); } + NPY_END_ALLOW_THREADS; } else { PyErr_SetString(PyExc_ValueError, @@ -548,6 +560,7 @@ arr_interp(PyObject *NPY_UNUSED(self), PyObject *args, PyObject *kwdict) } slopes = (double *) PyDataMem_NEW((lenxp - 1)*sizeof(double)); + NPY_BEGIN_ALLOW_THREADS; for (i = 0; i < lenxp - 1; i++) { slopes[i] = (dy[i + 1] - dy[i])/(dx[i + 1] - dx[i]); } @@ -567,6 +580,7 @@ arr_interp(PyObject *NPY_UNUSED(self), PyObject *args, PyObject *kwdict) } } + NPY_END_ALLOW_THREADS; PyDataMem_FREE(slopes); Py_DECREF(afp); Py_DECREF(axp); -- cgit v1.2.1 From 0d95803ea6c3f305cc2ceb848cee614b78a7902f Mon Sep 17 00:00:00 2001 From: David Warde-Farley Date: Sun, 18 Sep 2011 13:46:04 -0400 Subject: ENH: less fine-grained GIL management in digitize. --- numpy/lib/src/_compiled_base.c | 23 ++++++++++------------- 1 file changed, 10 insertions(+), 13 deletions(-) (limited to 'numpy/lib/src') diff --git a/numpy/lib/src/_compiled_base.c b/numpy/lib/src/_compiled_base.c index fcb39784c..73782ad2b 100644 --- a/numpy/lib/src/_compiled_base.c +++ b/numpy/lib/src/_compiled_base.c @@ -220,6 +220,7 @@ arr_digitize(PyObject *NPY_UNUSED(self), PyObject *args, PyObject *kwds) int m, i; static char *kwlist[] = {"x", "bins", NULL}; PyArray_Descr *type; + int bins_non_monotonic = 0; if (!PyArg_ParseTupleAndKeywords(args, kwds, "OO", kwlist, &ox, &obins)) { goto fail; @@ -252,9 +253,8 @@ arr_digitize(PyObject *NPY_UNUSED(self), PyObject *args, PyObject *kwds) "Both x and bins must have non-zero length"); goto fail; } - + NPY_BEGIN_ALLOW_THREADS; if (lbins == 1) { - NPY_BEGIN_ALLOW_THREADS; for (i = 0; i < lx; i++) { if (dx [i] >= dbins[0]) { iret[i] = 1; @@ -263,33 +263,30 @@ arr_digitize(PyObject *NPY_UNUSED(self), PyObject *args, PyObject *kwds) iret[i] = 0; } } - NPY_END_ALLOW_THREADS; } else { - NPY_BEGIN_ALLOW_THREADS; m = monotonic_ (dbins, lbins); - NPY_END_ALLOW_THREADS; if ( m == -1 ) { - NPY_BEGIN_ALLOW_THREADS; for ( i = 0; i < lx; i ++ ) { iret [i] = decr_slot_ ((double)dx[i], dbins, lbins); } - NPY_END_ALLOW_THREADS; } else if ( m == 1 ) { - NPY_BEGIN_ALLOW_THREADS; for ( i = 0; i < lx; i ++ ) { iret [i] = incr_slot_ ((double)dx[i], dbins, lbins); } - NPY_END_ALLOW_THREADS; } else { - PyErr_SetString(PyExc_ValueError, - "The bins must be montonically increasing or decreasing"); - goto fail; + /* defer PyErr_SetString until after NPY_END_ALLOW_THREADS */ + bins_non_monotonic = 1; } } - + NPY_END_ALLOW_THREADS; + if (bins_non_monotonic) { + PyErr_SetString(PyExc_ValueError, + "The bins must be monotonically increasing or decreasing"); + goto fail; + } Py_DECREF(ax); Py_DECREF(abins); return (PyObject *)aret; -- cgit v1.2.1 From a5268155c0486061c85d581a4ab75e21f8cbb8f1 Mon Sep 17 00:00:00 2001 From: David Warde-Farley Date: Mon, 19 Sep 2011 01:44:12 -0400 Subject: ENH: Use char instead of int for error flag. --- numpy/lib/src/_compiled_base.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'numpy/lib/src') diff --git a/numpy/lib/src/_compiled_base.c b/numpy/lib/src/_compiled_base.c index 73782ad2b..d3bbb2a19 100644 --- a/numpy/lib/src/_compiled_base.c +++ b/numpy/lib/src/_compiled_base.c @@ -220,7 +220,7 @@ arr_digitize(PyObject *NPY_UNUSED(self), PyObject *args, PyObject *kwds) int m, i; static char *kwlist[] = {"x", "bins", NULL}; PyArray_Descr *type; - int bins_non_monotonic = 0; + char bins_non_monotonic = 0; if (!PyArg_ParseTupleAndKeywords(args, kwds, "OO", kwlist, &ox, &obins)) { goto fail; -- cgit v1.2.1 From 623195534e98b12c5c109d0963b26d57617da99b Mon Sep 17 00:00:00 2001 From: David Warde-Farley Date: Mon, 19 Sep 2011 03:29:23 -0400 Subject: ENH: release GIL for C loops in ravel/unravel. --- numpy/lib/src/_compiled_base.c | 50 ++++++++++++++++++++++++++++++------------ 1 file changed, 36 insertions(+), 14 deletions(-) (limited to 'numpy/lib/src') diff --git a/numpy/lib/src/_compiled_base.c b/numpy/lib/src/_compiled_base.c index d3bbb2a19..f7c371ad0 100644 --- a/numpy/lib/src/_compiled_base.c +++ b/numpy/lib/src/_compiled_base.c @@ -648,8 +648,11 @@ ravel_multi_index_loop(int ravel_ndim, npy_intp *ravel_dims, char **coords, npy_intp *coords_strides) { int i; + char invalid; npy_intp j, m; + NPY_BEGIN_ALLOW_THREADS; + invalid = 0; while (count--) { npy_intp raveled = 0; for (i = 0; i < ravel_ndim; ++i) { @@ -657,11 +660,8 @@ ravel_multi_index_loop(int ravel_ndim, npy_intp *ravel_dims, j = *(npy_intp *)coords[i]; switch (modes[i]) { case NPY_RAISE: - if (j < 0 || j >= m) { - PyErr_SetString(PyExc_ValueError, - "invalid entry in coordinates array"); - return NPY_FAIL; - } + if (j < 0 || j >= m) + invalid = 1; break; case NPY_WRAP: if (j < 0) { @@ -694,10 +694,18 @@ ravel_multi_index_loop(int ravel_ndim, npy_intp *ravel_dims, coords[i] += coords_strides[i]; } + if (invalid) { + break; + } *(npy_intp *)coords[ravel_ndim] = raveled; coords[ravel_ndim] += coords_strides[ravel_ndim]; } - + NPY_END_ALLOW_THREADS; + if (invalid) { + PyErr_SetString(PyExc_ValueError, + "invalid entry in coordinates array"); + return NPY_FAIL; + } return NPY_SUCCEED; } @@ -848,14 +856,16 @@ unravel_index_loop_corder(int unravel_ndim, npy_intp *unravel_dims, npy_intp *coords) { int i; + char invalid; npy_intp val; + NPY_BEGIN_ALLOW_THREADS; + invalid = 0; while (count--) { val = *(npy_intp *)indices; if (val < 0 || val >= unravel_size) { - PyErr_SetString(PyExc_ValueError, - "invalid entry in index array"); - return NPY_FAIL; + invalid = 1; + break; } for (i = unravel_ndim-1; i >= 0; --i) { coords[i] = val % unravel_dims[i]; @@ -864,7 +874,12 @@ unravel_index_loop_corder(int unravel_ndim, npy_intp *unravel_dims, coords += unravel_ndim; indices += indices_stride; } - + NPY_END_ALLOW_THREADS; + if (invalid) { + PyErr_SetString(PyExc_ValueError, + "invalid entry in index array"); + return NPY_FAIL; + } return NPY_SUCCEED; } @@ -876,14 +891,16 @@ unravel_index_loop_forder(int unravel_ndim, npy_intp *unravel_dims, npy_intp *coords) { int i; + char invalid; npy_intp val; + NPY_BEGIN_ALLOW_THREADS; + invalid = 0; while (count--) { val = *(npy_intp *)indices; if (val < 0 || val >= unravel_size) { - PyErr_SetString(PyExc_ValueError, - "invalid entry in index array"); - return NPY_FAIL; + invalid = 1; + break; } for (i = 0; i < unravel_ndim; ++i) { *coords++ = val % unravel_dims[i]; @@ -891,7 +908,12 @@ unravel_index_loop_forder(int unravel_ndim, npy_intp *unravel_dims, } indices += indices_stride; } - + NPY_END_ALLOW_THREADS; + if (invalid) { + PyErr_SetString(PyExc_ValueError, + "invalid entry in index array"); + return NPY_FAIL; + } return NPY_SUCCEED; } -- cgit v1.2.1 From e6696cb736b05f0bb01fe4baac683746ace63a24 Mon Sep 17 00:00:00 2001 From: David Warde-Farley Date: Tue, 20 Sep 2011 16:55:53 -0400 Subject: REF: factor out inner loop of arr_insert. This makes subsequent thread-friendly modification easier. --- numpy/lib/src/_compiled_base.c | 76 ++++++++++++++++++++++++------------------ 1 file changed, 44 insertions(+), 32 deletions(-) (limited to 'numpy/lib/src') diff --git a/numpy/lib/src/_compiled_base.c b/numpy/lib/src/_compiled_base.c index f7c371ad0..715656aff 100644 --- a/numpy/lib/src/_compiled_base.c +++ b/numpy/lib/src/_compiled_base.c @@ -302,6 +302,46 @@ fail: static char arr_insert__doc__[] = "Insert vals sequentially into equivalent 1-d positions indicated by mask."; +static void +arr_insert_loop(char *mptr, char *vptr, char *input_data, char *zero, + char *avals_data, int melsize, int delsize, int objarray, + int totmask, int numvals, int nd, npy_intp *instrides, + npy_intp *inshape) +{ + /* + * Walk through mask array, when non-zero is encountered + * copy next value in the vals array to the input array. + * If we get through the value array, repeat it as necessary. + */ + int mindx, rem_indx, indx, i, copied; + copied = 0; + for (mindx = 0; mindx < totmask; mindx++) { + if (memcmp(mptr,zero,melsize) != 0) { + /* compute indx into input array */ + rem_indx = mindx; + indx = 0; + for (i = nd - 1; i > 0; --i) { + indx += (rem_indx % inshape[i]) * instrides[i]; + rem_indx /= inshape[i]; + } + indx += rem_indx * instrides[0]; + /* fprintf(stderr, "mindx = %d, indx=%d\n", mindx, indx); */ + /* Copy value element over to input array */ + memcpy(input_data+indx,vptr,delsize); + if (objarray) { + Py_INCREF(*((PyObject **)vptr)); + } + vptr += delsize; + copied += 1; + /* If we move past value data. Reset */ + if (copied >= numvals) { + vptr = avals_data; + } + } + mptr += melsize; + } +} + /* * Returns input array with values inserted sequentially into places * indicated by the mask @@ -313,9 +353,8 @@ arr_insert(PyObject *NPY_UNUSED(self), PyObject *args, PyObject *kwdict) PyArrayObject *ainput = NULL, *amask = NULL, *avals = NULL, *tmp = NULL; int numvals, totmask, sameshape; char *input_data, *mptr, *vptr, *zero = NULL; - int melsize, delsize, copied, nd; + int melsize, delsize, copied, nd, objarray, k; npy_intp *instrides, *inshape; - int mindx, rem_indx, indx, i, k, objarray; static char *kwlist[] = {"input", "mask", "vals", NULL}; @@ -397,40 +436,13 @@ arr_insert(PyObject *NPY_UNUSED(self), PyObject *args, PyObject *kwdict) return Py_None; } - /* - * Walk through mask array, when non-zero is encountered - * copy next value in the vals array to the input array. - * If we get through the value array, repeat it as necessary. - */ totmask = (int) PyArray_SIZE(amask); copied = 0; instrides = PyArray_STRIDES(ainput); inshape = PyArray_DIMS(ainput); - for (mindx = 0; mindx < totmask; mindx++) { - if (memcmp(mptr,zero,melsize) != 0) { - /* compute indx into input array */ - rem_indx = mindx; - indx = 0; - for (i = nd - 1; i > 0; --i) { - indx += (rem_indx % inshape[i]) * instrides[i]; - rem_indx /= inshape[i]; - } - indx += rem_indx * instrides[0]; - /* fprintf(stderr, "mindx = %d, indx=%d\n", mindx, indx); */ - /* Copy value element over to input array */ - memcpy(input_data+indx,vptr,delsize); - if (objarray) { - Py_INCREF(*((PyObject **)vptr)); - } - vptr += delsize; - copied += 1; - /* If we move past value data. Reset */ - if (copied >= numvals) { - vptr = PyArray_DATA(avals); - } - } - mptr += melsize; - } + arr_insert_loop(mptr, vptr, input_data, zero, PyArray_DATA(avals), + melsize, delsize, objarray, totmask, numvals, nd, + instrides, inshape); Py_DECREF(amask); Py_DECREF(avals); -- cgit v1.2.1 From 7d6ceb52c040df3f66e49b0ab11ec7b9108266fe Mon Sep 17 00:00:00 2001 From: David Warde-Farley Date: Tue, 20 Sep 2011 16:59:59 -0400 Subject: ENH: release the GIL for arr_insert inner loop. Releases it only conditionally, as object arrays require refcounting to be performed within the inner loop, making GIL release impractical. --- numpy/lib/src/_compiled_base.c | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) (limited to 'numpy/lib/src') diff --git a/numpy/lib/src/_compiled_base.c b/numpy/lib/src/_compiled_base.c index 715656aff..57a641b8c 100644 --- a/numpy/lib/src/_compiled_base.c +++ b/numpy/lib/src/_compiled_base.c @@ -440,9 +440,20 @@ arr_insert(PyObject *NPY_UNUSED(self), PyObject *args, PyObject *kwdict) copied = 0; instrides = PyArray_STRIDES(ainput); inshape = PyArray_DIMS(ainput); - arr_insert_loop(mptr, vptr, input_data, zero, PyArray_DATA(avals), - melsize, delsize, objarray, totmask, numvals, nd, - instrides, inshape); + if (objarray) { + /* object array, need to refcount, can't release the GIL */ + arr_insert_loop(mptr, vptr, input_data, zero, PyArray_DATA(avals), + melsize, delsize, objarray, totmask, numvals, nd, + instrides, inshape); + } + else { + /* No increfs take place in arr_insert_loop, so release the GIL */ + NPY_BEGIN_ALLOW_THREADS; + arr_insert_loop(mptr, vptr, input_data, zero, PyArray_DATA(avals), + melsize, delsize, objarray, totmask, numvals, nd, + instrides, inshape); + NPY_END_ALLOW_THREADS; + } Py_DECREF(amask); Py_DECREF(avals); -- cgit v1.2.1 From 214134f00dcd895582d80f9f09a961f8e33eb02c Mon Sep 17 00:00:00 2001 From: David Warde-Farley Date: Tue, 20 Sep 2011 17:27:53 -0400 Subject: REF: simplify multi-loop breaking with a goto. --- numpy/lib/src/_compiled_base.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'numpy/lib/src') diff --git a/numpy/lib/src/_compiled_base.c b/numpy/lib/src/_compiled_base.c index 57a641b8c..58b9f5ecd 100644 --- a/numpy/lib/src/_compiled_base.c +++ b/numpy/lib/src/_compiled_base.c @@ -683,8 +683,10 @@ ravel_multi_index_loop(int ravel_ndim, npy_intp *ravel_dims, j = *(npy_intp *)coords[i]; switch (modes[i]) { case NPY_RAISE: - if (j < 0 || j >= m) + if (j < 0 || j >= m) { invalid = 1; + goto end_while; + } break; case NPY_WRAP: if (j < 0) { @@ -717,12 +719,10 @@ ravel_multi_index_loop(int ravel_ndim, npy_intp *ravel_dims, coords[i] += coords_strides[i]; } - if (invalid) { - break; - } *(npy_intp *)coords[ravel_ndim] = raveled; coords[ravel_ndim] += coords_strides[ravel_ndim]; } +end_while: NPY_END_ALLOW_THREADS; if (invalid) { PyErr_SetString(PyExc_ValueError, -- cgit v1.2.1 From 67ba2871d104513e8c25aded9fb47a99108d8688 Mon Sep 17 00:00:00 2001 From: David Warde-Farley Date: Wed, 25 Jan 2012 11:49:43 -0500 Subject: DOC: add a high-level comment for arr_insert_loop --- numpy/lib/src/_compiled_base.c | 8 ++++++++ 1 file changed, 8 insertions(+) (limited to 'numpy/lib/src') diff --git a/numpy/lib/src/_compiled_base.c b/numpy/lib/src/_compiled_base.c index 58b9f5ecd..f35fa3c75 100644 --- a/numpy/lib/src/_compiled_base.c +++ b/numpy/lib/src/_compiled_base.c @@ -302,6 +302,14 @@ fail: static char arr_insert__doc__[] = "Insert vals sequentially into equivalent 1-d positions indicated by mask."; +/* + * Insert values from an input array into an output array, at positions + * indicated by a mask. If the arrays are of dtype object (indicated by + * the objarray flag), take care of reference counting. + * + * This function implements the copying logic of arr_insert() defined + * below. + */ static void arr_insert_loop(char *mptr, char *vptr, char *input_data, char *zero, char *avals_data, int melsize, int delsize, int objarray, -- cgit v1.2.1