1use crate::kernel::{
22 build_plan_fused, build_plan_fused_small, ensure_same_shape, for_each_inner_block_preordered,
23 sequential_contiguous_layout, total_len, SMALL_TENSOR_THRESHOLD,
24};
25use crate::layout_check::is_injective_layout;
26use crate::simd;
27use crate::view::{StridedView, StridedViewMut};
28use crate::{Exec, Result, StridedError};
29use strided_view::ElementOp;
30
31use crate::cost::KernelClass;
32use crate::fuse::compute_costs;
33use crate::threading::{for_each_inner_block_with_offsets, is_parallel, mapreduce_threaded};
34
35type Body<'a> = &'a (dyn Fn(&[isize], usize, &[isize]) + Sync);
37
38struct Raw<T>(*mut T);
41impl<T> Clone for Raw<T> {
42 fn clone(&self) -> Self {
43 *self
44 }
45}
46impl<T> Copy for Raw<T> {}
47unsafe impl<T> Send for Raw<T> {}
49unsafe impl<T> Sync for Raw<T> {}
50impl<T> Raw<T> {
51 fn get(self) -> *mut T {
53 self.0
54 }
55}
56
57fn validate_destination(dims: &[usize], strides: &[isize]) -> Result<()> {
58 if is_injective_layout(dims, strides) {
59 Ok(())
60 } else {
61 Err(StridedError::NonInjectiveOutputLayout)
62 }
63}
64
65fn run_update(
77 dims: &[usize],
78 strides_list: &[&[isize]],
79 elem_size: usize,
80 body: Body<'_>,
81 exec: Exec<'_>,
82) -> Result<()> {
83 let total = total_len(dims)?;
84 if total == 0 {
85 return Ok(());
86 }
87 if sequential_contiguous_layout(KernelClass::Map, dims, strides_list, &exec)?.is_some() {
92 let zeros: crate::inline::OperandVec<isize> = strides_list.iter().map(|_| 0isize).collect();
93 let ones: crate::inline::OperandVec<isize> = strides_list.iter().map(|_| 1isize).collect();
94 body(&zeros, total, &ones);
95 return Ok(());
96 }
97 if dims.iter().all(|&d| d == 1) {
99 let zeros: crate::inline::OperandVec<isize> = strides_list.iter().map(|_| 0isize).collect();
100 body(&zeros, 1, &zeros);
101 return Ok(());
102 }
103 let (fused_dims, ordered_strides, plan) = if total <= SMALL_TENSOR_THRESHOLD {
105 build_plan_fused_small(dims, strides_list)
106 } else {
107 build_plan_fused(dims, strides_list, Some(0), elem_size)
108 };
109
110 {
111 let total = total_len(&fused_dims)?;
112 if is_parallel(KernelClass::Map, total, &exec) {
113 let costs = compute_costs(&ordered_strides);
114 let initial_offsets: crate::inline::OperandVec<isize> =
115 strides_list.iter().map(|_| 0isize).collect();
116 return mapreduce_threaded(
117 &fused_dims,
118 &plan.block,
119 &ordered_strides,
120 &initial_offsets,
121 &costs,
122 KernelClass::Map,
123 exec,
124 &|dims, blocks, strides_list, offsets| {
125 for_each_inner_block_with_offsets(
126 dims,
127 blocks,
128 strides_list,
129 offsets,
130 |offsets, len, strides| {
131 body(offsets, len, strides);
132 Ok(())
133 },
134 )
135 },
136 );
137 }
138 }
139
140 let initial_offsets: crate::inline::OperandVec<isize> =
141 ordered_strides.iter().map(|_| 0isize).collect();
142 for_each_inner_block_preordered(
143 &fused_dims,
144 &plan.block,
145 &ordered_strides,
146 &initial_offsets,
147 |offsets, len, strides| {
148 body(offsets, len, strides);
149 Ok(())
150 },
151 )
152}
153
154#[inline(always)]
161unsafe fn inner_loop_update1<D: Copy, OpD: ElementOp<D>>(
162 dp: *mut D,
163 ds: isize,
164 len: usize,
165 f: &impl Fn(D) -> D,
166) {
167 if ds == 1 {
168 let dst = std::slice::from_raw_parts_mut(dp, len);
169 simd::dispatch_if_large(len, || {
170 for d in dst.iter_mut() {
171 *d = f(OpD::apply(*d));
172 }
173 });
174 } else {
175 let mut dp = dp;
176 for _ in 0..len {
177 *dp = f(OpD::apply(*dp));
178 dp = dp.wrapping_offset(ds);
179 }
180 }
181}
182
183#[inline(always)]
189unsafe fn inner_loop_update2<D: Copy, A: Copy, OpD: ElementOp<D>, OpA: ElementOp<A>>(
190 dp: *mut D,
191 ds: isize,
192 ap: *const A,
193 a_s: isize,
194 len: usize,
195 f: &impl Fn(D, A) -> D,
196) {
197 if ds == 1 && a_s == 1 {
198 let src_a = std::slice::from_raw_parts(ap, len);
199 let dst = std::slice::from_raw_parts_mut(dp, len);
200 simd::dispatch_if_large(len, || {
201 for (d, &a) in dst.iter_mut().zip(src_a) {
202 *d = f(OpD::apply(*d), OpA::apply(a));
203 }
204 });
205 } else {
206 let (mut dp, mut ap) = (dp, ap);
207 for _ in 0..len {
208 *dp = f(OpD::apply(*dp), OpA::apply(*ap));
209 dp = dp.wrapping_offset(ds);
210 ap = ap.wrapping_offset(a_s);
211 }
212 }
213}
214
215#[inline(always)]
220#[allow(clippy::too_many_arguments)] unsafe fn inner_loop_update3<
222 D: Copy,
223 A: Copy,
224 B: Copy,
225 OpD: ElementOp<D>,
226 OpA: ElementOp<A>,
227 OpB: ElementOp<B>,
228>(
229 dp: *mut D,
230 ds: isize,
231 ap: *const A,
232 a_s: isize,
233 bp: *const B,
234 b_s: isize,
235 len: usize,
236 f: &impl Fn(D, A, B) -> D,
237) {
238 if ds == 1 && a_s == 1 && b_s == 1 {
239 let src_a = std::slice::from_raw_parts(ap, len);
240 let src_b = std::slice::from_raw_parts(bp, len);
241 let dst = std::slice::from_raw_parts_mut(dp, len);
242 simd::dispatch_if_large(len, || {
243 for ((d, &a), &b) in dst.iter_mut().zip(src_a).zip(src_b) {
244 *d = f(OpD::apply(*d), OpA::apply(a), OpB::apply(b));
245 }
246 });
247 } else {
248 let (mut dp, mut ap, mut bp) = (dp, ap, bp);
249 for _ in 0..len {
250 *dp = f(OpD::apply(*dp), OpA::apply(*ap), OpB::apply(*bp));
251 dp = dp.wrapping_offset(ds);
252 ap = ap.wrapping_offset(a_s);
253 bp = bp.wrapping_offset(b_s);
254 }
255 }
256}
257
258pub fn map_update_into<D, OpD>(
276 dest: &mut StridedViewMut<D>,
277 f: impl Fn(D) -> D + Sync,
278 exec: Exec<'_>,
279) -> Result<()>
280where
281 D: Copy + Send + Sync,
282 OpD: ElementOp<D>,
283{
284 validate_destination(dest.dims(), dest.strides())?;
285 let dp = Raw(dest.as_mut_ptr());
286 run_update(
287 dest.dims(),
288 &[dest.strides()],
289 std::mem::size_of::<D>(),
290 &|offsets, len, strides| {
291 unsafe {
293 inner_loop_update1::<D, OpD>(dp.get().offset(offsets[0]), strides[0], len, &f);
294 }
295 },
296 exec,
297 )
298}
299
300pub fn zip_update2_into<D, A, OpD, OpA>(
321 dest: &mut StridedViewMut<D>,
322 a: &StridedView<A, OpA>,
323 f: impl Fn(D, A) -> D + Sync,
324 exec: Exec<'_>,
325) -> Result<()>
326where
327 D: Copy + Send + Sync,
328 A: Copy + Send + Sync,
329 OpD: ElementOp<D>,
330 OpA: ElementOp<A>,
331{
332 ensure_same_shape(dest.dims(), a.dims())?;
333 validate_destination(dest.dims(), dest.strides())?;
334 let dp = Raw(dest.as_mut_ptr());
335 let ap = Raw(a.ptr() as *mut A);
336 run_update(
337 dest.dims(),
338 &[dest.strides(), a.strides()],
339 std::mem::size_of::<D>().max(std::mem::size_of::<A>()),
340 &|offsets, len, strides| {
341 unsafe {
343 inner_loop_update2::<D, A, OpD, OpA>(
344 dp.get().offset(offsets[0]),
345 strides[0],
346 ap.get().offset(offsets[1]).cast_const(),
347 strides[1],
348 len,
349 &f,
350 );
351 }
352 },
353 exec,
354 )
355}
356
357pub fn zip_update3_into<D, A, B, OpD, OpA, OpB>(
381 dest: &mut StridedViewMut<D>,
382 a: &StridedView<A, OpA>,
383 b: &StridedView<B, OpB>,
384 f: impl Fn(D, A, B) -> D + Sync,
385 exec: Exec<'_>,
386) -> Result<()>
387where
388 D: Copy + Send + Sync,
389 A: Copy + Send + Sync,
390 B: Copy + Send + Sync,
391 OpD: ElementOp<D>,
392 OpA: ElementOp<A>,
393 OpB: ElementOp<B>,
394{
395 ensure_same_shape(dest.dims(), a.dims())?;
396 ensure_same_shape(dest.dims(), b.dims())?;
397 validate_destination(dest.dims(), dest.strides())?;
398 let dp = Raw(dest.as_mut_ptr());
399 let ap = Raw(a.ptr() as *mut A);
400 let bp = Raw(b.ptr() as *mut B);
401 run_update(
402 dest.dims(),
403 &[dest.strides(), a.strides(), b.strides()],
404 std::mem::size_of::<D>()
405 .max(std::mem::size_of::<A>())
406 .max(std::mem::size_of::<B>()),
407 &|offsets, len, strides| {
408 unsafe {
410 inner_loop_update3::<D, A, B, OpD, OpA, OpB>(
411 dp.get().offset(offsets[0]),
412 strides[0],
413 ap.get().offset(offsets[1]).cast_const(),
414 strides[1],
415 bp.get().offset(offsets[2]).cast_const(),
416 strides[2],
417 len,
418 &f,
419 );
420 }
421 },
422 exec,
423 )
424}
425
426#[cfg(test)]
427#[path = "update_view/tests/tests.rs"]
428mod tests;