#![cfg(all(feature = "std", feature = "threadteam"))]
use faer::dyn_stack::{MemBuffer, MemStack};
use faer::utils::cost::ParThreshold;
use faer::utils::team::stats;
use faer::{Auto, Mat, Par};
use std::alloc::{GlobalAlloc, Layout, System};
use std::sync::atomic::{AtomicBool, AtomicUsize, Ordering};
struct Counting;
static COUNTING: AtomicBool = AtomicBool::new(false);
static ALLOCATIONS: AtomicUsize = AtomicUsize::new(0);
unsafe impl GlobalAlloc for Counting {
unsafe fn alloc(&self, layout: Layout) -> *mut u8 {
if COUNTING.load(Ordering::Relaxed) {
ALLOCATIONS.fetch_add(1, Ordering::Relaxed);
}
unsafe { System.alloc(layout) }
}
unsafe fn alloc_zeroed(&self, layout: Layout) -> *mut u8 {
if COUNTING.load(Ordering::Relaxed) {
ALLOCATIONS.fetch_add(1, Ordering::Relaxed);
}
unsafe { System.alloc_zeroed(layout) }
}
unsafe fn realloc(&self, ptr: *mut u8, layout: Layout, new_size: usize) -> *mut u8 {
if COUNTING.load(Ordering::Relaxed) {
ALLOCATIONS.fetch_add(1, Ordering::Relaxed);
}
unsafe { System.realloc(ptr, layout, new_size) }
}
unsafe fn dealloc(&self, ptr: *mut u8, layout: Layout) {
unsafe { System.dealloc(ptr, layout) }
}
}
#[global_allocator]
static GLOBAL: Counting = Counting;
fn counted(mut f: impl FnMut()) -> (usize, usize) {
f(); stats::enable(true);
let _ = stats::take();
ALLOCATIONS.store(0, Ordering::SeqCst);
COUNTING.store(true, Ordering::SeqCst);
f();
COUNTING.store(false, Ordering::SeqCst);
let n = ALLOCATIONS.load(Ordering::SeqCst);
stats::enable(false);
let s = stats::take();
let parallel = s.run.iter().skip(2).sum::<usize>() + s.split.iter().skip(2).sum::<usize>();
(n, parallel)
}
fn sample(m: usize, n: usize, seed: u64) -> Mat<f64> {
let mut x = seed.wrapping_mul(6364136223846793005).wrapping_add(1442695040888963407);
Mat::from_fn(m, n, |_, _| {
x = x.wrapping_mul(6364136223846793005).wrapping_add(1442695040888963407);
((x >> 11) as f64 / (1u64 << 53) as f64) - 0.5
})
}
#[test]
fn parallel_regions_allocate_nothing() {
use faer::linalg::cholesky::lblt::factor as lblt;
use faer::linalg::lu::full_pivoting::factor as flu;
for width in [4usize, 8, 70] {
faer::team::with_team(width, |mut par: Par<'_>| {
let n = 96;
let a0 = sample(n, n, 1);
let mut a = a0.clone();
let (mut p, mut pi, mut q, mut qi) = (vec![0usize; n], vec![0usize; n], vec![0usize; n], vec![0usize; n]);
let params = flu::FullPivLuParams { par_threshold: ParThreshold::Fixed(0), ..<flu::FullPivLuParams as Auto<f64>>::auto() };
let mut mem = MemBuffer::new(flu::lu_in_place_scratch::<usize, f64>(n, n, par.shape(), params.into()));
let (allocs, parallel) = counted(|| {
a.copy_from(&a0);
flu::lu_in_place(a.as_mut(), &mut p, &mut pi, &mut q, &mut qi, par.reborrow(), MemStack::new(&mut mem), params.into());
});
assert!(parallel > 0, "full pivoting LU, width {width}: no parallel region");
assert_eq!(allocs, 0, "full pivoting LU, width {width}");
let x = sample(n, n, 2);
let s0 = &x + x.transpose();
let mut s = s0.clone();
let mut sub = faer::diag::Diag::<f64>::zeros(n);
let params = lblt::LbltParams {
pivoting: lblt::PivotingStrategy::Full,
par_threshold: ParThreshold::Fixed(0),
..<lblt::LbltParams as Auto<f64>>::auto()
};
let mut mem = MemBuffer::new(lblt::cholesky_in_place_scratch::<usize, f64>(n, par.shape(), params.into()));
let (allocs, parallel) = counted(|| {
s.copy_from(&s0);
lblt::cholesky_in_place(s.as_mut(), sub.as_mut(), &mut p, &mut pi, par.reborrow(), MemStack::new(&mut mem), params.into());
});
assert!(parallel > 0, "Bunch-Kaufman, width {width}: no parallel region");
assert_eq!(allocs, 0, "Bunch-Kaufman, width {width}");
let subs: Vec<f64> = (0..n).map(|i| sub[i]).collect();
assert!(subs.iter().any(|&x| x != 0.0) && subs.iter().any(|&x| x == 0.0), "Bunch-Kaufman, width {width}: a pivot size never occurred");
let mut left = vec![0usize; 4];
let mut right = vec![0usize; 4];
let (allocs, parallel) = counted(|| {
faer::utils::thread::join_sized(
par.reborrow(),
width / 2,
|_| left.iter_mut().for_each(|x| *x += 1),
|_| right.iter_mut().for_each(|x| *x += 2),
);
});
assert!(parallel > 0, "join_sized, width {width}: no split");
assert_eq!(allocs, 0, "join_sized, width {width}");
});
}
}