Skip to content

Commit 5298d6a

Browse files
bors[bot]cuviper
andauthored
Merge #887
887: Reduce the amount of generic code for ParallelExtend r=cuviper a=cuviper For unindexed parallel itererators, we've implemented `ParallelExtend` for most collections using an intermediate `LinkedList<Vec<T>>` like: ```rust par_iter .into_par_iter() .fold(Vec::new, vec_push) .map(as_list) .reduce(LinkedList::new, list_append) ``` However, this introduces `Fold`, `Map`, and `Reduce` types that are all dependent on the input iterator type. When it comes to very complicated cases like nested tuple unzips, this can add up quickly. For example, in rust-lang/rust#68926 an 8-way unzip leads to 3.7GB of LLVM IR, with lines up to 67K characters in long generic types. Now we add a new `ListVecConsumer` that is not generic at all itself, and implements `Consumer<T>` etc. generic only on the item type. So each collection now gets the same `LinkedList<Vec<T>>` as before with: ```rust par_iter.into_par_iter().drive_unindexed(ListVecConsumer); ``` Each implementation now also separates the code that doesn't need to be iterator-specific to a separate function, for their `reserve` and final `extend` from the list data. That 8-way unzip is now _only_ 1.5GB with lines up to 17K characters. Compile time drops from 12.8s to 7.7s debug, 32.1s to 26.9s release. Co-authored-by: Josh Stone <[email protected]>
2 parents 67c2565 + 7d2444a commit 5298d6a

File tree

2 files changed

+315
-117
lines changed

2 files changed

+315
-117
lines changed

src/iter/collect/mod.rs

Lines changed: 2 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
use super::{IndexedParallelIterator, IntoParallelIterator, ParallelExtend, ParallelIterator};
1+
use super::{IndexedParallelIterator, ParallelIterator};
22
use std::mem::MaybeUninit;
33
use std::slice;
44

@@ -33,7 +33,7 @@ where
3333
/// *any* `ParallelIterator` here, and `CollectConsumer` has to also implement
3434
/// `UnindexedConsumer`. That implementation panics `unreachable!` in case
3535
/// there's a bug where we actually do try to use this unindexed.
36-
fn special_extend<I, T>(pi: I, len: usize, v: &mut Vec<T>)
36+
pub(super) fn special_extend<I, T>(pi: I, len: usize, v: &mut Vec<T>)
3737
where
3838
I: ParallelIterator<Item = T>,
3939
T: Send,
@@ -141,33 +141,3 @@ impl<'c, T: Send + 'c> Collect<'c, T> {
141141
unsafe { slice::from_raw_parts_mut(tail_ptr, len) }
142142
}
143143
}
144-
145-
/// Extends a vector with items from a parallel iterator.
146-
impl<T> ParallelExtend<T> for Vec<T>
147-
where
148-
T: Send,
149-
{
150-
fn par_extend<I>(&mut self, par_iter: I)
151-
where
152-
I: IntoParallelIterator<Item = T>,
153-
{
154-
// See the vec_collect benchmarks in rayon-demo for different strategies.
155-
let par_iter = par_iter.into_par_iter();
156-
match par_iter.opt_len() {
157-
Some(len) => {
158-
// When Rust gets specialization, we can get here for indexed iterators
159-
// without relying on `opt_len`. Until then, `special_extend()` fakes
160-
// an unindexed mode on the promise that `opt_len()` is accurate.
161-
special_extend(par_iter, len, self);
162-
}
163-
None => {
164-
// This works like `extend`, but `Vec::append` is more efficient.
165-
let list = super::extend::collect(par_iter);
166-
self.reserve(super::extend::len(&list));
167-
for mut vec in list {
168-
self.append(&mut vec);
169-
}
170-
}
171-
}
172-
}
173-
}

0 commit comments

Comments
 (0)