1use crate::align::Alignment;
15use crate::arch::SimdArch;
16use crate::execution::ExecutionMode;
17use crate::kernel::SimdKernel;
18use crate::ops::ElementOp;
19use crate::scalar::Scalar;
20use crate::view::{SimdError, SimdView};
21
22const NT_STORE_MIN_BYTES: usize = 8 * 1024 * 1024;
31
32impl<'a, T: 'a, Arch: SimdArch + SimdKernel<T>, Align: Alignment, Mode: ExecutionMode, Ref: 'a>
33 SimdView<'a, T, Arch, Align, Mode, Ref>
34where
35 T: Scalar,
36{
37 #[inline(always)]
42 pub fn sum(&self) -> T {
43 let data = self.as_slice();
44 let len = data.len();
45 let lane_count = Arch::LANE_COUNT;
46 let unroll_factor = Arch::UNROLL_FACTOR;
47 let chunk_size = lane_count * unroll_factor;
48 let unrolled_simd_len = (len / chunk_size) * chunk_size;
49 let simd_len = (len / lane_count) * lane_count;
50 let mut ptr = data.as_ptr();
51
52 let accumulator = unsafe {
53 if unrolled_simd_len > 0 {
54 let load = |p| {
55 if crate::align::is_aligned_for_arch::<Arch, Align>() {
56 Arch::load_aligned(p)
57 } else {
58 Arch::load_unaligned(p)
59 }
60 };
61
62 let mut acc0 = load(ptr);
63 let mut acc1 = load(ptr.add(lane_count));
64 let mut acc2 = load(ptr.add(lane_count * 2));
65 let mut acc3 = load(ptr.add(lane_count * 3));
66 ptr = ptr.add(chunk_size);
67
68 for _ in 1..(unrolled_simd_len / chunk_size) {
69 let v0 = load(ptr);
70 let v1 = load(ptr.add(lane_count));
71 let v2 = load(ptr.add(lane_count * 2));
72 let v3 = load(ptr.add(lane_count * 3));
73
74 acc0 = Arch::add(acc0, v0);
75 acc1 = Arch::add(acc1, v1);
76 acc2 = Arch::add(acc2, v2);
77 acc3 = Arch::add(acc3, v3);
78
79 ptr = ptr.add(chunk_size);
80 }
81
82 let mut acc = Arch::add(acc0, acc1);
83 acc = Arch::add(acc, acc2);
84 acc = Arch::add(acc, acc3);
85 Some(acc)
86 } else {
87 None
88 }
89 };
90
91 let mut acc = if let Some(a) = accumulator {
92 a
93 } else {
94 unsafe { Arch::zero() }
95 };
96
97 unsafe {
99 let mut middle_ptr = data.as_ptr().add(unrolled_simd_len);
100 for _ in 0..((simd_len - unrolled_simd_len) / lane_count) {
101 let val = if crate::align::is_aligned_for_arch::<Arch, Align>() {
102 Arch::load_aligned(middle_ptr)
103 } else {
104 Arch::load_unaligned(middle_ptr)
105 };
106 acc = Arch::add(acc, val);
107 middle_ptr = middle_ptr.add(lane_count);
108 }
109 }
110
111 let mut total = unsafe { Arch::sum_reduce(acc) };
112
113 for i in simd_len..len {
115 total += data[i];
116 }
117
118 total
119 }
120
121 #[inline(always)]
126 pub fn dot<ORef>(
127 &self,
128 other: &SimdView<'_, T, Arch, Align, Mode, ORef>,
129 ) -> Result<T, SimdError>
130 where
131 ORef: 'a,
132 {
133 super::check_lengths_equal(self.len(), other.len())?;
134
135 let len = self.len();
136 let lane_count = Arch::LANE_COUNT;
137 let unroll_factor = Arch::UNROLL_FACTOR;
138 let chunk_size = lane_count * unroll_factor;
139 let unrolled_simd_len = (len / chunk_size) * chunk_size;
140
141 let mut ptr1 = self.as_slice().as_ptr();
142 let mut ptr2 = other.as_slice().as_ptr();
143
144 let accumulator = unsafe {
145 if unrolled_simd_len > 0 {
146 let load = |p| {
147 if crate::align::is_aligned_for_arch::<Arch, Align>() {
148 Arch::load_aligned(p)
149 } else {
150 Arch::load_unaligned(p)
151 }
152 };
153
154 let v0_1 = load(ptr1);
155 let v0_2 = load(ptr2);
156 let mut acc0 = Arch::mul(v0_1, v0_2);
157
158 let v1_1 = load(ptr1.add(lane_count));
159 let v1_2 = load(ptr2.add(lane_count));
160 let mut acc1 = Arch::mul(v1_1, v1_2);
161
162 let v2_1 = load(ptr1.add(lane_count * 2));
163 let v2_2 = load(ptr2.add(lane_count * 2));
164 let mut acc2 = Arch::mul(v2_1, v2_2);
165
166 let v3_1 = load(ptr1.add(lane_count * 3));
167 let v3_2 = load(ptr2.add(lane_count * 3));
168 let mut acc3 = Arch::mul(v3_1, v3_2);
169
170 ptr1 = ptr1.add(chunk_size);
171 ptr2 = ptr2.add(chunk_size);
172
173 for _ in 1..(unrolled_simd_len / chunk_size) {
174 let v0_1 = load(ptr1);
175 let v0_2 = load(ptr2);
176 acc0 = Arch::fmadd(v0_1, v0_2, acc0);
177
178 let v1_1 = load(ptr1.add(lane_count));
179 let v1_2 = load(ptr2.add(lane_count));
180 acc1 = Arch::fmadd(v1_1, v1_2, acc1);
181
182 let v2_1 = load(ptr1.add(lane_count * 2));
183 let v2_2 = load(ptr2.add(lane_count * 2));
184 acc2 = Arch::fmadd(v2_1, v2_2, acc2);
185
186 let v3_1 = load(ptr1.add(lane_count * 3));
187 let v3_2 = load(ptr2.add(lane_count * 3));
188 acc3 = Arch::fmadd(v3_1, v3_2, acc3);
189
190 ptr1 = ptr1.add(chunk_size);
191 ptr2 = ptr2.add(chunk_size);
192 }
193
194 let mut acc = Arch::add(acc0, acc1);
195 acc = Arch::add(acc, acc2);
196 acc = Arch::add(acc, acc3);
197 Some(acc)
198 } else {
199 None
200 }
201 };
202
203 let simd_len = (len / lane_count) * lane_count;
204
205 let mut acc_vec = accumulator;
212 unsafe {
213 let load = |p| {
214 if crate::align::is_aligned_for_arch::<Arch, Align>() {
215 Arch::load_aligned(p)
216 } else {
217 Arch::load_unaligned(p)
218 }
219 };
220 let mut middle_ptr1 = self.as_slice().as_ptr().add(unrolled_simd_len);
221 let mut middle_ptr2 = other.as_slice().as_ptr().add(unrolled_simd_len);
222 for _ in 0..((simd_len - unrolled_simd_len) / lane_count) {
223 let v1 = load(middle_ptr1);
224 let v2 = load(middle_ptr2);
225 acc_vec = Some(match acc_vec {
226 Some(a) => Arch::fmadd(v1, v2, a),
227 None => Arch::mul(v1, v2),
228 });
229 middle_ptr1 = middle_ptr1.add(lane_count);
230 middle_ptr2 = middle_ptr2.add(lane_count);
231 }
232 }
233 let mut total = match acc_vec {
234 Some(acc) => unsafe { Arch::sum_reduce(acc) },
235 None => T::ZERO,
236 };
237
238 let s_slice = self.as_slice();
240 let o_slice = other.as_slice();
241 for i in simd_len..len {
242 total += s_slice[i] * o_slice[i];
243 }
244
245 Ok(total)
246 }
247
248 #[inline(always)]
254 pub fn elementwise_mul<ORef>(
255 &self,
256 other: &SimdView<'_, T, Arch, Align, Mode, ORef>,
257 out: &mut [T],
258 ) -> Result<(), SimdError>
259 where
260 ORef: 'a,
261 {
262 super::check_lengths_equal(self.len(), other.len())?;
263 super::check_output_length(self.len(), out.len())?;
264
265 let len = self.len();
266 let lane_count = Arch::LANE_COUNT;
267 let simd_len = (len / lane_count) * lane_count;
268
269 let mut ptr1 = self.as_slice().as_ptr();
270 let mut ptr2 = other.as_slice().as_ptr();
271 let mut ptr_out = out.as_mut_ptr();
272
273 unsafe {
274 let load = |p| {
275 if crate::align::is_aligned_for_arch::<Arch, Align>() {
276 Arch::load_aligned(p)
277 } else {
278 Arch::load_unaligned(p)
279 }
280 };
281
282 let store = |p, val| {
283 let is_out_aligned = crate::align::is_aligned_for_arch::<Arch, Align>()
284 && (p as usize) % Align::ALIGN_BYTES == 0;
285
286 if is_out_aligned {
287 Arch::store_aligned(p, val);
288 } else {
289 Arch::store_unaligned(p, val);
290 }
291 };
292
293 for _ in 0..(simd_len / lane_count) {
294 let v1 = load(ptr1);
295 let v2 = load(ptr2);
296 let res = Arch::mul(v1, v2);
297 store(ptr_out, res);
298
299 ptr1 = ptr1.add(lane_count);
300 ptr2 = ptr2.add(lane_count);
301 ptr_out = ptr_out.add(lane_count);
302 }
303 }
304
305 let s_slice = self.as_slice();
306 let o_slice = other.as_slice();
307 for i in simd_len..len {
308 out[i] = s_slice[i] * o_slice[i];
309 }
310
311 Ok(())
312 }
313
314 #[inline(always)]
323 pub fn zip_into<ORef, Op>(
324 &self,
325 other: &SimdView<'_, T, Arch, Align, Mode, ORef>,
326 out: &mut [T],
327 op: Op,
328 ) -> Result<(), SimdError>
329 where
330 ORef: 'a,
331 Op: ElementOp<T>,
332 {
333 super::check_lengths_equal(self.len(), other.len())?;
334 super::check_output_length(self.len(), out.len())?;
335
336 let len = self.len();
337 let lane_count = Arch::LANE_COUNT;
338 let simd_len = (len / lane_count) * lane_count;
339
340 if Arch::SUPPORTS_NT_STORE
348 && len.saturating_mul(core::mem::size_of::<T>()) >= NT_STORE_MIN_BYTES
349 {
350 return unsafe { self.zip_into_streaming(other, out, op, len, simd_len) };
353 }
354
355 let ptr_self = self.as_slice().as_ptr();
356 let ptr_other = other.as_slice().as_ptr();
357 let ptr_out = out.as_mut_ptr();
358
359 unsafe {
360 let load = |p| {
361 if crate::align::is_aligned_for_arch::<Arch, Align>() {
362 Arch::load_aligned(p)
363 } else {
364 Arch::load_unaligned(p)
365 }
366 };
367
368 let store = |p, val| {
369 let is_out_aligned = crate::align::is_aligned_for_arch::<Arch, Align>()
370 && (p as usize) % Align::ALIGN_BYTES == 0;
371 if is_out_aligned {
372 Arch::store_aligned(p, val);
373 } else {
374 Arch::store_unaligned(p, val);
375 }
376 };
377
378 for i in (0..simd_len).step_by(lane_count) {
379 let va = load(ptr_self.add(i));
380 let vb = load(ptr_other.add(i));
381 let vr = op.apply::<Arch>(va, vb);
382 store(ptr_out.add(i), vr);
383 }
384 }
385
386 let s_slice = self.as_slice();
387 let o_slice = other.as_slice();
388 for i in simd_len..len {
389 out[i] = op.apply_scalar(s_slice[i], o_slice[i]);
390 }
391
392 Ok(())
393 }
394
395 #[inline]
410 unsafe fn zip_into_streaming<ORef, Op>(
411 &self,
412 other: &SimdView<'_, T, Arch, Align, Mode, ORef>,
413 out: &mut [T],
414 op: Op,
415 len: usize,
416 _simd_len: usize,
417 ) -> Result<(), SimdError>
418 where
419 ORef: 'a,
420 Op: ElementOp<T>,
421 {
422 let lane_count = Arch::LANE_COUNT;
423 let s = self.as_slice();
424 let o = other.as_slice();
425 let ptr_self = s.as_ptr();
426 let ptr_other = o.as_ptr();
427 let ptr_out = out.as_mut_ptr();
428
429 let align_bytes = lane_count * core::mem::size_of::<T>();
434 let addr = ptr_out as usize;
435 let head = ((align_bytes - (addr % align_bytes)) % align_bytes) / core::mem::size_of::<T>();
436 let head = head.min(len);
437
438 for i in 0..head {
439 out[i] = op.apply_scalar(s[i], o[i]);
440 }
441
442 let mid_end = head + ((len - head) / lane_count) * lane_count;
443 let mut i = head;
444 while i < mid_end {
445 let va = Arch::load_unaligned(ptr_self.add(i));
448 let vb = Arch::load_unaligned(ptr_other.add(i));
449 let vr = op.apply::<Arch>(va, vb);
450 Arch::store_streaming(ptr_out.add(i), vr);
451 i += lane_count;
452 }
453
454 Arch::stream_write_barrier();
455
456 for i in mid_end..len {
457 out[i] = op.apply_scalar(s[i], o[i]);
458 }
459
460 Ok(())
461 }
462
463 pub fn zip_transform<ORef, Op>(
471 &self,
472 other: &SimdView<'_, T, Arch, Align, Mode, ORef>,
473 op: Op,
474 ) -> Result<crate::vec::AlignedVec<T, Align>, SimdError>
475 where
476 ORef: 'a,
477 Op: ElementOp<T>,
478 {
479 super::check_lengths_equal(self.len(), other.len())?;
480 let len = self.len();
481 let mut out = crate::vec::AlignedVec::with_capacity(len);
482 unsafe {
484 out.set_len(len);
485 }
486 self.zip_into(other, out.as_mut_slice(), op)?;
487 Ok(out)
488 }
489}