1use crate::error::{LinalgError, LinalgResult};
12use scirs2_core::ndarray::{Array2, ArrayView2, ArrayViewMut2};
13#[cfg(target_arch = "x86_64")]
14use std::arch::x86_64::*;
15use std::sync::atomic::{AtomicU64, Ordering};
16use std::time::{Duration, Instant};
17
18#[derive(Debug)]
20pub struct MemoryAccessPatternAnalyzer {
21 sequential_access_count: AtomicU64,
23 random_access_count: AtomicU64,
25 stride_access_patterns: Vec<(usize, u64)>, #[allow(dead_code)]
29 predicted_miss_rate: f64,
30}
31
32impl MemoryAccessPatternAnalyzer {
33 pub fn new() -> Self {
34 Self {
35 sequential_access_count: AtomicU64::new(0),
36 random_access_count: AtomicU64::new(0),
37 stride_access_patterns: Vec::new(),
38 predicted_miss_rate: 0.05, }
40 }
41
42 pub fn analyze_and_recommend_prefetch(&self, matrixdims: (usize, usize)) -> PrefetchStrategy {
44 let (m, n) = matrixdims;
45 let total_elements = m * n;
46
47 if total_elements > 1_000_000 {
49 PrefetchStrategy::Aggressive {
50 prefetch_distance: 8,
51 prefetch_hint: PrefetchHint::T0, }
53 } else if total_elements > 100_000 {
54 PrefetchStrategy::Moderate {
55 prefetch_distance: 4,
56 prefetch_hint: PrefetchHint::T1, }
58 } else {
59 PrefetchStrategy::Conservative {
60 prefetch_distance: 2,
61 prefetch_hint: PrefetchHint::T2, }
63 }
64 }
65
66 pub fn record_access_pattern(&mut self, accesstype: AccessType) {
68 match accesstype {
69 AccessType::Sequential => {
70 self.sequential_access_count.fetch_add(1, Ordering::Relaxed);
71 }
72 AccessType::Random => {
73 self.random_access_count.fetch_add(1, Ordering::Relaxed);
74 }
75 AccessType::Strided(stride) => {
76 if let Some(pattern) = self
78 .stride_access_patterns
79 .iter_mut()
80 .find(|(s_, _)| *s_ == stride)
81 {
82 pattern.1 += 1;
83 } else {
84 self.stride_access_patterns.push((stride, 1));
85 }
86 }
87 }
88 }
89}
90
91#[derive(Debug, Clone, Copy)]
93pub enum AccessType {
94 Sequential,
95 Random,
96 Strided(usize),
97}
98
99#[derive(Debug, Clone, Copy)]
101pub enum PrefetchStrategy {
102 Conservative {
103 prefetch_distance: usize,
104 prefetch_hint: PrefetchHint,
105 },
106 Moderate {
107 prefetch_distance: usize,
108 prefetch_hint: PrefetchHint,
109 },
110 Aggressive {
111 prefetch_distance: usize,
112 prefetch_hint: PrefetchHint,
113 },
114}
115
116#[derive(Debug, Clone, Copy)]
118pub enum PrefetchHint {
119 T0, T1, T2, NTA, }
124
125pub struct CacheAwareMatrixOperations {
127 l1_cachesize: usize,
129 l2_cachesize: usize,
131 l3_cachesize: usize,
133 #[allow(dead_code)]
135 cache_linesize: usize,
136 pattern_analyzer: MemoryAccessPatternAnalyzer,
138}
139
140impl CacheAwareMatrixOperations {
141 pub fn new() -> Self {
142 Self {
143 l1_cachesize: 32 * 1024, l2_cachesize: 512 * 1024, l3_cachesize: 8 * 1024 * 1024, cache_linesize: 64, pattern_analyzer: MemoryAccessPatternAnalyzer::new(),
148 }
149 }
150
151 pub fn calculate_optimal_blocksizes(&self, elementsize: usize) -> CacheBlockSizes {
153 let l1_elements = (self.l1_cachesize / 3) / elementsize; let l1_blocksize = (l1_elements as f64).sqrt() as usize;
156
157 let l2_elements = (self.l2_cachesize / 3) / elementsize;
159 let l2_blocksize = (l2_elements as f64).sqrt() as usize;
160
161 let l3_elements = (self.l3_cachesize / 3) / elementsize;
163 let l3_blocksize = (l3_elements as f64).sqrt() as usize;
164
165 CacheBlockSizes {
166 l1_block_m: l1_blocksize.min(256),
167 l1_block_n: l1_blocksize.min(256),
168 l1_block_k: l1_blocksize.min(256),
169 l2_block_m: l2_blocksize.min(1024),
170 l2_block_n: l2_blocksize.min(1024),
171 l2_block_k: l2_blocksize.min(1024),
172 l3_block_m: l3_blocksize.min(4096),
173 l3_block_n: l3_blocksize.min(4096),
174 l3_block_k: l3_blocksize.min(4096),
175 }
176 }
177
178 pub fn cache_aware_gemm_f32(
180 &mut self,
181 a: &ArrayView2<f32>,
182 b: &ArrayView2<f32>,
183 c: &mut ArrayViewMut2<f32>,
184 ) -> LinalgResult<()> {
185 let (m, k) = a.dim();
186 let (_, n) = b.dim();
187
188 if k != b.nrows() || m != c.nrows() || n != c.ncols() {
189 return Err(LinalgError::ShapeError(
190 "Matrix dimensions incompatible for multiplication".to_string(),
191 ));
192 }
193
194 let blocksizes = self.calculate_optimal_blocksizes(std::mem::size_of::<f32>());
195 let prefetch_strategy = self.pattern_analyzer.analyze_and_recommend_prefetch((m, n));
196
197 self.three_level_blocked_gemm(a, b, c, &blocksizes, &prefetch_strategy)?;
199
200 Ok(())
201 }
202
203 fn three_level_blocked_gemm(
205 &mut self,
206 a: &ArrayView2<f32>,
207 b: &ArrayView2<f32>,
208 c: &mut ArrayViewMut2<f32>,
209 blocksizes: &CacheBlockSizes,
210 prefetch_strategy: &PrefetchStrategy,
211 ) -> LinalgResult<()> {
212 let (m, k) = a.dim();
213 let (_, n) = b.dim();
214
215 for ii in (0..m).step_by(blocksizes.l3_block_m) {
217 for jj in (0..n).step_by(blocksizes.l3_block_n) {
218 for kk in (0..k).step_by(blocksizes.l3_block_k) {
219 let i_end = (ii + blocksizes.l3_block_m).min(m);
220 let j_end = (jj + blocksizes.l3_block_n).min(n);
221 let k_end = (kk + blocksizes.l3_block_k).min(k);
222
223 for i2 in (ii..i_end).step_by(blocksizes.l2_block_m) {
225 for j2 in (jj..j_end).step_by(blocksizes.l2_block_n) {
226 for k2 in (kk..k_end).step_by(blocksizes.l2_block_k) {
227 let i2_end = (i2 + blocksizes.l2_block_m).min(i_end);
228 let j2_end = (j2 + blocksizes.l2_block_n).min(j_end);
229 let k2_end = (k2 + blocksizes.l2_block_k).min(k_end);
230
231 self.l1_blocked_gemm_with_prefetch(
233 a,
234 b,
235 c,
236 i2,
237 i2_end,
238 j2,
239 j2_end,
240 k2,
241 k2_end,
242 blocksizes,
243 prefetch_strategy,
244 )?;
245 }
246 }
247 }
248 }
249 }
250 }
251
252 Ok(())
253 }
254
255 fn l1_blocked_gemm_with_prefetch(
257 &mut self,
258 a: &ArrayView2<f32>,
259 b: &ArrayView2<f32>,
260 c: &mut ArrayViewMut2<f32>,
261 i_start: usize,
262 i_end: usize,
263 j_start: usize,
264 j_end: usize,
265 k_start: usize,
266 k_end: usize,
267 blocksizes: &CacheBlockSizes,
268 prefetch_strategy: &PrefetchStrategy,
269 ) -> LinalgResult<()> {
270 for i in (i_start..i_end).step_by(blocksizes.l1_block_m) {
271 for j in (j_start..j_end).step_by(blocksizes.l1_block_n) {
272 for k_iter in (k_start..k_end).step_by(blocksizes.l1_block_k) {
273 let i_block_end = (i + blocksizes.l1_block_m).min(i_end);
274 let j_block_end = (j + blocksizes.l1_block_n).min(j_end);
275 let k_block_end = (k_iter + blocksizes.l1_block_k).min(k_end);
276
277 self.intelligent_prefetch(a, b, c, i, j, k_iter, prefetch_strategy);
279
280 for ii in i..i_block_end {
282 for jj in j..j_block_end {
283 let mut sum = 0.0f32;
284
285 for kk in k_iter..k_block_end {
287 sum += a[[ii, kk]] * b[[kk, jj]];
288 }
289
290 c[[ii, jj]] += sum;
291 }
292 }
293 }
294 }
295 }
296
297 self.pattern_analyzer
299 .record_access_pattern(AccessType::Sequential);
300
301 Ok(())
302 }
303
304 fn intelligent_prefetch(
306 &self,
307 a: &ArrayView2<f32>,
308 b: &ArrayView2<f32>,
309 c: &ArrayViewMut2<f32>,
310 i: usize,
311 j: usize,
312 k: usize,
313 strategy: &PrefetchStrategy,
314 ) {
315 let (prefetch_distance, hint) = match strategy {
316 PrefetchStrategy::Conservative {
317 prefetch_distance,
318 prefetch_hint,
319 } => (*prefetch_distance, *prefetch_hint),
320 PrefetchStrategy::Moderate {
321 prefetch_distance,
322 prefetch_hint,
323 } => (*prefetch_distance, *prefetch_hint),
324 PrefetchStrategy::Aggressive {
325 prefetch_distance,
326 prefetch_hint,
327 } => (*prefetch_distance, *prefetch_hint),
328 };
329
330 #[cfg(target_arch = "x86_64")]
331 unsafe {
332 macro_rules! prefetch_with_hint {
333 ($ptr:expr, $hint:expr) => {
334 match $hint {
335 PrefetchHint::T0 => _mm_prefetch($ptr as *const i8, 3),
336 PrefetchHint::T1 => _mm_prefetch($ptr as *const i8, 2),
337 PrefetchHint::T2 => _mm_prefetch($ptr as *const i8, 1),
338 PrefetchHint::NTA => _mm_prefetch($ptr as *const i8, 0),
339 }
340 };
341 }
342
343 if i + prefetch_distance < a.nrows() {
345 let a_ptr = &a[[i + prefetch_distance, k]] as *const f32;
346 prefetch_with_hint!(a_ptr, hint);
347 }
348
349 if j + prefetch_distance < b.ncols() {
351 let b_ptr = &b[[k, j + prefetch_distance]] as *const f32;
352 prefetch_with_hint!(b_ptr, hint);
353 }
354
355 if i + prefetch_distance < c.nrows() && j + prefetch_distance < c.ncols() {
357 let c_ptr = &c[[i + prefetch_distance, j + prefetch_distance]] as *const f32;
358 prefetch_with_hint!(c_ptr, hint);
359 }
360 }
361
362 #[cfg(not(target_arch = "x86_64"))]
363 {
364 let _ = (a, b, c, i, j, k, strategy);
366 }
367 }
368
369 pub fn cache_aware_transpose_f32(
371 &mut self,
372 input: &ArrayView2<f32>,
373 ) -> LinalgResult<Array2<f32>> {
374 let (rows, cols) = input.dim();
375 let mut result = Array2::zeros((cols, rows));
376
377 let elementsize = std::mem::size_of::<f32>();
379 let optimal_blocksize = ((self.l1_cachesize / 2) / elementsize).min(64);
380 let blocksize = (optimal_blocksize as f64).sqrt() as usize;
381
382 for i in (0..rows).step_by(blocksize) {
384 for j in (0..cols).step_by(blocksize) {
385 let i_end = (i + blocksize).min(rows);
386 let j_end = (j + blocksize).min(cols);
387
388 for ii in i..i_end {
390 for jj in j..j_end {
391 result[[jj, ii]] = input[[ii, jj]];
392 }
393 }
394 }
395 }
396
397 self.pattern_analyzer
398 .record_access_pattern(AccessType::Strided(rows));
399
400 Ok(result)
401 }
402}
403
404#[derive(Debug, Clone)]
406pub struct CacheBlockSizes {
407 pub l1_block_m: usize,
408 pub l1_block_n: usize,
409 pub l1_block_k: usize,
410 pub l2_block_m: usize,
411 pub l2_block_n: usize,
412 pub l2_block_k: usize,
413 pub l3_block_m: usize,
414 pub l3_block_n: usize,
415 pub l3_block_k: usize,
416}
417
418pub struct RuntimePerformanceProfiler {
420 timing_history: Vec<(String, Duration)>,
422 cache_miss_rates: Vec<f64>,
424 #[allow(dead_code)]
426 optimization_scores: Vec<f64>,
427 session_start: Option<Instant>,
429}
430
431impl RuntimePerformanceProfiler {
432 pub fn new() -> Self {
433 Self {
434 timing_history: Vec::new(),
435 cache_miss_rates: Vec::new(),
436 optimization_scores: Vec::new(),
437 session_start: None,
438 }
439 }
440
441 pub fn start_session(&mut self, operationname: &str) {
443 self.session_start = Some(Instant::now());
444 self.timing_history
445 .push((operationname.to_string(), Duration::ZERO));
446 }
447
448 pub fn end_session(&mut self) -> Option<Duration> {
450 if let Some(start_time) = self.session_start.take() {
451 let duration = start_time.elapsed();
452
453 if let Some(last_entry) = self.timing_history.last_mut() {
455 last_entry.1 = duration;
456 }
457
458 Some(duration)
459 } else {
460 None
461 }
462 }
463
464 pub fn analyze_and_recommend(&self) -> Vec<OptimizationRecommendation> {
466 let mut recommendations = Vec::new();
467
468 if let Some(avg_time) = self.calculate_average_operation_time() {
470 if avg_time > Duration::from_millis(100) {
471 recommendations.push(OptimizationRecommendation::IncreaseBlockSize);
472 recommendations.push(OptimizationRecommendation::EnableAggressivePrefetch);
473 } else if avg_time < Duration::from_millis(10) {
474 recommendations.push(OptimizationRecommendation::DecreaseBlockSize);
475 }
476 }
477
478 if let Some(avg_miss_rate) = self.calculate_average_cache_miss_rate() {
480 if avg_miss_rate > 0.1 {
481 recommendations.push(OptimizationRecommendation::OptimizeMemoryLayout);
482 recommendations.push(OptimizationRecommendation::IncreaseBlockSize);
483 }
484 }
485
486 recommendations
487 }
488
489 fn calculate_average_operation_time(&self) -> Option<Duration> {
490 if self.timing_history.is_empty() {
491 return None;
492 }
493
494 let total_nanos: u64 = self
495 .timing_history
496 .iter()
497 .map(|(_, duration)| duration.as_nanos() as u64)
498 .sum();
499
500 Some(Duration::from_nanos(
501 total_nanos / self.timing_history.len() as u64,
502 ))
503 }
504
505 fn calculate_average_cache_miss_rate(&self) -> Option<f64> {
506 if self.cache_miss_rates.is_empty() {
507 return None;
508 }
509
510 Some(self.cache_miss_rates.iter().sum::<f64>() / self.cache_miss_rates.len() as f64)
511 }
512}
513
514#[derive(Debug, Clone)]
516pub enum OptimizationRecommendation {
517 IncreaseBlockSize,
518 DecreaseBlockSize,
519 EnableAggressivePrefetch,
520 OptimizeMemoryLayout,
521 SwitchToSIMDImplementation,
522 UseParallelExecution,
523}
524
525impl Default for MemoryAccessPatternAnalyzer {
526 fn default() -> Self {
527 Self::new()
528 }
529}
530
531impl Default for CacheAwareMatrixOperations {
532 fn default() -> Self {
533 Self::new()
534 }
535}
536
537impl Default for RuntimePerformanceProfiler {
538 fn default() -> Self {
539 Self::new()
540 }
541}
542
543pub struct BranchOptimizer;
545
546impl BranchOptimizer {
547 #[allow(dead_code)]
549 #[inline(always)]
550 pub fn likely_branch<T>(_condition: bool, if_true: T, iffalse: T) -> T {
551 if _condition {
554 if_true
555 } else {
556 iffalse
557 }
558 }
559
560 #[allow(dead_code)]
562 #[inline(always)]
563 pub fn unlikely_branch<T>(_condition: bool, if_true: T, iffalse: T) -> T {
564 if _condition {
567 if_true
568 } else {
569 iffalse
570 }
571 }
572
573 #[allow(dead_code)]
575 pub fn unrolled_loop_with_prefetch<F>(
576 start: usize,
577 end: usize,
578 unroll_factor: usize,
579 mut operation: F,
580 ) where
581 F: FnMut(usize),
582 {
583 let mut i = start;
584
585 while i + unroll_factor <= end {
587 for offset in 0..unroll_factor {
588 operation(i + offset);
589 }
590 i += unroll_factor;
591 }
592
593 while i < end {
595 operation(i);
596 i += 1;
597 }
598 }
599}
600
601pub struct AdaptiveVectorizationEngine {
606 cpu_features: CpuFeatures,
608 strategy_performance: std::collections::HashMap<VectorizationStrategy, f64>,
610 auto_tuning_enabled: bool,
612}
613
614#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
615pub enum VectorizationStrategy {
616 SSE42,
618 AVX,
620 AVX2,
622 AVX512,
624 Scalar,
626}
627
628#[derive(Debug, Clone)]
629pub struct CpuFeatures {
630 pub sse42: bool,
631 pub avx: bool,
632 pub avx2: bool,
633 pub avx512: bool,
634 pub fma: bool,
635 pub cache_linesize: usize,
636}
637
638impl AdaptiveVectorizationEngine {
639 pub fn new() -> Self {
641 let cpu_features = Self::detect_cpu_features();
642
643 Self {
644 cpu_features,
645 strategy_performance: std::collections::HashMap::new(),
646 auto_tuning_enabled: true,
647 }
648 }
649
650 #[allow(dead_code)]
652 fn detect_cpu_features() -> CpuFeatures {
653 #[cfg(target_arch = "x86_64")]
654 {
655 CpuFeatures {
656 sse42: is_x86_feature_detected!("sse4.2"),
657 avx: is_x86_feature_detected!("avx"),
658 avx2: is_x86_feature_detected!("avx2"),
659 avx512: is_x86_feature_detected!("avx512f"),
660 fma: is_x86_feature_detected!("fma"),
661 cache_linesize: 64, }
663 }
664 #[cfg(not(target_arch = "x86_64"))]
665 {
666 CpuFeatures {
667 sse42: false,
668 avx: false,
669 avx2: false,
670 avx512: false,
671 fma: false,
672 cache_linesize: 64,
673 }
674 }
675 }
676
677 pub fn select_optimal_strategy(&self, matrixsize: (usize, usize)) -> VectorizationStrategy {
679 let (rows, cols) = matrixsize;
680 let total_elements = rows * cols;
681
682 if total_elements > 100_000 {
684 if self.cpu_features.avx512 {
685 return VectorizationStrategy::AVX512;
686 } else if self.cpu_features.avx2 {
687 return VectorizationStrategy::AVX2;
688 } else if self.cpu_features.avx {
689 return VectorizationStrategy::AVX;
690 }
691 }
692
693 if total_elements > 10_000 {
695 if self.cpu_features.avx2 {
696 return VectorizationStrategy::AVX2;
697 } else if self.cpu_features.avx {
698 return VectorizationStrategy::AVX;
699 } else if self.cpu_features.sse42 {
700 return VectorizationStrategy::SSE42;
701 }
702 }
703
704 if self.cpu_features.sse42 && total_elements > 1_000 {
706 VectorizationStrategy::SSE42
707 } else {
708 VectorizationStrategy::Scalar
709 }
710 }
711
712 #[allow(dead_code)]
714 pub fn adaptivematrix_multiply_f32(
715 &mut self,
716 a: &ArrayView2<f32>,
717 b: &ArrayView2<f32>,
718 ) -> LinalgResult<Array2<f32>> {
719 let start_time = Instant::now();
720
721 let strategy = self.select_optimal_strategy((a.nrows(), a.ncols()));
722 let result = match strategy {
723 VectorizationStrategy::AVX512 => self.matrix_multiply_avx512_f32(a, b),
724 VectorizationStrategy::AVX2 => self.matrix_multiply_avx2_f32(a, b),
725 VectorizationStrategy::AVX => self.matrix_multiply_avx_f32(a, b),
726 VectorizationStrategy::SSE42 => self.matrix_multiply_sse42_f32(a, b),
727 VectorizationStrategy::Scalar => self.matrix_multiply_scalar_f32(a, b),
728 };
729
730 if self.auto_tuning_enabled {
732 let duration = start_time.elapsed().as_secs_f64();
733 self.strategy_performance.insert(strategy, duration);
734 }
735
736 result
737 }
738
739 #[allow(dead_code)]
741 fn matrix_multiply_avx512_f32(
742 &self,
743 a: &ArrayView2<f32>,
744 b: &ArrayView2<f32>,
745 ) -> LinalgResult<Array2<f32>> {
746 self.matrix_multiply_avx2_f32(a, b)
748 }
749
750 #[allow(dead_code)]
752 fn matrix_multiply_avx2_f32(
753 &self,
754 a: &ArrayView2<f32>,
755 b: &ArrayView2<f32>,
756 ) -> LinalgResult<Array2<f32>> {
757 if a.ncols() != b.nrows() {
758 return Err(LinalgError::ShapeError(
759 "Matrix dimensions incompatible for multiplication".to_string(),
760 ));
761 }
762
763 let (m, k) = a.dim();
764 let n = b.ncols();
765 let mut result = Array2::zeros((m, n));
766
767 const BLOCK_SIZE: usize = 64;
769
770 for i in (0..m).step_by(BLOCK_SIZE) {
771 for j in (0..n).step_by(BLOCK_SIZE) {
772 for kk in (0..k).step_by(BLOCK_SIZE) {
773 let i_end = (i + BLOCK_SIZE).min(m);
774 let j_end = (j + BLOCK_SIZE).min(n);
775 let k_end = (kk + BLOCK_SIZE).min(k);
776
777 for ii in i..i_end {
779 for jj in (j..j_end).step_by(8) {
780 let jj_end = (jj + 8).min(j_end);
781 for kkk in kk..k_end {
782 let a_val = a[[ii, kkk]];
783 for jjj in jj..jj_end {
784 result[[ii, jjj]] += a_val * b[[kkk, jjj]];
785 }
786 }
787 }
788 }
789 }
790 }
791 }
792
793 Ok(result)
794 }
795
796 #[allow(dead_code)]
798 fn matrix_multiply_avx_f32(
799 &self,
800 a: &ArrayView2<f32>,
801 b: &ArrayView2<f32>,
802 ) -> LinalgResult<Array2<f32>> {
803 self.matrix_multiply_scalar_f32(a, b)
805 }
806
807 #[allow(dead_code)]
809 fn matrix_multiply_sse42_f32(
810 &self,
811 a: &ArrayView2<f32>,
812 b: &ArrayView2<f32>,
813 ) -> LinalgResult<Array2<f32>> {
814 self.matrix_multiply_scalar_f32(a, b)
816 }
817
818 fn matrix_multiply_scalar_f32(
820 &self,
821 a: &ArrayView2<f32>,
822 b: &ArrayView2<f32>,
823 ) -> LinalgResult<Array2<f32>> {
824 if a.ncols() != b.nrows() {
825 return Err(LinalgError::ShapeError(
826 "Matrix dimensions incompatible for multiplication".to_string(),
827 ));
828 }
829
830 let (m, k) = a.dim();
831 let n = b.ncols();
832 let mut result = Array2::zeros((m, n));
833
834 for i in 0..m {
835 for j in 0..n {
836 for kk in 0..k {
837 result[[i, j]] += a[[i, kk]] * b[[kk, j]];
838 }
839 }
840 }
841
842 Ok(result)
843 }
844
845 #[allow(dead_code)]
847 pub fn get_performance_report(&self) -> std::collections::HashMap<VectorizationStrategy, f64> {
848 self.strategy_performance.clone()
849 }
850
851 #[allow(dead_code)]
853 pub fn set_auto_tuning(&mut self, enabled: bool) {
854 self.auto_tuning_enabled = enabled;
855 }
856}
857
858impl Default for AdaptiveVectorizationEngine {
859 fn default() -> Self {
860 Self::new()
861 }
862}
863
864#[cfg(test)]
865mod tests {
866 use super::*;
867 use approx::assert_abs_diff_eq;
868 use scirs2_core::ndarray::array;
869
870 #[test]
871 fn test_cache_awarematrix_operations() {
872 let mut cache_ops = CacheAwareMatrixOperations::new();
873
874 let a = array![[1.0, 2.0, 3.0], [4.0, 5.0, 6.0]];
875 let b = array![[7.0, 8.0], [9.0, 10.0], [11.0, 12.0]];
876 let mut c = Array2::zeros((2, 2));
877
878 let result = cache_ops.cache_aware_gemm_f32(&a.view(), &b.view(), &mut c.view_mut());
879 assert!(result.is_ok());
880
881 assert_abs_diff_eq!(c[[0, 0]], 58.0, epsilon = 1e-6);
883 assert_abs_diff_eq!(c[[0, 1]], 64.0, epsilon = 1e-6);
884 assert_abs_diff_eq!(c[[1, 0]], 139.0, epsilon = 1e-6);
885 assert_abs_diff_eq!(c[[1, 1]], 154.0, epsilon = 1e-6);
886 }
887
888 #[test]
889 fn test_cache_aware_transpose() {
890 let mut cache_ops = CacheAwareMatrixOperations::new();
891
892 let input = array![[1.0, 2.0, 3.0], [4.0, 5.0, 6.0]];
893 let result = cache_ops
894 .cache_aware_transpose_f32(&input.view())
895 .expect("Operation failed");
896
897 let expected = array![[1.0, 4.0], [2.0, 5.0], [3.0, 6.0]];
898
899 for (actual, expected) in result.iter().zip(expected.iter()) {
900 assert_abs_diff_eq!(*actual, *expected, epsilon = 1e-10);
901 }
902 }
903
904 #[test]
905 fn test_memory_access_pattern_analyzer() {
906 let analyzer = MemoryAccessPatternAnalyzer::new();
907
908 let strategy = analyzer.analyze_and_recommend_prefetch((1000, 1000));
909 if let PrefetchStrategy::Aggressive {
910 prefetch_distance, ..
911 } = strategy
912 {
913 assert!(prefetch_distance > 0);
914 }
915 }
916
917 #[test]
918 fn test_runtime_performance_profiler() {
919 let mut profiler = RuntimePerformanceProfiler::new();
920
921 profiler.start_session("test_operation");
922 std::thread::sleep(Duration::from_millis(1));
923 let duration = profiler.end_session();
924
925 assert!(duration.is_some());
926 assert!(duration.expect("Operation failed") >= Duration::from_millis(1));
927
928 let recommendations = profiler.analyze_and_recommend();
929 assert!(!recommendations.is_empty() || profiler.timing_history.len() < 2);
931 }
932
933 #[test]
934 fn test_branch_optimizer() {
935 let result1 = BranchOptimizer::likely_branch(true, 42, 0);
936 assert_eq!(result1, 42);
937
938 let result2 = BranchOptimizer::unlikely_branch(false, 0, 42);
939 assert_eq!(result2, 42);
940 }
941
942 #[test]
943 fn test_adaptive_vectorization_engine() {
944 let mut engine = AdaptiveVectorizationEngine::new();
945
946 let features = &engine.cpu_features;
948 assert!(features.cache_linesize > 0);
949
950 let small_strategy = engine.select_optimal_strategy((10, 10));
952 let medium_strategy = engine.select_optimal_strategy((100, 100));
953 let large_strategy = engine.select_optimal_strategy((1000, 1000));
954
955 assert!(matches!(
957 small_strategy,
958 VectorizationStrategy::Scalar | VectorizationStrategy::SSE42
959 ));
960 println!("Small matrix strategy: {:?}", small_strategy);
961 println!("Medium matrix strategy: {:?}", medium_strategy);
962 println!("Large matrix strategy: {:?}", large_strategy);
963
964 let a = array![[1.0f32, 2.0], [3.0, 4.0]];
966 let b = array![[5.0f32, 6.0], [7.0, 8.0]];
967
968 let result = engine
969 .adaptivematrix_multiply_f32(&a.view(), &b.view())
970 .expect("Operation failed");
971
972 let expected = array![[19.0f32, 22.0], [43.0, 50.0]];
974 for (actual, expected) in result.iter().zip(expected.iter()) {
975 assert_abs_diff_eq!(*actual, *expected, epsilon = 1e-10);
976 }
977
978 engine.set_auto_tuning(false);
980 let performance_report = engine.get_performance_report();
981 assert!(!performance_report.is_empty());
982 }
983}