Open benbovy opened 1 year ago
There are more things to consider here as we are dealing with Numpy object
arrays.
At runtime we need to cast Python object pointers to C++ Geography pointers (and raise an error if the cast is not possible). We have to do that for each array element, we can't just cast the array buffer pointer before calling the vectorized function in a C++ loop. I wonder if it is possible to do that safely while the GIL is released... Maybe using pybind11::handle
instead of pybind11:object
(unlike the latter, the former does not do any reference counting)? After all, we just want a pointer and we don't want to create or destroy any Python object.
What about vectorized functions that create new Python objects wrapping Geography C++ instances? Possible to do it without holding the GIL? That seems weird to me.
@jorisvandenbossche what is the approach used in shapely for releasing the GIL?
Summary of the general approach we use in Shapely:
get_geom
helper (code)), and this is done in the inner loop of the ufunc where we are looping over the object dtype input array (code example for the simple unary predicates). geom_arr_to_npy
helper function (code, see example usage in the unary geom->geom ufuncs: code). Thanks @jorisvandenbossche, that's very helpful.
The last point (vectorized functions that create new geometries as output) may be tricky to implement around pybind11::vectorize
. I'm afraid we would need to add our own version. Or another possibility would be to use xtensor and xtensor-python? xt::vectorize
(xtensor) can operate on any xtensor container (C++ structures) and xt::pyvectorize
(xtensor-python) is just a thin wrapper around it...
Xref https://github.com/pybind/pybind11/issues/1042 in case we would need to wrap a vector of new PyObject geometries into a new object dtype numpy array without copy. Not sure we would really need that, but just in case... (maybe possible to move objects from vector -> numpy array without much overhead?)
pybind11:vectorize()
is very convenient, it allows us to provide Numpy-like universal functions (broadcasting, fast-loop call) with minimal effort. However, it seems to be problematic when releasing the GIL.I've tried releasing the GIL in a simple pybind11 vectorized function like this:
Unfortunately, this compiles but gives a segmentation fault at runtime.
Same behavior when trying something similar on functions accepting numpy arrays as arguments and/or return values, e.g.,
A solution that works for the latter example is releasing the GIL within the function just before the loop:
So as an alternative approach to
py::vectorize()
, we could do something like this:This is more effort, though.
Perhaps one could propose an option for
py::vectorize()
so that it releases the GIL internally when looping over the vectorized arguments?