#[cfg(feature = "parallel")]
use crate::kernel::same_contiguous_layout;
use crate::kernel::{
build_plan_fused, ensure_same_shape, for_each_inner_block_preordered,
sequential_contiguous_layout, total_len,
};
use crate::maybe_sync::{MaybeSendSync, MaybeSync};
use crate::simd;
use crate::view::{col_major_strides, StridedArray, StridedView, StridedViewMut};
use crate::{Result, StridedError};
use std::ops::Range;
use strided_view::ElementOp;
#[cfg(feature = "parallel")]
use crate::fuse::compute_costs;
use crate::inline::InlineVec;
#[cfg(feature = "parallel")]
use crate::threading::{
for_each_inner_block_with_offsets, mapreduce_threaded_fold, SendPtr, MINTHREADLENGTH,
};
const FOLD_LANES: usize = 16;
const AXIS_SWEEP_MIN_LEN: usize = 16;
const AXIS_SWEEP_BLOCK_BYTES: usize = 64 * 1024;
#[inline(always)]
fn fold_contiguous<T, Op, M, R, U>(src: &[T], init: U, map_fn: &M, reduce_fn: &R) -> U
where
T: Copy,
Op: ElementOp<T>,
M: Fn(T) -> U,
R: Fn(U, U) -> U,
U: Clone,
{
if src.len() < 2 * FOLD_LANES {
let mut acc = init;
for &value in src {
acc = reduce_fn(acc, map_fn(Op::apply(value)));
}
return acc;
}
let (head, rest) = src.split_at(FOLD_LANES);
let mut lanes: [U; FOLD_LANES] = core::array::from_fn(|lane| map_fn(Op::apply(head[lane])));
let mut chunks = rest.chunks_exact(FOLD_LANES);
for chunk in chunks.by_ref() {
for (lane, &value) in lanes.iter_mut().zip(chunk) {
*lane = reduce_fn(lane.clone(), map_fn(Op::apply(value)));
}
}
for (lane, &value) in lanes.iter_mut().zip(chunks.remainder()) {
*lane = reduce_fn(lane.clone(), map_fn(Op::apply(value)));
}
lanes.into_iter().fold(init, reduce_fn)
}
#[inline(always)]
unsafe fn fold_run<T, Op, M, R, U>(
ptr: *const T,
stride: isize,
len: usize,
init: U,
map_fn: &M,
reduce_fn: &R,
) -> U
where
T: Copy,
Op: ElementOp<T>,
M: Fn(T) -> U,
R: Fn(U, U) -> U,
U: Clone,
{
if stride == 1 {
let run = unsafe { std::slice::from_raw_parts(ptr, len) };
return fold_contiguous::<T, Op, M, R, U>(run, init, map_fn, reduce_fn);
}
let mut acc = init;
let mut cursor = ptr;
for index in 0..len {
acc = reduce_fn(acc, map_fn(Op::apply(unsafe { *cursor })));
if index + 1 < len {
cursor = cursor.wrapping_offset(stride);
}
}
acc
}
pub fn reduce<T: Copy + MaybeSendSync, Op: ElementOp<T>, M, R, U>(
src: &StridedView<T, Op>,
map_fn: M,
reduce_fn: R,
init: U,
) -> Result<U>
where
M: Fn(T) -> U + MaybeSync,
R: Fn(U, U) -> U + MaybeSync,
U: Clone + MaybeSendSync,
{
reduce_impl(src, map_fn, reduce_fn, init)
}
fn reduce_impl<T: Copy + MaybeSendSync, Op: ElementOp<T>, M, R, U>(
src: &StridedView<T, Op>,
map_fn: M,
reduce_fn: R,
init: U,
) -> Result<U>
where
M: Fn(T) -> U + MaybeSync,
R: Fn(U, U) -> U + MaybeSync,
U: Clone + MaybeSendSync,
{
let src_ptr = src.ptr();
let src_dims = src.dims();
let src_strides = src.strides();
if total_len(src_dims)? == 0 {
return Ok(init);
}
let contiguous = sequential_contiguous_layout(src_dims, &[src_strides])?;
if contiguous.is_some() {
let len = total_len(src_dims)?;
let src = unsafe { std::slice::from_raw_parts(src_ptr, len) };
return Ok(simd::dispatch_if_large(len, || {
fold_contiguous::<T, Op, M, R, U>(src, init, &map_fn, &reduce_fn)
}));
}
#[cfg(feature = "parallel")]
{
let total = total_len(src_dims)?;
let nthreads = crate::execution_policy::rayon_threads();
if total > MINTHREADLENGTH
&& nthreads > 1
&& same_contiguous_layout(src_dims, &[src_strides]).is_some()
{
let src_slice = unsafe { std::slice::from_raw_parts(src_ptr, total) };
let result = crate::threading::parallel_map_reduce(
0..total,
nthreads,
&|range| {
simd::dispatch_if_large(range.len(), || {
fold_contiguous::<T, Op, M, R, U>(
&src_slice[range],
init.clone(),
&map_fn,
&reduce_fn,
)
})
},
&|a, b| reduce_fn(a, b),
);
return Ok(result);
}
}
let strides_list: [&[isize]; 1] = [src_strides];
let (fused_dims, ordered_strides, plan) =
build_plan_fused(src_dims, &strides_list, None, std::mem::size_of::<T>());
#[cfg(feature = "parallel")]
{
let total = total_len(&fused_dims)?;
let nthreads = crate::execution_policy::rayon_threads();
if total > MINTHREADLENGTH && nthreads > 1 {
let src_send = SendPtr(src_ptr as *mut T);
let costs = compute_costs(&ordered_strides);
let initial_offsets: crate::inline::OperandVec<isize> =
ordered_strides.iter().map(|_| 0isize).collect();
let map_fn_ref = &map_fn;
let reduce_fn_ref = &reduce_fn;
let init_ref = &init;
let result = mapreduce_threaded_fold(
&fused_dims,
&plan.block,
&ordered_strides,
&initial_offsets,
&costs,
nthreads,
&|dims, blocks, strides_list, offsets| {
let mut acc = init_ref.clone();
for_each_inner_block_with_offsets(
dims,
blocks,
strides_list,
offsets,
|offsets, len, strides| {
let mut ptr = unsafe { src_send.as_const().offset(offsets[0]) };
let stride = strides[0];
for _ in 0..len {
let val = Op::apply(unsafe { *ptr });
let mapped = map_fn_ref(val);
acc = reduce_fn_ref(acc.clone(), mapped);
unsafe {
ptr = ptr.offset(stride);
}
}
Ok(())
},
)?;
Ok(acc)
},
&|a, b| reduce_fn_ref(a, b),
)?;
return Ok(result);
}
}
let mut acc = init;
let initial_offsets: crate::inline::OperandVec<isize> =
ordered_strides.iter().map(|_| 0isize).collect();
for_each_inner_block_preordered(
&fused_dims,
&plan.block,
&ordered_strides,
&initial_offsets,
|offsets, len, strides| {
let mut ptr = unsafe { src_ptr.offset(offsets[0]) };
let stride = strides[0];
for _ in 0..len {
let val = Op::apply(unsafe { *ptr });
let mapped = map_fn(val);
acc = reduce_fn(acc.clone(), mapped);
unsafe {
ptr = ptr.offset(stride);
}
}
Ok(())
},
)?;
Ok(acc)
}
#[inline(always)]
fn fold_contiguous2<A, B, OpA, OpB, M, R, U>(
a: &[A],
b: &[B],
init: U,
map_fn: &M,
reduce_fn: &R,
) -> U
where
A: Copy,
B: Copy,
OpA: ElementOp<A>,
OpB: ElementOp<B>,
M: Fn(A, B) -> U,
R: Fn(U, U) -> U,
U: Clone,
{
debug_assert_eq!(a.len(), b.len());
if a.len() < 2 * FOLD_LANES {
let mut acc = init;
for (&value_a, &value_b) in a.iter().zip(b) {
acc = reduce_fn(acc, map_fn(OpA::apply(value_a), OpB::apply(value_b)));
}
return acc;
}
let (head_a, rest_a) = a.split_at(FOLD_LANES);
let (head_b, rest_b) = b.split_at(FOLD_LANES);
let mut lanes: [U; FOLD_LANES] =
core::array::from_fn(|lane| map_fn(OpA::apply(head_a[lane]), OpB::apply(head_b[lane])));
let mut chunks_a = rest_a.chunks_exact(FOLD_LANES);
let mut chunks_b = rest_b.chunks_exact(FOLD_LANES);
for (chunk_a, chunk_b) in chunks_a.by_ref().zip(chunks_b.by_ref()) {
for ((lane, &value_a), &value_b) in lanes.iter_mut().zip(chunk_a).zip(chunk_b) {
*lane = reduce_fn(
lane.clone(),
map_fn(OpA::apply(value_a), OpB::apply(value_b)),
);
}
}
for ((lane, &value_a), &value_b) in lanes
.iter_mut()
.zip(chunks_a.remainder())
.zip(chunks_b.remainder())
{
*lane = reduce_fn(
lane.clone(),
map_fn(OpA::apply(value_a), OpB::apply(value_b)),
);
}
lanes.into_iter().fold(init, reduce_fn)
}
#[inline(always)]
#[allow(clippy::too_many_arguments)]
unsafe fn fold_run2<A, B, OpA, OpB, M, R, U>(
a_ptr: *const A,
a_stride: isize,
b_ptr: *const B,
b_stride: isize,
len: usize,
init: &U,
map_fn: &M,
reduce_fn: &R,
) -> U
where
A: Copy,
B: Copy,
OpA: ElementOp<A>,
OpB: ElementOp<B>,
M: Fn(A, B) -> U,
R: Fn(U, U) -> U,
U: Clone,
{
if a_stride == 1 && b_stride == 1 {
let a = unsafe { std::slice::from_raw_parts(a_ptr, len) };
let b = unsafe { std::slice::from_raw_parts(b_ptr, len) };
return fold_contiguous2::<A, B, OpA, OpB, M, R, U>(a, b, init.clone(), map_fn, reduce_fn);
}
let mut acc = init.clone();
let mut cursor_a = a_ptr;
let mut cursor_b = b_ptr;
for index in 0..len {
let mapped = map_fn(
OpA::apply(unsafe { *cursor_a }),
OpB::apply(unsafe { *cursor_b }),
);
acc = reduce_fn(acc, mapped);
if index + 1 < len {
cursor_a = cursor_a.wrapping_offset(a_stride);
cursor_b = cursor_b.wrapping_offset(b_stride);
}
}
acc
}
pub fn zip_reduce<A, B, OpA, OpB, M, R, U>(
a: &StridedView<A, OpA>,
b: &StridedView<B, OpB>,
map_fn: M,
reduce_fn: R,
init: U,
) -> Result<U>
where
A: Copy + MaybeSendSync,
B: Copy + MaybeSendSync,
OpA: ElementOp<A>,
OpB: ElementOp<B>,
M: Fn(A, B) -> U + MaybeSync,
R: Fn(U, U) -> U + MaybeSync,
U: Clone + MaybeSendSync,
{
ensure_same_shape(a.dims(), b.dims())?;
let a_ptr = a.ptr();
let b_ptr = b.ptr();
let dims = a.dims();
let a_strides = a.strides();
let b_strides = b.strides();
let total = total_len(dims)?;
if total == 0 {
return Ok(init);
}
if sequential_contiguous_layout(dims, &[a_strides, b_strides])?.is_some() {
return Ok(simd::dispatch_if_large(total, || {
let sa = unsafe { std::slice::from_raw_parts(a_ptr, total) };
let sb = unsafe { std::slice::from_raw_parts(b_ptr, total) };
fold_contiguous2::<A, B, OpA, OpB, M, R, U>(sa, sb, init, &map_fn, &reduce_fn)
}));
}
#[cfg(feature = "parallel")]
{
let nthreads = crate::execution_policy::rayon_threads();
if total > MINTHREADLENGTH
&& nthreads > 1
&& same_contiguous_layout(dims, &[a_strides, b_strides]).is_some()
{
let sa = unsafe { std::slice::from_raw_parts(a_ptr, total) };
let sb = unsafe { std::slice::from_raw_parts(b_ptr, total) };
let result = crate::threading::parallel_map_reduce(
0..total,
nthreads,
&|range| {
let len = range.len();
simd::dispatch_if_large(len, || {
fold_contiguous2::<A, B, OpA, OpB, M, R, U>(
&sa[range.clone()],
&sb[range],
init.clone(),
&map_fn,
&reduce_fn,
)
})
},
&|left, right| reduce_fn(left, right),
);
return Ok(result);
}
}
let strides_list: [&[isize]; 2] = [a_strides, b_strides];
let elem_size = std::mem::size_of::<A>().max(std::mem::size_of::<B>());
let (fused_dims, ordered_strides, plan) =
build_plan_fused(dims, &strides_list, None, elem_size);
#[cfg(feature = "parallel")]
{
let total = total_len(&fused_dims)?;
let nthreads = crate::execution_policy::rayon_threads();
if total > MINTHREADLENGTH && nthreads > 1 {
let a_send = SendPtr(a_ptr as *mut A);
let b_send = SendPtr(b_ptr as *mut B);
let costs = compute_costs(&ordered_strides);
let initial_offsets: crate::inline::OperandVec<isize> =
ordered_strides.iter().map(|_| 0isize).collect();
let map_fn_ref = &map_fn;
let reduce_fn_ref = &reduce_fn;
let init_ref = &init;
let result = mapreduce_threaded_fold(
&fused_dims,
&plan.block,
&ordered_strides,
&initial_offsets,
&costs,
nthreads,
&|dims, blocks, strides_list, offsets| {
let mut acc = init_ref.clone();
for_each_inner_block_with_offsets(
dims,
blocks,
strides_list,
offsets,
|offsets, len, strides| {
acc = unsafe {
fold_run2::<A, B, OpA, OpB, M, R, U>(
a_send.as_const().offset(offsets[0]),
strides[0],
b_send.as_const().offset(offsets[1]),
strides[1],
len,
&acc,
map_fn_ref,
reduce_fn_ref,
)
};
Ok(())
},
)?;
Ok(acc)
},
&|left, right| reduce_fn_ref(left, right),
)?;
return Ok(result);
}
}
let mut acc = init;
let initial_offsets: crate::inline::OperandVec<isize> =
ordered_strides.iter().map(|_| 0isize).collect();
for_each_inner_block_preordered(
&fused_dims,
&plan.block,
&ordered_strides,
&initial_offsets,
|offsets, len, strides| {
acc = unsafe {
fold_run2::<A, B, OpA, OpB, M, R, U>(
a_ptr.offset(offsets[0]),
strides[0],
b_ptr.offset(offsets[1]),
strides[1],
len,
&acc,
&map_fn,
&reduce_fn,
)
};
Ok(())
},
)?;
Ok(acc)
}
pub fn reduce_axis<T: Copy + MaybeSendSync, Op: ElementOp<T>, M, R, U>(
src: &StridedView<T, Op>,
axis: usize,
map_fn: M,
reduce_fn: R,
init: U,
) -> Result<StridedArray<U>>
where
M: Fn(T) -> U + MaybeSync,
R: Fn(U, U) -> U + MaybeSync,
U: Clone + MaybeSendSync,
{
let rank = src.ndim();
if axis >= rank {
return Err(StridedError::InvalidAxis { axis, rank });
}
total_len(src.dims())?;
let out_dims: InlineVec<usize> = src
.dims()
.iter()
.enumerate()
.filter(|(i, _)| *i != axis)
.map(|(_, &d)| d)
.collect();
let (out_dims, out_strides) = if out_dims.is_empty() {
let mut dims = InlineVec::new();
dims.push(1usize);
let strides = col_major_strides(&dims);
(dims, strides)
} else {
let strides = col_major_strides(&out_dims);
(out_dims, strides)
};
let total_out = total_len(&out_dims)?;
let mut out =
StridedArray::<U>::from_parts(vec![init.clone(); total_out], &out_dims, &out_strides, 0)?;
{
let mut dest = out.view_mut();
reduce_axis_into_impl(&mut dest, src, axis, map_fn, reduce_fn, init)?;
}
Ok(out)
}
pub fn reduce_axis_into<T: Copy + MaybeSendSync, Op: ElementOp<T>, M, R, U>(
dest: &mut StridedViewMut<U>,
src: &StridedView<T, Op>,
axis: usize,
map_fn: M,
reduce_fn: R,
init: U,
) -> Result<()>
where
M: Fn(T) -> U + MaybeSync,
R: Fn(U, U) -> U + MaybeSync,
U: Clone + MaybeSendSync,
{
let rank = src.ndim();
if axis >= rank {
return Err(StridedError::InvalidAxis { axis, rank });
}
let src_dims = src.dims();
total_len(src_dims)?;
let kept_len = rank - 1;
let dest_len = total_len(dest.dims())?;
if kept_len == 0 {
if dest_len != 1 {
return Err(StridedError::ShapeMismatch(vec![dest_len], vec![1]));
}
} else {
if dest.ndim() != kept_len {
return Err(StridedError::RankMismatch(dest.ndim(), kept_len));
}
let dest_dims = dest.dims();
let mut d = 0usize;
for (i, &src_dim) in src_dims.iter().enumerate() {
if i == axis {
continue;
}
if src_dim != dest_dims[d] {
let expected: Vec<usize> = src_dims
.iter()
.enumerate()
.filter(|(i, _)| *i != axis)
.map(|(_, &d)| d)
.collect();
return Err(StridedError::ShapeMismatch(dest_dims.to_vec(), expected));
}
d += 1;
}
if !crate::layout_check::is_injective_layout(dest.dims(), dest.strides()) {
return Err(StridedError::NonInjectiveOutputLayout);
}
}
reduce_axis_into_impl(dest, src, axis, map_fn, reduce_fn, init)
}
fn reduce_axis_into_impl<T: Copy + MaybeSendSync, Op: ElementOp<T>, M, R, U>(
dest: &mut StridedViewMut<U>,
src: &StridedView<T, Op>,
axis: usize,
map_fn: M,
reduce_fn: R,
init: U,
) -> Result<()>
where
M: Fn(T) -> U + MaybeSync,
R: Fn(U, U) -> U + MaybeSync,
U: Clone + MaybeSendSync,
{
let rank = src.ndim();
let src_dims = src.dims();
let src_strides = src.strides();
let src_ptr = src.ptr();
let dest_len = total_len(dest.dims())?;
let axis_len = src_dims[axis];
let axis_stride = src_strides[axis];
if axis_len == 0 {
fill_outputs_with(dest, &init);
return Ok(());
}
if rank == 1 {
let acc = unsafe {
fold_run::<T, Op, M, R, U>(src_ptr, axis_stride, axis_len, init, &map_fn, &reduce_fn)
};
unsafe { *dest.as_mut_ptr() = acc };
return Ok(());
}
if dest_len == 0 {
return Ok(());
}
let dest_stride_view = dest.strides();
let mut kept: InlineVec<(usize, isize)> = InlineVec::new();
let mut dest_linear = true;
let mut prefix = 1usize;
for (i, (&dim, &src_stride)) in src_dims.iter().zip(src_strides).enumerate() {
if i == axis {
continue;
}
let d = kept.len();
kept.push((dim, src_stride));
if dest_linear && dest_stride_view[d] != prefix as isize {
dest_linear = false;
}
prefix = prefix.saturating_mul(dim);
}
let dest_strides: InlineVec<isize> = if dest_linear {
InlineVec::new()
} else {
dest_stride_view.iter().copied().collect()
};
let out_ptr = dest.as_mut_ptr();
let parts = AxisParts {
kept: &kept,
dest_strides: &dest_strides,
axis_len,
axis_stride,
map_fn: &map_fn,
reduce_fn: &reduce_fn,
init: &init,
};
#[cfg(feature = "parallel")]
{
let work = dest_len.saturating_mul(axis_len);
let nthreads = crate::threading::parallel_threads_for_len(work).min(dest_len);
if nthreads > 1 {
let src_send = SendPtr(src_ptr as *mut T);
let out_send = SendPtr(out_ptr);
let parts = &parts;
if dest_linear {
crate::threading::parallel_for_each(0..dest_len, nthreads, &|range| {
unsafe {
reduce_axis_range_linear::<T, Op, M, R, U>(
src_send.as_const(),
out_send.as_ptr(),
range,
parts,
)
}
});
} else {
crate::threading::parallel_for_each(0..dest_len, nthreads, &|range| {
unsafe {
reduce_axis_range_strided::<T, Op, M, R, U>(
src_send.as_const(),
out_send.as_ptr(),
range,
parts,
)
}
});
}
return Ok(());
}
}
if dest_linear {
unsafe {
reduce_axis_range_linear::<T, Op, M, R, U>(src_ptr, out_ptr, 0..dest_len, &parts)
};
} else {
unsafe {
reduce_axis_range_strided::<T, Op, M, R, U>(src_ptr, out_ptr, 0..dest_len, &parts)
};
}
Ok(())
}
fn fill_outputs_with<U: Clone>(dest: &mut StridedViewMut<U>, value: &U) {
let dims = dest.dims();
if dims.iter().any(|&dim| dim == 0) {
return;
}
let strides = dest.strides();
let rank = dims.len();
let total: usize = dims.iter().product();
let base = dest.as_mut_ptr();
if rank == 0 {
unsafe { *base = value.clone() };
return;
}
let mut coords_inline = [0usize; strided_view::INLINE_RANK];
let mut coords_heap: Vec<usize> = Vec::new();
let coords: &mut [usize] = if rank <= strided_view::INLINE_RANK {
&mut coords_inline[..rank]
} else {
coords_heap.resize(rank, 0);
&mut coords_heap[..]
};
let mut offset = 0isize;
for _ in 0..total {
unsafe { *base.offset(offset) = value.clone() };
for axis in 0..rank {
coords[axis] += 1;
offset += strides[axis];
if coords[axis] < dims[axis] {
break;
}
coords[axis] = 0;
offset -= dims[axis] as isize * strides[axis];
}
}
}
struct AxisParts<'a, M, R, U> {
kept: &'a [(usize, isize)],
dest_strides: &'a [isize],
axis_len: usize,
axis_stride: isize,
map_fn: &'a M,
reduce_fn: &'a R,
init: &'a U,
}
#[inline(never)]
unsafe fn reduce_axis_range_linear<T, Op, M, R, U>(
src: *const T,
out: *mut U,
range: Range<usize>,
parts: &AxisParts<'_, M, R, U>,
) where
T: Copy,
Op: ElementOp<T>,
M: Fn(T) -> U,
R: Fn(U, U) -> U,
U: Clone,
{
let kept = parts.kept;
let (lead_extent, lead_stride) = kept[0];
let sweep = lead_stride == 1 && lead_extent >= AXIS_SWEEP_MIN_LEN && parts.axis_len > 1;
let block = (AXIS_SWEEP_BLOCK_BYTES / std::mem::size_of::<U>().max(1)).max(AXIS_SWEEP_MIN_LEN);
let coords_len = kept.len();
let mut coords_inline = [0usize; strided_view::INLINE_RANK];
let mut coords_heap: Vec<usize> = Vec::new();
let coords: &mut [usize] = if coords_len <= strided_view::INLINE_RANK {
&mut coords_inline[..coords_len]
} else {
coords_heap.resize(coords_len, 0);
&mut coords_heap[..]
};
let mut rest = range.start;
let mut src_off = 0isize;
for (coord, &(extent, stride)) in coords.iter_mut().zip(kept) {
*coord = rest % extent;
rest /= extent;
src_off += *coord as isize * stride;
}
let mut output = range.start;
while output < range.end {
let len = if sweep {
(lead_extent - coords[0]).min(range.end - output).min(block)
} else {
1
};
if sweep {
unsafe { sweep_block::<T, Op, M, R, U>(src, src_off, out.add(output), len, parts) };
} else {
unsafe {
let acc = fold_run::<T, Op, M, R, U>(
src.offset(src_off),
parts.axis_stride,
parts.axis_len,
parts.init.clone(),
parts.map_fn,
parts.reduce_fn,
);
*out.add(output) = acc;
}
}
output += len;
if output >= range.end {
break;
}
coords[0] += len;
src_off += len as isize * lead_stride;
let mut axis = 0;
while coords[axis] == kept[axis].0 {
src_off -= kept[axis].0 as isize * kept[axis].1;
coords[axis] = 0;
axis += 1;
coords[axis] += 1;
src_off += kept[axis].1;
}
}
}
#[inline(never)]
unsafe fn reduce_axis_range_strided<T, Op, M, R, U>(
src: *const T,
out: *mut U,
range: Range<usize>,
parts: &AxisParts<'_, M, R, U>,
) where
T: Copy,
Op: ElementOp<T>,
M: Fn(T) -> U,
R: Fn(U, U) -> U,
U: Clone,
{
let kept = parts.kept;
let dest_strides = parts.dest_strides;
let (lead_extent, lead_stride) = kept[0];
let lead_dest_stride = dest_strides[0];
let sweep = lead_stride == 1
&& lead_dest_stride == 1
&& lead_extent >= AXIS_SWEEP_MIN_LEN
&& parts.axis_len > 1;
let block = (AXIS_SWEEP_BLOCK_BYTES / std::mem::size_of::<U>().max(1)).max(AXIS_SWEEP_MIN_LEN);
let coords_len = kept.len();
let mut coords_inline = [0usize; strided_view::INLINE_RANK];
let mut coords_heap: Vec<usize> = Vec::new();
let coords: &mut [usize] = if coords_len <= strided_view::INLINE_RANK {
&mut coords_inline[..coords_len]
} else {
coords_heap.resize(coords_len, 0);
&mut coords_heap[..]
};
let mut rest = range.start;
let mut src_off = 0isize;
let mut dest_off = 0isize;
for (coord, (&(extent, stride), &dest_stride)) in
coords.iter_mut().zip(kept.iter().zip(dest_strides))
{
*coord = rest % extent;
rest /= extent;
src_off += *coord as isize * stride;
dest_off += *coord as isize * dest_stride;
}
let mut output = range.start;
while output < range.end {
let len = if sweep {
(lead_extent - coords[0]).min(range.end - output).min(block)
} else {
1
};
if sweep {
unsafe {
sweep_block::<T, Op, M, R, U>(src, src_off, out.offset(dest_off), len, parts)
};
} else {
unsafe {
let acc = fold_run::<T, Op, M, R, U>(
src.offset(src_off),
parts.axis_stride,
parts.axis_len,
parts.init.clone(),
parts.map_fn,
parts.reduce_fn,
);
*out.offset(dest_off) = acc;
}
}
output += len;
if output >= range.end {
break;
}
coords[0] += len;
src_off += len as isize * lead_stride;
dest_off += len as isize * lead_dest_stride;
let mut axis = 0;
while coords[axis] == kept[axis].0 {
src_off -= kept[axis].0 as isize * kept[axis].1;
dest_off -= kept[axis].0 as isize * dest_strides[axis];
coords[axis] = 0;
axis += 1;
coords[axis] += 1;
src_off += kept[axis].1;
dest_off += dest_strides[axis];
}
}
}
#[inline(always)]
unsafe fn sweep_block<T, Op, M, R, U>(
src: *const T,
src_off: isize,
out: *mut U,
len: usize,
parts: &AxisParts<'_, M, R, U>,
) where
T: Copy,
Op: ElementOp<T>,
M: Fn(T) -> U,
R: Fn(U, U) -> U,
U: Clone,
{
let map_fn = parts.map_fn;
let reduce_fn = parts.reduce_fn;
let out = unsafe { std::slice::from_raw_parts_mut(out, len) };
let column = |k: usize| {
unsafe {
std::slice::from_raw_parts(src.offset(src_off + k as isize * parts.axis_stride), len)
}
};
let first = column(0);
for (slot, &value) in out.iter_mut().zip(first) {
*slot = reduce_fn(parts.init.clone(), map_fn(Op::apply(value)));
}
let mut k = 1;
while k + 4 <= parts.axis_len {
let (c0, c1, c2, c3) = (column(k), column(k + 1), column(k + 2), column(k + 3));
for (index, slot) in out.iter_mut().enumerate() {
let mut acc = reduce_fn(slot.clone(), map_fn(Op::apply(c0[index])));
acc = reduce_fn(acc, map_fn(Op::apply(c1[index])));
acc = reduce_fn(acc, map_fn(Op::apply(c2[index])));
acc = reduce_fn(acc, map_fn(Op::apply(c3[index])));
*slot = acc;
}
k += 4;
}
while k < parts.axis_len {
let values = column(k);
for (slot, &value) in out.iter_mut().zip(values) {
*slot = reduce_fn(slot.clone(), map_fn(Op::apply(value)));
}
k += 1;
}
}