[Question] Parallelizing over a list of PyArrays with rayon #363
-
I have a simple extension which operates on a single #[pymodule]
fn repro(_py: Python<'_>, m: &PyModule) -> PyResult<()> {
fn cumsum(array: ArrayView1<'_, f64>) -> Array1<f64> {
let mut total = 0.;
Array1::from_iter(
array
.as_slice()
.expect("input not contiguous")
.iter()
.map(|v| {
total += v;
total
})
)
}
#[pyfn(m)]
#[pyo3(name = "cumsum_single")]
fn cumsum_single_py<'py>(
py: Python<'py>,
array: PyReadonlyArray1<'_, f64>,
) -> &'py PyArray1<f64> {
cumsum(array.as_array()).into_pyarray(py)
}
#[pyfn(m)]
#[pyo3(name = "cumsum_many_sequential")]
fn cumsum_many_sequential_py<'py>(
py: Python<'py>,
arrays: Vec<PyReadonlyArray1<'_, f64>>,
) -> Vec<&'py PyArray1<f64>> {
arrays.into_iter().map(|arr| cumsum_single_py(py, arr)).collect()
}
Ok(())
} The problem is when I try to use
The constraints look a bit weird on the rayon side (to my best understanding, #[pyfn(m)]
#[pyo3(name = "cumsum_many_rayon")]
fn cumsum_many_rayon_py<'py>(
py: Python<'py>,
arrays: Vec<PyReadonlyArray1<'_, f64>>,
) -> Vec<&'py PyArray1<f64>> {
let arrays: Vec<_> = arrays
.iter()
.map(|pa| pa.as_array())
.collect();
// first collect: for some reason cannot send PyReadonlyArray<_, _>,
// with ArrayBase<ViewRepr<_>, _> it works. But they hold references
// in a way that forces me to materialize a vector, instead of using
// par_bridge() directly
let results: Vec<_> = arrays
.into_par_iter()
.map(cumsum)
.collect();
// second collect: need to turn the parallel iterator back to sequential
// for into_pyarray
results
.into_iter()
.map(|result| result.into_pyarray(py))
.collect()
// third collect: to create the actual returned Python list
} This solution uses three individual |
Beta Was this translation helpful? Give feedback.
Replies: 2 comments 1 reply
-
You are just copying references, not the underlying data.
The problem when using Rayon here is that At the moment I don't see a better way to achieve what you need. I suspect that the overhead of the three calls to One alternative that would however require a different signature and would preclude the arrays from having different lengths would be to take a |
Beta Was this translation helpful? Give feedback.
-
Thanks a lot for your quick reply, it's good to know I have not strayed too far from what is reasonable :) |
Beta Was this translation helpful? Give feedback.
You are just copying references, not the underlying data.
The problem when using Rayon here is that
PyReadonlyArray<'py, T, D>: !Send
as it basically wraps a&'py PyArray<T, D>
which is notSend
either as it is locked to the lifetime'py
for which the GIL is held by the current thread.At the moment I don't see a better way to achieve what you need. I suspect that the overhead of the three calls to
collect
should be manageable if what you are doing instead ofcumsum
is sufficiently expensive.One alternative that would however require a different signature …