Skip to content

Commit 72e6798

Browse files
authored
Merge pull request #1081 from adamreichold/with-min-len
Add Parallel::with_min_len to limit splitting
2 parents ee5880b + 4efc563 commit 72e6798

File tree

2 files changed

+57
-10
lines changed

2 files changed

+57
-10
lines changed

src/parallel/mod.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,10 @@
3232
//! “unindexed”. Use ndarray’s [Zip] for lock step parallel iteration of
3333
//! multiple arrays or producers at a time.
3434
//!
35+
//! For the unindexed parallel iterators, an inherent method [`with_min_len`](Parallel::with_min_len)
36+
//! is provided to limit the number of elements each parallel task processes in way that is
37+
//! similar to Rayon's [`IndexedParallelIterator::with_min_len`](rayon::prelude::IndexedParallelIterator::with_min_len).
38+
//!
3539
//! # Examples
3640
//!
3741
//! ## Arrays and array views

src/parallel/par.rs

Lines changed: 53 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -21,11 +21,14 @@ use crate::split_at::SplitPreference;
2121
#[derive(Copy, Clone, Debug)]
2222
pub struct Parallel<I> {
2323
iter: I,
24+
min_len: usize,
2425
}
2526

27+
const DEFAULT_MIN_LEN: usize = 1;
28+
2629
/// Parallel producer wrapper.
2730
#[derive(Copy, Clone, Debug)]
28-
struct ParallelProducer<I>(I);
31+
struct ParallelProducer<I>(I, usize);
2932

3033
macro_rules! par_iter_wrapper {
3134
// thread_bounds are either Sync or Send + Sync
@@ -40,6 +43,7 @@ macro_rules! par_iter_wrapper {
4043
fn into_par_iter(self) -> Self::Iter {
4144
Parallel {
4245
iter: self,
46+
min_len: DEFAULT_MIN_LEN,
4347
}
4448
}
4549
}
@@ -67,7 +71,7 @@ macro_rules! par_iter_wrapper {
6771
fn with_producer<Cb>(self, callback: Cb) -> Cb::Output
6872
where Cb: ProducerCallback<Self::Item>
6973
{
70-
callback.callback(ParallelProducer(self.iter))
74+
callback.callback(ParallelProducer(self.iter, self.min_len))
7175
}
7276

7377
fn len(&self) -> usize {
@@ -106,7 +110,7 @@ macro_rules! par_iter_wrapper {
106110

107111
fn split_at(self, i: usize) -> (Self, Self) {
108112
let (a, b) = self.0.split_at(i);
109-
(ParallelProducer(a), ParallelProducer(b))
113+
(ParallelProducer(a, self.1), ParallelProducer(b, self.1))
110114
}
111115
}
112116

@@ -131,11 +135,11 @@ macro_rules! par_iter_view_wrapper {
131135
fn into_par_iter(self) -> Self::Iter {
132136
Parallel {
133137
iter: self,
138+
min_len: DEFAULT_MIN_LEN,
134139
}
135140
}
136141
}
137142

138-
139143
impl<'a, A, D> ParallelIterator for Parallel<$view_name<'a, A, D>>
140144
where D: Dimension,
141145
A: $($thread_bounds)*,
@@ -144,28 +148,47 @@ macro_rules! par_iter_view_wrapper {
144148
fn drive_unindexed<C>(self, consumer: C) -> C::Result
145149
where C: UnindexedConsumer<Self::Item>
146150
{
147-
bridge_unindexed(ParallelProducer(self.iter), consumer)
151+
bridge_unindexed(ParallelProducer(self.iter, self.min_len), consumer)
148152
}
149153

150154
fn opt_len(&self) -> Option<usize> {
151155
None
152156
}
153157
}
154158

159+
impl<'a, A, D> Parallel<$view_name<'a, A, D>>
160+
where D: Dimension,
161+
A: $($thread_bounds)*,
162+
{
163+
/// Sets the minimum number of elements desired to process in each job. This will not be
164+
/// split any smaller than this length, but of course a producer could already be smaller
165+
/// to begin with.
166+
///
167+
/// ***Panics*** if `min_len` is zero.
168+
pub fn with_min_len(self, min_len: usize) -> Self {
169+
assert_ne!(min_len, 0, "Minimum number of elements must at least be one to avoid splitting off empty tasks.");
170+
171+
Self {
172+
min_len,
173+
..self
174+
}
175+
}
176+
}
177+
155178
impl<'a, A, D> UnindexedProducer for ParallelProducer<$view_name<'a, A, D>>
156179
where D: Dimension,
157180
A: $($thread_bounds)*,
158181
{
159182
type Item = <$view_name<'a, A, D> as IntoIterator>::Item;
160183
fn split(self) -> (Self, Option<Self>) {
161-
if self.0.len() <= 1 {
184+
if self.0.len() <= self.1 {
162185
return (self, None)
163186
}
164187
let array = self.0;
165188
let max_axis = array.max_stride_axis();
166189
let mid = array.len_of(max_axis) / 2;
167190
let (a, b) = array.split_at(max_axis, mid);
168-
(ParallelProducer(a), Some(ParallelProducer(b)))
191+
(ParallelProducer(a, self.1), Some(ParallelProducer(b, self.1)))
169192
}
170193

171194
fn fold_with<F>(self, folder: F) -> F
@@ -217,6 +240,7 @@ macro_rules! zip_impl {
217240
fn into_par_iter(self) -> Self::Iter {
218241
Parallel {
219242
iter: self,
243+
min_len: DEFAULT_MIN_LEN,
220244
}
221245
}
222246
}
@@ -233,7 +257,7 @@ macro_rules! zip_impl {
233257
fn drive_unindexed<Cons>(self, consumer: Cons) -> Cons::Result
234258
where Cons: UnindexedConsumer<Self::Item>
235259
{
236-
bridge_unindexed(ParallelProducer(self.iter), consumer)
260+
bridge_unindexed(ParallelProducer(self.iter, self.min_len), consumer)
237261
}
238262

239263
fn opt_len(&self) -> Option<usize> {
@@ -251,11 +275,11 @@ macro_rules! zip_impl {
251275
type Item = ($($p::Item ,)*);
252276

253277
fn split(self) -> (Self, Option<Self>) {
254-
if !self.0.can_split() {
278+
if self.0.size() <= self.1 {
255279
return (self, None)
256280
}
257281
let (a, b) = self.0.split();
258-
(ParallelProducer(a), Some(ParallelProducer(b)))
282+
(ParallelProducer(a, self.1), Some(ParallelProducer(b, self.1)))
259283
}
260284

261285
fn fold_with<Fold>(self, folder: Fold) -> Fold
@@ -284,6 +308,25 @@ zip_impl! {
284308
[P1 P2 P3 P4 P5 P6],
285309
}
286310

311+
impl<D, Parts> Parallel<Zip<Parts, D>>
312+
where
313+
D: Dimension,
314+
{
315+
/// Sets the minimum number of elements desired to process in each job. This will not be
316+
/// split any smaller than this length, but of course a producer could already be smaller
317+
/// to begin with.
318+
///
319+
/// ***Panics*** if `min_len` is zero.
320+
pub fn with_min_len(self, min_len: usize) -> Self {
321+
assert_ne!(min_len, 0, "Minimum number of elements must at least be one to avoid splitting off empty tasks.");
322+
323+
Self {
324+
min_len,
325+
..self
326+
}
327+
}
328+
}
329+
287330
/// A parallel iterator (unindexed) that produces the splits of the array
288331
/// or producer `P`.
289332
pub(crate) struct ParallelSplits<P> {

0 commit comments

Comments
 (0)