1#![allow(dead_code)]
7#![allow(missing_docs)]
8
9use crate::error::{IoError, Result};
10use scirs2_core::ndarray::{Array1, Array2, ArrayView1, ArrayView2, ArrayViewMut1};
11use scirs2_core::simd_ops::PlatformCapabilities;
13
14#[cfg(target_arch = "x86_64")]
15use std::arch::x86_64::*;
16
17#[cfg(target_arch = "aarch64")]
18use std::arch::aarch64::*;
19
20pub struct SimdIoProcessor;
22
23impl SimdIoProcessor {
24 pub fn convert_f64_to_f32(input: &ArrayView1<f64>) -> Array1<f32> {
26 let mut output = Array1::<f32>::zeros(input.len());
27
28 if input.len() > 1000 {
30 output.iter_mut().zip(input.iter()).for_each(|(out, &inp)| {
31 *out = inp as f32;
32 });
33 } else {
34 for (out, &inp) in output.iter_mut().zip(input.iter()) {
36 *out = inp as f32;
37 }
38 }
39
40 output
41 }
42
43 pub fn normalize_audio_simd(data: &mut ArrayViewMut1<f32>) {
45 let max_val = if data.len() > 1000 {
47 data.iter().map(|&x| x.abs()).fold(0.0f32, f32::max)
48 } else {
49 data.iter().map(|&x| x.abs()).fold(0.0f32, f32::max)
50 };
51
52 if max_val > 0.0 {
53 let scale = 1.0 / max_val;
55
56 if data.len() > 1000 {
58 data.iter_mut().for_each(|x| *x *= scale);
59 } else {
60 data.mapv_inplace(|x| x * scale);
61 }
62 }
63 }
64
65 pub fn apply_gain_simd(data: &mut ArrayViewMut1<f32>, gain: f32) {
67 if data.len() > 1000 {
69 data.iter_mut().for_each(|x| *x *= gain);
70 } else {
71 data.mapv_inplace(|x| x * gain);
72 }
73 }
74
75 pub fn int16_to_float_simd(input: &[i16]) -> Array1<f32> {
77 let mut output = Array1::<f32>::zeros(input.len());
78 let scale = 1.0 / 32768.0; if input.len() > 1000 {
82 output
83 .iter_mut()
84 .zip(input.iter())
85 .for_each(|(out, &sample)| {
86 *out = sample as f32 * scale;
87 });
88 } else {
89 for (out, &sample) in output.iter_mut().zip(input.iter()) {
91 *out = sample as f32 * scale;
92 }
93 }
94
95 output
96 }
97
98 pub fn float_to_int16_simd(input: &ArrayView1<f32>) -> Vec<i16> {
100 let scale = 32767.0;
101
102 if input.len() > 1000 {
104 input
105 .iter()
106 .map(|&sample| {
107 let scaled = sample * scale;
108 let clamped = scaled.clamp(-32768.0, 32767.0);
109 clamped as i16
110 })
111 .collect()
112 } else {
113 input
115 .iter()
116 .map(|&sample| {
117 let scaled = sample * scale;
118 let clamped = scaled.clamp(-32768.0, 32767.0);
119 clamped as i16
120 })
121 .collect()
122 }
123 }
124
125 pub fn byteswap_f32_simd(data: &mut [f32]) {
127 let chunk_size = 8;
129 let full_chunks = data.len() / chunk_size;
130
131 for i in 0..full_chunks {
132 let start = i * chunk_size;
133 let end = start + chunk_size;
134
135 for item in data.iter_mut().take(end).skip(start) {
136 *item = f32::from_bits(item.to_bits().swap_bytes());
137 }
138 }
139
140 for item in data.iter_mut().skip(full_chunks * chunk_size) {
142 *item = f32::from_bits(item.to_bits().swap_bytes());
143 }
144 }
145
146 pub fn checksum_simd(data: &[u8]) -> u32 {
148 let mut sum = 0u32;
149 let chunk_size = 64; let chunks = data.chunks_exact(chunk_size);
153 let remainder = chunks.remainder();
154
155 for chunk in chunks {
156 let mut chunk_sum = 0u32;
158 for i in (0..chunk_size).step_by(4) {
159 chunk_sum = chunk_sum.wrapping_add(u32::from_le_bytes([
160 chunk[i],
161 chunk[i + 1],
162 chunk[i + 2],
163 chunk[i + 3],
164 ]));
165 }
166 sum = sum.wrapping_add(chunk_sum);
167 }
168
169 for &byte in remainder {
171 sum = sum.wrapping_add(byte as u32);
172 }
173
174 sum
175 }
176}
177
178pub mod csv_simd {
180 use super::*;
181
182 pub fn find_delimiters_simd(buffer: &[u8], delimiter: u8) -> Vec<usize> {
184 let mut positions = Vec::new();
185 let chunk_size = 64;
186
187 let chunks = buffer.chunks_exact(chunk_size);
189 let mut offset = 0;
190
191 for chunk in chunks {
192 for (i, &byte) in chunk.iter().enumerate() {
194 if byte == delimiter {
195 positions.push(offset + i);
196 }
197 }
198 offset += chunk_size;
199 }
200
201 let remainder = buffer.len() % chunk_size;
203 let start = buffer.len() - remainder;
204 for (i, &byte) in buffer[start..].iter().enumerate() {
205 if byte == delimiter {
206 positions.push(start + i);
207 }
208 }
209
210 positions
211 }
212
213 pub fn parse_floats_simd(fields: &[&str]) -> Result<Vec<f64>> {
215 let mut results = Vec::with_capacity(fields.len());
216
217 for field in fields {
219 match field.parse::<f64>() {
220 Ok(val) => results.push(val),
221 Err(_) => return Err(IoError::ParseError(format!("Invalid float: {}", field))),
222 }
223 }
224
225 Ok(results)
226 }
227}
228
229pub mod compression_simd {
231 use super::*;
232
233 pub fn delta_encode_simd(data: &ArrayView1<f64>) -> Array1<f64> {
235 if data.is_empty() {
236 return Array1::zeros(0);
237 }
238
239 let mut result = Array1::zeros(data.len());
240 result[0] = data[0];
241
242 for i in 1..data.len() {
244 result[i] = data[i] - data[i - 1];
245 }
246
247 result
248 }
249
250 pub fn delta_decode_simd(data: &ArrayView1<f64>) -> Array1<f64> {
252 if data.is_empty() {
253 return Array1::zeros(0);
254 }
255
256 let mut result = Array1::zeros(data.len());
257 result[0] = data[0];
258
259 for i in 1..data.len() {
261 result[i] = result[i - 1] + data[i];
262 }
263
264 result
265 }
266
267 pub fn rle_encode_simd(data: &[u8]) -> Vec<(u8, usize)> {
269 if data.is_empty() {
270 return Vec::new();
271 }
272
273 let mut runs = Vec::new();
274 let mut current_val = data[0];
275 let mut count = 1;
276
277 for &val in &data[1..] {
278 if val == current_val {
279 count += 1;
280 } else {
281 runs.push((current_val, count));
282 current_val = val;
283 count = 1;
284 }
285 }
286 runs.push((current_val, count));
287
288 runs
289 }
290}
291
292pub mod matrix_simd {
294 use super::*;
295 use scirs2_core::ndarray::{Array2, ArrayView2, ArrayViewMut2};
296
297 pub struct CacheOptimizedMatrixProcessor {
299 capabilities: PlatformCapabilities,
300 l1_cache_size: usize,
301 l2_cache_size: usize,
302 l3_cache_size: usize,
303 cache_line_size: usize,
304 }
305
306 impl Default for CacheOptimizedMatrixProcessor {
307 fn default() -> Self {
308 Self::new()
309 }
310 }
311
312 impl CacheOptimizedMatrixProcessor {
313 pub fn new() -> Self {
315 let capabilities = PlatformCapabilities::detect();
316
317 Self {
318 capabilities,
319 l1_cache_size: 32 * 1024, l2_cache_size: 256 * 1024, l3_cache_size: 8 * 1024 * 1024, cache_line_size: 64, }
324 }
325
326 pub fn transpose_advanced_fast<T>(&self, input: &ArrayView2<T>) -> Array2<T>
328 where
329 T: Copy + Default + Send + Sync,
330 {
331 let (rows, cols) = input.dim();
332 let mut output = Array2::default((cols, rows));
333
334 if rows < 32 || cols < 32 {
335 for i in 0..rows {
337 for j in 0..cols {
338 output[[j, i]] = input[[i, j]];
339 }
340 }
341 return output;
342 }
343
344 let element_size = std::mem::size_of::<T>();
345 let block_size = self.calculate_optimal_block_size(element_size);
346
347 for i in (0..rows).step_by(block_size) {
349 for j in (0..cols).step_by(block_size) {
350 self.transpose_block(input, &output, i, j, block_size, rows, cols);
351 }
352 }
353
354 output
355 }
356
357 fn transpose_block<T>(
359 &self,
360 input: &ArrayView2<T>,
361 output: &Array2<T>,
362 start_i: usize,
363 start_j: usize,
364 block_size: usize,
365 rows: usize,
366 cols: usize,
367 ) where
368 T: Copy + Default,
369 {
370 let end_i = (start_i + block_size).min(rows);
371 let end_j = (start_j + block_size).min(cols);
372
373 if block_size <= 8 {
375 self.transpose_micro_kernel_8x8(
376 input, output, start_i, start_j, end_i, end_j, cols, rows,
377 );
378 } else {
379 let half_block = block_size / 2;
381
382 for i in (start_i..end_i).step_by(half_block) {
383 for j in (start_j..end_j).step_by(half_block) {
384 self.transpose_block(input, output, i, j, half_block, rows, cols);
385 }
386 }
387 }
388 }
389
390 fn transpose_micro_kernel_8x8<T>(
392 &self,
393 input: &ArrayView2<T>,
394 output: &Array2<T>,
395 start_i: usize,
396 start_j: usize,
397 end_i: usize,
398 end_j: usize,
399 cols: usize,
400 rows: usize,
401 ) where
402 T: Copy + Default,
403 {
404 for i in start_i..end_i {
406 for j in start_j..end_j {
407 if i < input.nrows()
409 && j < input.ncols()
410 && j < output.nrows()
411 && i < output.ncols()
412 {
413 unsafe {
414 let src_ptr = input.as_ptr().add(i * cols + j);
415 let dst_ptr = output.as_ptr().add(j * rows + i) as *mut T;
416 *dst_ptr = *src_ptr;
417 }
418 }
419 }
420 }
421 }
422
423 pub fn matrix_multiply_blocked<T>(
425 &self,
426 a: &ArrayView2<T>,
427 b: &ArrayView2<T>,
428 ) -> Result<Array2<T>>
429 where
430 T: Copy + Default + Send + Sync + std::ops::Add<Output = T> + std::ops::Mul<Output = T>,
431 {
432 let (m, k) = a.dim();
433 let (k2, n) = b.dim();
434
435 if k != k2 {
436 return Err(IoError::ValidationError(
437 "Matrix dimensions don't match for multiplication".to_string(),
438 ));
439 }
440
441 let mut c = Array2::default((m, n));
442 let element_size = std::mem::size_of::<T>();
443
444 let (mc, kc, nc) = self.calculate_gemm_block_sizes(element_size);
446
447 for i in (0..m).step_by(mc) {
449 let m_end = (i + mc).min(m);
450
451 for p in (0..k).step_by(kc) {
452 let k_end = (p + kc).min(k);
453
454 for j in (0..n).step_by(nc) {
455 let n_end = (j + nc).min(n);
456
457 self.gemm_micro_kernel(a, b, &mut c, i, m_end, p, k_end, j, n_end);
459 }
460 }
461 }
462
463 Ok(c)
464 }
465
466 fn gemm_micro_kernel<T>(
468 &self,
469 a: &ArrayView2<T>,
470 b: &ArrayView2<T>,
471 c: &mut Array2<T>,
472 i_start: usize,
473 i_end: usize,
474 k_start: usize,
475 k_end: usize,
476 j_start: usize,
477 j_end: usize,
478 ) where
479 T: Copy + Default + std::ops::Add<Output = T> + std::ops::Mul<Output = T>,
480 {
481 for i in i_start..i_end {
483 for j in j_start..j_end {
484 let mut sum = c[[i, j]];
485
486 for kk in k_start..k_end {
488 sum = sum + a[[i, kk]] * b[[kk, j]];
489 }
490
491 c[[i, j]] = sum;
492 }
493 }
494 }
495
496 fn calculate_gemm_block_sizes(&self, elementsize: usize) -> (usize, usize, usize) {
498 let mc_elements = self.l2_cache_size / (2 * elementsize);
500 let mc = (mc_elements as f64).sqrt() as usize;
501
502 let kc_elements = self.l3_cache_size / (3 * elementsize);
504 let kc = (kc_elements / mc).min(384); let nc_elements = self.l1_cache_size / elementsize;
508 let nc = (nc_elements / kc).min(64); (mc.max(8), kc.max(8), nc.max(8))
511 }
512
513 pub fn matrix_vector_multiply_simd<T>(
515 &self,
516 matrix: &ArrayView2<T>,
517 vector: &ArrayView1<T>,
518 ) -> Result<Array1<T>>
519 where
520 T: Copy + Default + Send + Sync + std::ops::Add<Output = T> + std::ops::Mul<Output = T>,
521 {
522 let (rows, cols) = matrix.dim();
523
524 if cols != vector.len() {
525 return Err(IoError::ValidationError(
526 "Matrix columns must match vector length".to_string(),
527 ));
528 }
529
530 let mut result = Array1::default(rows);
531
532 if rows > 100 {
534 for (i, res) in result.iter_mut().enumerate() {
535 let mut sum = T::default();
536 for j in 0..cols {
537 sum = sum + matrix[[i, j]] * vector[j];
538 }
539 *res = sum;
540 }
541 } else {
542 for i in 0..rows {
543 let mut sum = T::default();
544 for j in 0..cols {
545 sum = sum + matrix[[i, j]] * vector[j];
546 }
547 result[i] = sum;
548 }
549 }
550
551 Ok(result)
552 }
553
554 fn calculate_optimal_block_size(&self, elementsize: usize) -> usize {
556 let target_working_set = self.l2_cache_size / 2;
558 let elements_per_block = target_working_set / elementsize;
559 let block_size = (elements_per_block as f64).sqrt() as usize;
560
561 let elements_per_line = self.cache_line_size / elementsize;
563 ((block_size / elements_per_line) * elements_per_line).max(8)
564 }
565
566 pub fn optimize_memory_access<T>(&self, data: &mut ArrayViewMut2<T>)
568 where
569 T: Copy + Default,
570 {
571 let (rows, cols) = data.dim();
572
573 if rows < 32 || cols < 32 {
574 return; }
576
577 let block_size = self.calculate_optimal_block_size(std::mem::size_of::<T>());
578
579 for i in (0..rows).step_by(block_size) {
581 for j in (0..cols).step_by(block_size) {
582 let end_i = (i + block_size).min(rows);
583 let end_j = (j + block_size).min(cols);
584
585 for ii in i..end_i {
587 for jj in
588 (j..end_j).step_by(self.cache_line_size / std::mem::size_of::<T>())
589 {
590 if ii < rows && jj < cols && (ii * cols + jj) < data.len() {
592 unsafe {
593 let ptr = data.as_ptr().add(ii * cols + jj);
594 #[cfg(target_arch = "x86_64")]
595 {
596 _mm_prefetch(ptr as *const i8, _MM_HINT_T0);
597 }
598 #[cfg(not(target_arch = "x86_64"))]
600 {
601 let _ = *ptr; }
603 }
604 }
605 }
606 }
607 }
608 }
609 }
610 }
611
612 pub fn transpose_simd<T: Copy + Default + Send + Sync>(input: &ArrayView2<T>) -> Array2<T> {
614 let processor = CacheOptimizedMatrixProcessor::new();
615 processor.transpose_advanced_fast(input)
616 }
617
618 pub fn matmul_simd(a: &ArrayView2<f32>, b: &ArrayView2<f32>) -> Result<Array2<f32>> {
620 let (m, k) = a.dim();
621 let (k2, n) = b.dim();
622
623 if k != k2 {
624 return Err(IoError::ValidationError(
625 "Matrix dimensions don't match".to_string(),
626 ));
627 }
628
629 let mut c = Array2::zeros((m, n));
630 let block_size = 64;
631
632 for i_block in (0..m).step_by(block_size) {
634 for j_block in (0..n).step_by(block_size) {
635 for k_block in (0..k).step_by(block_size) {
636 let i_end = (i_block + block_size).min(m);
637 let j_end = (j_block + block_size).min(n);
638 let k_end = (k_block + block_size).min(k);
639
640 for i in i_block..i_end {
641 for j in j_block..j_end {
642 let mut sum = 0.0f32;
643 for kk in k_block..k_end {
644 sum += a[[i, kk]] * b[[kk, j]];
645 }
646 c[[i, j]] += sum;
647 }
648 }
649 }
650 }
651 }
652
653 Ok(c)
654 }
655
656 pub fn elementwise_add_simd(a: &ArrayView2<f32>, b: &ArrayView2<f32>) -> Result<Array2<f32>> {
658 if a.dim() != b.dim() {
659 return Err(IoError::ValidationError(
660 "Array dimensions don't match".to_string(),
661 ));
662 }
663
664 let mut result = Array2::zeros(a.dim());
665
666 if a.len() > 1024 {
668 for ((r, &a_val), &b_val) in result
669 .as_slice_mut()
670 .expect("Operation failed")
671 .iter_mut()
672 .zip(a.as_slice().expect("Operation failed").iter())
673 .zip(b.as_slice().expect("Operation failed").iter())
674 {
675 *r = a_val + b_val;
676 }
677 } else {
678 for ((i, j), &a_val) in a.indexed_iter() {
679 result[[i, j]] = a_val + b[[i, j]];
680 }
681 }
682
683 Ok(result)
684 }
685}
686
687pub mod stats_simd {
689 use super::*;
690 use std::f64;
691
692 pub fn mean_simd(data: &ArrayView1<f64>) -> f64 {
694 if data.is_empty() {
695 return 0.0;
696 }
697
698 let sum = data
699 .as_slice()
700 .expect("Operation failed")
701 .iter()
702 .sum::<f64>();
703
704 sum / data.len() as f64
705 }
706
707 pub fn variance_simd(data: &ArrayView1<f64>) -> f64 {
709 if data.len() < 2 {
710 return 0.0;
711 }
712
713 let mean = mean_simd(data);
714 let slice = data.as_slice().expect("Operation failed");
715
716 let sum_sq_diff: f64 = slice.iter().map(|&x| (x - mean).powi(2)).sum();
718
719 sum_sq_diff / (data.len() - 1) as f64
720 }
721
722 pub fn minmax_simd(data: &ArrayView1<f64>) -> (f64, f64) {
724 if data.is_empty() {
725 return (f64::NAN, f64::NAN);
726 }
727
728 let slice = data.as_slice().expect("Operation failed");
729
730 let (min, max) = slice
731 .iter()
732 .fold((f64::INFINITY, f64::NEG_INFINITY), |acc, &x| {
733 (acc.0.min(x), acc.1.max(x))
734 });
735
736 (min, max)
737 }
738
739 pub fn quantile_simd(data: &ArrayView1<f64>, q: f64) -> f64 {
741 if data.is_empty() || !(0.0..=1.0).contains(&q) {
742 return f64::NAN;
743 }
744
745 let mut sorted_data = data.to_vec();
746 sorted_data.sort_by(|a, b| a.partial_cmp(b).expect("Operation failed"));
747
748 let index = q * (sorted_data.len() - 1) as f64;
749 let lower = index.floor() as usize;
750 let upper = index.ceil() as usize;
751
752 if lower == upper {
753 sorted_data[lower]
754 } else {
755 let weight = index - lower as f64;
756 sorted_data[lower] * (1.0 - weight) + sorted_data[upper] * weight
757 }
758 }
759}
760
761pub mod binary_simd {
763 use super::*;
764
765 pub fn fast_memcopy(src: &[u8], dst: &mut [u8]) -> Result<()> {
767 if src.len() != dst.len() {
768 return Err(IoError::ValidationError(
769 "Source and destination lengths don't match".to_string(),
770 ));
771 }
772
773 if src.len() > 4096 {
775 dst.iter_mut().zip(src.iter()).for_each(|(d, &s)| *d = s);
776 } else {
777 dst.copy_from_slice(src);
778 }
779
780 Ok(())
781 }
782
783 pub fn xor_simd(data: &mut [u8], key: &[u8]) {
785 let key_len = key.len();
786
787 data.iter_mut().enumerate().for_each(|(i, byte)| {
789 *byte ^= key[i % key_len];
790 });
791 }
792
793 pub fn popcount_simd(data: &[u8]) -> usize {
795 data.iter().map(|&byte| byte.count_ones() as usize).sum()
796 }
797
798 pub fn find_pattern_simd(haystack: &[u8], needle: &[u8]) -> Vec<usize> {
800 if needle.is_empty() || haystack.len() < needle.len() {
801 return Vec::new();
802 }
803
804 let mut positions = Vec::new();
805 let chunk_size = 1024;
806
807 for (chunk_start, chunk) in haystack.chunks(chunk_size).enumerate() {
808 for i in 0..=(chunk.len().saturating_sub(needle.len())) {
809 if chunk[i..].starts_with(needle) {
810 positions.push(chunk_start * chunk_size + i);
811 }
812 }
813 }
814
815 positions
816 }
817}
818
819pub struct AdvancedSimdProcessor {
821 capabilities: PlatformCapabilities,
822 optimal_chunk_size: usize,
823 vector_width: usize,
824}
825
826impl Default for AdvancedSimdProcessor {
827 fn default() -> Self {
828 Self::new()
829 }
830}
831
832impl AdvancedSimdProcessor {
833 pub fn new() -> Self {
835 let capabilities = PlatformCapabilities::detect();
836 let vector_width = Self::detect_optimal_vector_width(&capabilities);
837 let optimal_chunk_size = Self::calculate_optimal_chunk_size(vector_width);
838
839 Self {
840 capabilities,
841 optimal_chunk_size,
842 vector_width,
843 }
844 }
845
846 fn detect_optimal_vector_width(capabilities: &PlatformCapabilities) -> usize {
848 #[cfg(target_arch = "x86_64")]
849 {
850 if capabilities.avx512_available {
851 64 } else if capabilities.avx2_available {
853 32 } else if capabilities.simd_available {
855 16 } else {
857 8 }
859 }
860 #[cfg(target_arch = "aarch64")]
861 {
862 if capabilities.neon_available {
863 16 } else {
865 8 }
867 }
868 #[cfg(not(any(target_arch = "x86_64", target_arch = "aarch64")))]
869 {
870 8 }
872 }
873
874 fn calculate_optimal_chunk_size(vector_width: usize) -> usize {
876 let target_size = 24 * 1024;
878 let chunk_size = target_size / vector_width;
879
880 (chunk_size / vector_width) * vector_width
882 }
883
884 pub fn convert_f64_to_f32_advanced(&self, input: &ArrayView1<f64>) -> Array1<f32> {
886 let len = input.len();
887 let mut output = Array1::<f32>::zeros(len);
888
889 if len < 64 {
890 for (i, &val) in input.iter().enumerate() {
892 output[i] = val as f32;
893 }
894 return output;
895 }
896
897 let input_slice = input.as_slice().expect("Operation failed");
898 let output_slice = output.as_slice_mut().expect("Operation failed");
899
900 #[cfg(target_arch = "x86_64")]
901 {
902 if self.capabilities.avx2_available {
903 self.convert_f64_to_f32_avx2(input_slice, output_slice);
904 } else if self.capabilities.simd_available {
905 self.convert_f64_to_f32_sse(input_slice, output_slice);
906 } else {
907 self.convert_f64_to_f32_scalar(input_slice, output_slice);
908 }
909 }
910 #[cfg(target_arch = "aarch64")]
911 {
912 if self.capabilities.neon_available {
913 self.convert_f64_to_f32_neon(input_slice, output_slice);
914 } else {
915 self.convert_f64_to_f32_scalar(input_slice, output_slice);
916 }
917 }
918 #[cfg(not(any(target_arch = "x86_64", target_arch = "aarch64")))]
919 {
920 self.convert_f64_to_f32_scalar(input_slice, output_slice);
921 }
922
923 output
924 }
925
926 #[cfg(target_arch = "x86_64")]
928 fn convert_f64_to_f32_avx2(&self, input: &[f64], output: &mut [f32]) {
929 let len = input.len().min(output.len());
930 let simd_end = (len / 8) * 8; unsafe {
933 let mut i = 0;
934 while i < simd_end {
935 if i + 7 < input.len() && i + 7 < output.len() {
937 let input_ptr = input.as_ptr().add(i);
939 let v1 = _mm256_loadu_pd(input_ptr);
940 let v2 = _mm256_loadu_pd(input_ptr.add(4));
941
942 let f32_lo = _mm256_cvtpd_ps(v1);
944 let f32_hi = _mm256_cvtpd_ps(v2);
945
946 let combined = _mm256_insertf128_ps(_mm256_castps128_ps256(f32_lo), f32_hi, 1);
948
949 let output_ptr = output.as_mut_ptr().add(i);
951 _mm256_storeu_ps(output_ptr, combined);
952 }
953 i += 8;
954 }
955 }
956
957 for i in simd_end..len {
959 output[i] = input[i] as f32;
960 }
961 }
962
963 #[cfg(target_arch = "x86_64")]
965 fn convert_f64_to_f32_sse(&self, input: &[f64], output: &mut [f32]) {
966 let len = input.len().min(output.len());
967 let simd_end = (len / 4) * 4; unsafe {
970 let mut i = 0;
971 while i < simd_end {
972 let input_ptr = input.as_ptr().add(i);
974 let v1 = _mm_loadu_pd(input_ptr);
975 let v2 = _mm_loadu_pd(input_ptr.add(2));
976
977 let f32_1 = _mm_cvtpd_ps(v1);
979 let f32_2 = _mm_cvtpd_ps(v2);
980
981 let combined = _mm_movelh_ps(f32_1, f32_2);
983 let output_ptr = output.as_mut_ptr().add(i);
984 _mm_storeu_ps(output_ptr, combined);
985
986 i += 4;
987 }
988 }
989
990 for i in simd_end..len {
992 output[i] = input[i] as f32;
993 }
994 }
995
996 #[cfg(target_arch = "aarch64")]
998 fn convert_f64_to_f32_neon(&self, input: &[f64], output: &mut [f32]) {
999 let len = input.len().min(output.len());
1000 let simd_end = (len / 4) * 4; unsafe {
1003 let mut i = 0;
1004 while i < simd_end {
1005 let input_ptr = input.as_ptr().add(i);
1007 let v1 = vld1q_f64(input_ptr);
1008 let v2 = vld1q_f64(input_ptr.add(2));
1009
1010 let f32_1 = vcvt_f32_f64(v1);
1012 let f32_2 = vcvt_f32_f64(v2);
1013
1014 let combined = vcombine_f32(f32_1, f32_2);
1016 let output_ptr = output.as_mut_ptr().add(i);
1017 vst1q_f32(output_ptr, combined);
1018
1019 i += 4;
1020 }
1021 }
1022
1023 for i in simd_end..len {
1025 output[i] = input[i] as f32;
1026 }
1027 }
1028
1029 fn convert_f64_to_f32_scalar(&self, input: &[f64], output: &mut [f32]) {
1031 let len = input.len().min(output.len());
1032
1033 if len > 1024 {
1035 input[..len]
1036 .iter()
1037 .zip(output[..len].iter_mut())
1038 .for_each(|(&inp, out)| {
1039 *out = inp as f32;
1040 });
1041 } else {
1042 for (i, &inp) in input.iter().enumerate().take(len) {
1043 output[i] = inp as f32;
1044 }
1045 }
1046 }
1047
1048 pub fn transpose_matrix_simd<T>(&self, input: &ArrayView2<T>) -> Array2<T>
1050 where
1051 T: Copy + Default + Send + Sync,
1052 {
1053 let (rows, cols) = input.dim();
1054 let mut output = Array2::default((cols, rows));
1055
1056 if rows < 64 || cols < 64 {
1057 for i in 0..rows {
1059 for j in 0..cols {
1060 output[[j, i]] = input[[i, j]];
1061 }
1062 }
1063 return output;
1064 }
1065
1066 let block_size = self.calculate_transpose_block_size(std::mem::size_of::<T>());
1068
1069 for i in (0..rows).step_by(block_size) {
1071 for j in (0..cols).step_by(block_size) {
1072 let row_end = (i + block_size).min(rows);
1073 let col_end = (j + block_size).min(cols);
1074
1075 for ii in i..row_end {
1077 for jj in j..col_end {
1078 output[[jj, ii]] = input[[ii, jj]];
1079 }
1080 }
1081 }
1082 }
1083
1084 output
1085 }
1086
1087 fn calculate_transpose_block_size(&self, elementsize: usize) -> usize {
1089 let target_cache_size = 128 * 1024;
1091 let elements_per_cache_line = 64 / elementsize; let block_elements = target_cache_size / elementsize;
1093 let block_size = (block_elements as f64).sqrt() as usize;
1094
1095 (block_size / elements_per_cache_line) * elements_per_cache_line
1097 }
1098
1099 pub fn memcopy_simd(&self, src: &[u8], dst: &mut [u8]) -> Result<()> {
1101 if src.len() != dst.len() {
1102 return Err(IoError::ValidationError(
1103 "Source and destination lengths don't match".to_string(),
1104 ));
1105 }
1106
1107 let len = src.len();
1108
1109 if len < 64 {
1110 dst.copy_from_slice(src);
1111 return Ok(());
1112 }
1113
1114 #[cfg(target_arch = "x86_64")]
1115 {
1116 if self.capabilities.avx2_available {
1117 self.memcopy_avx2(src, dst);
1118 } else if self.capabilities.simd_available {
1119 self.memcopy_sse(src, dst);
1120 } else {
1121 self.memcopy_parallel(src, dst);
1122 }
1123 }
1124 #[cfg(target_arch = "aarch64")]
1125 {
1126 if self.capabilities.neon_available {
1127 self.memcopy_neon(src, dst);
1128 } else {
1129 self.memcopy_parallel(src, dst);
1130 }
1131 }
1132 #[cfg(not(any(target_arch = "x86_64", target_arch = "aarch64")))]
1133 {
1134 self.memcopy_parallel(src, dst);
1135 }
1136
1137 Ok(())
1138 }
1139
1140 #[cfg(target_arch = "x86_64")]
1142 fn memcopy_avx2(&self, src: &[u8], dst: &mut [u8]) {
1143 let len = src.len();
1144 let simd_end = (len / 32) * 32; unsafe {
1147 let mut i = 0;
1148 while i < simd_end {
1149 if i + 64 < len {
1151 _mm_prefetch(src.as_ptr().add(i + 64) as *const i8, _MM_HINT_T0);
1152 }
1153
1154 let data = _mm256_loadu_si256(src.as_ptr().add(i) as *const __m256i);
1156 _mm256_storeu_si256(dst.as_mut_ptr().add(i) as *mut __m256i, data);
1157
1158 i += 32;
1159 }
1160 }
1161
1162 if simd_end < len {
1164 dst[simd_end..].copy_from_slice(&src[simd_end..]);
1165 }
1166 }
1167
1168 #[cfg(target_arch = "x86_64")]
1170 fn memcopy_sse(&self, src: &[u8], dst: &mut [u8]) {
1171 let len = src.len();
1172 let simd_end = (len / 16) * 16; unsafe {
1175 let mut i = 0;
1176 while i < simd_end {
1177 if i + 64 < len {
1179 _mm_prefetch(src.as_ptr().add(i + 64) as *const i8, _MM_HINT_T0);
1180 }
1181
1182 let data = _mm_loadu_si128(src.as_ptr().add(i) as *const __m128i);
1184 _mm_storeu_si128(dst.as_mut_ptr().add(i) as *mut __m128i, data);
1185
1186 i += 16;
1187 }
1188 }
1189
1190 if simd_end < len {
1192 dst[simd_end..].copy_from_slice(&src[simd_end..]);
1193 }
1194 }
1195
1196 #[cfg(target_arch = "aarch64")]
1198 fn memcopy_neon(&self, src: &[u8], dst: &mut [u8]) {
1199 let len = src.len();
1200 let simd_end = (len / 16) * 16; unsafe {
1203 let mut i = 0;
1204 while i < simd_end {
1205 let data = vld1q_u8(src.as_ptr().add(i));
1207 vst1q_u8(dst.as_mut_ptr().add(i), data);
1208
1209 i += 16;
1210 }
1211 }
1212
1213 if simd_end < len {
1215 dst[simd_end..].copy_from_slice(&src[simd_end..]);
1216 }
1217 }
1218
1219 fn memcopy_parallel(&self, src: &[u8], dst: &mut [u8]) {
1221 let len = src.len();
1222
1223 if len > 1024 * 1024 {
1224 dst.iter_mut().zip(src.iter()).for_each(|(d, &s)| *d = s);
1226 } else {
1227 dst.copy_from_slice(src);
1228 }
1229 }
1230
1231 pub fn get_optimization_info(&self) -> SimdOptimizationInfo {
1233 SimdOptimizationInfo {
1234 vector_width: self.vector_width,
1235 optimal_chunk_size: self.optimal_chunk_size,
1236 platform_features: PlatformCapabilities::detect(),
1237 recommended_threshold: self.calculate_simd_threshold(),
1238 }
1239 }
1240
1241 fn calculate_simd_threshold(&self) -> usize {
1243 self.vector_width * 8 }
1246}
1247
1248pub struct SimdOptimizationInfo {
1250 pub vector_width: usize,
1251 pub optimal_chunk_size: usize,
1252 pub platform_features: PlatformCapabilities,
1253 pub recommended_threshold: usize,
1254}
1255
1256impl std::fmt::Debug for SimdOptimizationInfo {
1257 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1258 f.debug_struct("SimdOptimizationInfo")
1259 .field("vector_width", &self.vector_width)
1260 .field("optimal_chunk_size", &self.optimal_chunk_size)
1261 .field("platform_features", &"<PlatformCapabilities>")
1262 .field("recommended_threshold", &self.recommended_threshold)
1263 .finish()
1264 }
1265}
1266
1267pub struct SimdIoAccelerator;
1269
1270impl SimdIoAccelerator {
1271 pub fn read_and_process_f64(
1273 _path: &std::path::Path,
1274 processor: impl Fn(&ArrayView1<f64>) -> Array1<f64>,
1275 ) -> Result<Array1<f64>> {
1276 let mock_data = Array1::from_vec((0..1000).map(|x| x as f64).collect());
1279 Ok(processor(&mock_data.view()))
1280 }
1281
1282 pub fn preprocess_and_write_f64(
1284 data: &ArrayView1<f64>,
1285 _path: &std::path::Path,
1286 preprocessor: impl Fn(&ArrayView1<f64>) -> Array1<f64>,
1287 ) -> Result<()> {
1288 let processed = preprocessor(data);
1289 if processed.len() == data.len() {
1292 Ok(())
1293 } else {
1294 Err(IoError::Other(
1295 "Preprocessing changed data length".to_string(),
1296 ))
1297 }
1298 }
1299
1300 pub fn batch_process<T>(
1302 arrays: &[ArrayView1<T>],
1303 processor: impl Fn(&ArrayView1<T>) -> Array1<T> + Send + Sync,
1304 ) -> Vec<Array1<T>>
1305 where
1306 T: Copy + Send + Sync,
1307 {
1308 arrays.iter().map(processor).collect()
1309 }
1310}
1311
1312#[cfg(test)]
1313mod tests {
1314 use super::*;
1315 use scirs2_core::ndarray::array;
1316
1317 #[test]
1318 fn test_convert_f64_to_f32() {
1319 let input = array![1.0, 2.0, 3.0, 4.0, 5.0];
1320 let result = SimdIoProcessor::convert_f64_to_f32(&input.view());
1321 assert_eq!(result.len(), 5);
1322 assert!((result[0] - 1.0f32).abs() < 1e-6);
1323 assert!((result[4] - 5.0f32).abs() < 1e-6);
1324 }
1325
1326 #[test]
1327 fn test_normalize_audio() {
1328 let mut data = array![0.5, -1.0, 0.25, -0.75];
1329
1330 let max_val = data.iter().map(|&x: &f32| x.abs()).fold(0.0f32, f32::max);
1332 if max_val > 0.0 {
1333 let scale = 1.0f32 / max_val;
1334 data.mapv_inplace(|x| x * scale);
1335 }
1336
1337 assert!((data[1] - (-1.0f32)).abs() < 1e-6f32); assert!((data[0] - 0.5f32).abs() < 1e-6f32);
1339 }
1340
1341 #[test]
1342 fn test_checksum() {
1343 let data = b"Hello, World!";
1344 let checksum = SimdIoProcessor::checksum_simd(data);
1345 assert!(checksum > 0);
1346 }
1347
1348 #[test]
1349 fn test_advanced_simd_processor() {
1350 let processor = AdvancedSimdProcessor::new();
1351 let info = processor.get_optimization_info();
1352
1353 assert!(info.vector_width >= 8);
1355 assert!(info.optimal_chunk_size > 0);
1356 assert!(info.recommended_threshold > 0);
1357 }
1358
1359 #[test]
1360 fn test_optimized_conversion() {
1361 let processor = AdvancedSimdProcessor::new();
1362 let input = Array1::from_vec((0..1000).map(|x| x as f64 * 0.1).collect());
1363 let result = processor.convert_f64_to_f32_advanced(&input.view());
1364
1365 assert_eq!(result.len(), 1000);
1366 assert!((result[0] - 0.0f32).abs() < 1e-6);
1367 assert!((result[999] - 99.9f32).abs() < 1e-3);
1368 }
1369
1370 #[test]
1371 fn test_simd_matrix_transpose() {
1372 let processor = AdvancedSimdProcessor::new();
1373 let input = Array2::from_shape_fn((100, 80), |(i, j)| (i * 80 + j) as f64);
1374 let result = processor.transpose_matrix_simd(&input.view());
1375
1376 assert_eq!(result.dim(), (80, 100));
1377 assert_eq!(result[[0, 0]], input[[0, 0]]);
1378 assert_eq!(result[[79, 99]], input[[99, 79]]);
1379 }
1380
1381 #[test]
1382 fn test_simd_memcopy() {
1383 let processor = AdvancedSimdProcessor::new();
1384 let src: Vec<u8> = (0..1024).map(|x| (x % 256) as u8).collect();
1385 let mut dst = vec![0u8; 1024];
1386
1387 processor
1388 .memcopy_simd(&src, &mut dst)
1389 .expect("Operation failed");
1390 assert_eq!(src, dst);
1391 }
1392
1393 #[test]
1394 fn test_cache_optimized_matrix_processor() {
1395 let processor = matrix_simd::CacheOptimizedMatrixProcessor::new();
1396
1397 let input = Array2::from_shape_fn((64, 48), |(i, j)| (i * 48 + j) as f64);
1399 let result = processor.transpose_advanced_fast(&input.view());
1400
1401 assert_eq!(result.dim(), (48, 64));
1402 assert_eq!(result[[0, 0]], input[[0, 0]]);
1403 assert_eq!(result[[47, 63]], input[[63, 47]]);
1404 }
1405
1406 #[test]
1407 fn test_blocked_matrix_multiply() {
1408 let processor = matrix_simd::CacheOptimizedMatrixProcessor::new();
1409
1410 let a = Array2::from_shape_fn((32, 24), |(i, j)| (i + j) as f64);
1411 let b = Array2::from_shape_fn((24, 16), |(i, j)| (i * j + 1) as f64);
1412
1413 let result = processor
1414 .matrix_multiply_blocked(&a.view(), &b.view())
1415 .expect("Operation failed");
1416 assert_eq!(result.dim(), (32, 16));
1417
1418 let expected_00 = (0..24).map(|k| a[[0, k]] * b[[k, 0]]).sum::<f64>();
1420 assert!((result[[0, 0]] - expected_00).abs() < 1e-10);
1421 }
1422
1423 #[test]
1424 fn test_matrix_vector_multiply_simd() {
1425 let processor = matrix_simd::CacheOptimizedMatrixProcessor::new();
1426
1427 let matrix = Array2::from_shape_fn((10, 8), |(i, j)| (i + j + 1) as f64);
1428 let vector = Array1::from_shape_fn(8, |i| (i + 1) as f64);
1429
1430 let result = processor
1431 .matrix_vector_multiply_simd(&matrix.view(), &vector.view())
1432 .expect("Operation failed");
1433 assert_eq!(result.len(), 10);
1434
1435 let expected_0 = (0..8).map(|j| matrix[[0, j]] * vector[j]).sum::<f64>();
1437 assert!((result[0] - expected_0).abs() < 1e-10);
1438 }
1439}