pub const MIN_PAR: usize = 32;
const MAX_DEFAULT_THREADS: usize = 6;
pub fn num_threads() -> usize {
if let Ok(s) = std::env::var("FFDP_THREADS") {
if let Ok(n) = s.parse::<usize>() {
if n >= 1 {
return n;
}
}
}
std::thread::available_parallelism()
.map(|n| n.get())
.unwrap_or(1)
.min(MAX_DEFAULT_THREADS)
}
pub fn par_map<T, R, F>(items: &[T], threads: usize, f: F) -> Vec<R>
where
T: Sync,
R: Send,
F: Fn(&T) -> R + Sync,
{
let n = items.len();
if threads <= 1 || n < MIN_PAR {
return items.iter().map(&f).collect();
}
let t = threads.min(n);
let chunk = n.div_ceil(t);
let chunks: Vec<&[T]> = items.chunks(chunk).collect();
let f = &f;
let mut parts: Vec<Vec<R>> = Vec::with_capacity(chunks.len());
std::thread::scope(|scope| {
let handles: Vec<_> = chunks
.iter()
.map(|c| scope.spawn(move || c.iter().map(f).collect::<Vec<R>>()))
.collect();
for h in handles {
parts.push(h.join().expect("worker thread panicked"));
}
});
parts.into_iter().flatten().collect()
}
pub fn par_map_with<T, R, S, I, F>(items: &[T], threads: usize, init: I, f: F) -> Vec<R>
where
T: Sync,
R: Send,
I: Fn() -> S + Sync,
F: Fn(&mut S, &T) -> R + Sync,
{
let n = items.len();
if threads <= 1 || n < MIN_PAR {
let mut s = init();
return items.iter().map(|x| f(&mut s, x)).collect();
}
let t = threads.min(n);
let chunk = n.div_ceil(t);
let chunks: Vec<&[T]> = items.chunks(chunk).collect();
let init = &init;
let f = &f;
let mut parts: Vec<Vec<R>> = Vec::with_capacity(chunks.len());
std::thread::scope(|scope| {
let handles: Vec<_> = chunks
.iter()
.map(|c| {
scope.spawn(move || {
let mut s = init();
c.iter().map(|x| f(&mut s, x)).collect::<Vec<R>>()
})
})
.collect();
for h in handles {
parts.push(h.join().expect("worker thread panicked"));
}
});
parts.into_iter().flatten().collect()
}