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