use std::marker::PhantomData;
use std::num::NonZero;
use std::sync::Mutex;
use std::sync::atomic::{AtomicUsize, Ordering};
#[must_use]
pub fn available() -> usize {
std::thread::available_parallelism().map_or(1, NonZero::get)
}
pub fn for_each(n: usize, threads: usize, f: impl Fn(usize) + Sync) {
let threads = threads.clamp(1, n.max(1));
if threads == 1 {
(0..n).for_each(f);
return;
}
let next = AtomicUsize::new(0);
let work = || {
loop {
let i = next.fetch_add(1, Ordering::Relaxed);
if i >= n {
break;
}
f(i);
}
};
std::thread::scope(|s| {
for _ in 1..threads {
s.spawn(work);
}
work();
});
}
pub fn map<T: Send>(n: usize, threads: usize, f: impl Fn(usize) -> T + Sync) -> Vec<T> {
let slots: Vec<Mutex<Option<T>>> = (0..n).map(|_| Mutex::new(None)).collect();
for_each(n, threads, |i| *slots[i].lock().unwrap() = Some(f(i)));
slots.into_iter().map(|s| s.into_inner().unwrap().unwrap()).collect()
}
#[derive(Debug)]
pub struct Shared<'a> {
ptr: *mut f32,
len: usize,
_borrow: PhantomData<&'a mut [f32]>,
}
unsafe impl Send for Shared<'_> {}
unsafe impl Sync for Shared<'_> {}
impl<'a> Shared<'a> {
pub fn new(buf: &'a mut [f32]) -> Self {
Self { ptr: buf.as_mut_ptr(), len: buf.len(), _borrow: PhantomData }
}
#[inline(always)]
pub unsafe fn set(&self, i: usize, v: f32) {
assert!(i < self.len);
unsafe { self.ptr.add(i).write(v) }
}
}
impl Shared<'_> {
#[inline(always)]
#[allow(clippy::mut_from_ref)]
pub unsafe fn slice_mut(&self, start: usize, len: usize) -> &mut [f32] {
assert!(start + len <= self.len);
unsafe { std::slice::from_raw_parts_mut(self.ptr.add(start), len) }
}
#[inline(always)]
pub unsafe fn get(&self, i: usize) -> f32 {
assert!(i < self.len);
unsafe { self.ptr.add(i).read() }
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn every_task_runs_once() {
for threads in [1, 2, 7] {
for n in [0, 1, 5, 100] {
let got = map(n, threads, |i| i * 2);
assert_eq!(got, (0..n).map(|i| i * 2).collect::<Vec<_>>());
}
}
}
}