use std::cell::UnsafeCell;
use std::mem::MaybeUninit;
use std::sync::atomic::{AtomicUsize, Ordering};
pub fn worker_count() -> usize {
std::env::var("LBZIP2_THREADS")
.ok()
.and_then(|v| v.parse::<usize>().ok())
.unwrap_or_else(|| {
std::thread::available_parallelism()
.map(|n| n.get())
.unwrap_or(4)
})
.max(1)
}
pub fn par_map<T, F>(n: usize, f: F) -> Vec<T>
where
T: Send,
F: Fn(usize) -> T + Sync,
{
if n == 0 {
return Vec::new();
}
let threads = worker_count().min(n);
if threads <= 1 {
return (0..n).map(f).collect();
}
struct Slots<T>(Vec<UnsafeCell<MaybeUninit<T>>>);
unsafe impl<T: Send> Sync for Slots<T> {}
let next = AtomicUsize::new(0);
let slots = Slots((0..n).map(|_| UnsafeCell::new(MaybeUninit::uninit())).collect());
let next_ref = &next;
let slots_ref = &slots;
let f_ref = &f;
std::thread::scope(|s| {
for _ in 0..threads {
s.spawn(move || loop {
let i = next_ref.fetch_add(1, Ordering::Relaxed);
if i >= n {
break;
}
let v = f_ref(i);
unsafe { (*slots_ref.0[i].get()).write(v); }
});
}
});
slots
.0
.into_iter()
.map(|cell| unsafe { cell.into_inner().assume_init() })
.collect()
}