1use crate::error::{StatsError, StatsResult};
9use scirs2_core::ndarray::{Array1, Array2, ArrayView1, ArrayView2, Axis};
10use scirs2_core::numeric::{Float, NumCast, One, Zero};
11use serde::{Deserialize, Serialize};
12use std::alloc::{GlobalAlloc, Layout, System};
13use std::collections::{HashMap, VecDeque};
14use std::mem;
15use std::sync::{Arc, Mutex, RwLock};
16
17#[derive(Debug, Clone, Serialize, Deserialize)]
19#[allow(dead_code)]
20pub struct MemoryOptimizationConfig {
21 pub memory_limit: usize,
23 pub enable_streaming: bool,
25 pub enable_memory_mapping: bool,
27 pub enable_cache_optimization: bool,
29 pub enable_adaptive_allocation: bool,
31 pub cache_linesize: usize,
33 pub memory_poolsize: usize,
35 pub enable_compression: bool,
37 pub streaming_chunksize: usize,
39}
40
41impl Default for MemoryOptimizationConfig {
42 fn default() -> Self {
43 Self {
44 memory_limit: 1024 * 1024 * 1024, enable_streaming: true,
46 enable_memory_mapping: true,
47 enable_cache_optimization: true,
48 enable_adaptive_allocation: true,
49 cache_linesize: 64, memory_poolsize: 64 * 1024 * 1024, enable_compression: false, streaming_chunksize: 10000, }
54 }
55}
56
57#[derive(Debug, Clone, Serialize, Deserialize)]
59#[allow(dead_code)]
60pub struct MemoryProfile {
61 pub current_usage: usize,
63 pub peak_usage: usize,
65 pub allocation_count: usize,
67 pub deallocation_count: usize,
69 pub fragmentation_ratio: f64,
71 pub cache_hit_ratio: f64,
73 pub efficiency_score: f64,
75 pub active_pools: Vec<MemoryPoolStats>,
77}
78
79#[derive(Debug, Clone, Serialize, Deserialize)]
81#[allow(dead_code)]
82pub struct MemoryPoolStats {
83 pub pool_id: String,
85 pub poolsize: usize,
87 pub used_bytes: usize,
89 pub allocations: usize,
91 pub avg_allocationsize: f64,
93}
94
95pub struct StreamingStatsCalculator<F> {
97 config: MemoryOptimizationConfig,
98 count: usize,
100 sum: F,
102 sum_squares: F,
104 min_value: Option<F>,
106 max_value: Option<F>,
108 memory_profile: Arc<Mutex<MemoryProfile>>,
110 computation_buffer: VecDeque<F>,
112}
113
114pub struct CacheOptimizedMatrix<F> {
116 data: Array2<F>,
117 blocksize: usize,
118 memory_layout: MatrixLayout,
119 cache_linesize: usize,
120}
121
122#[derive(Debug, Clone, Copy)]
124#[allow(dead_code)]
125pub enum MatrixLayout {
126 RowMajor,
127 ColumnMajor,
128 BlockedRowMajor,
129 BlockedColumnMajor,
130 ZOrderCurve,
131}
132
133pub struct AdaptiveStatsAllocator {
135 config: MemoryOptimizationConfig,
136 memory_pools: HashMap<String, Arc<Mutex<MemoryPool>>>,
137 allocation_patterns: Arc<RwLock<AllocationPatternAnalyzer>>,
138 global_stats: Arc<Mutex<MemoryProfile>>,
139}
140
141#[allow(dead_code)]
143struct MemoryPool {
144 pool_id: String,
145 base_ptr: *mut u8,
146 poolsize: usize,
147 usedsize: usize,
148 free_blocks: Vec<MemoryBlock>,
149 used_blocks: Vec<MemoryBlock>,
150 allocation_count: usize,
151}
152
153#[derive(Debug, Clone)]
155#[allow(dead_code)]
156struct MemoryBlock {
157 offset: usize,
158 size: usize,
159 alignment: usize,
160 allocation_time: std::time::Instant,
161}
162
163#[allow(dead_code)]
165struct AllocationPatternAnalyzer {
166 allocation_history: VecDeque<AllocationEvent>,
167 pattern_cache: HashMap<String, AllocationPattern>,
168 prediction_accuracy: f64,
169}
170
171#[derive(Debug, Clone)]
173#[allow(dead_code)]
174struct AllocationEvent {
175 size: usize,
176 alignment: usize,
177 lifetime: std::time::Duration,
178 operation_type: String,
179 timestamp: std::time::Instant,
180}
181
182#[derive(Debug, Clone)]
184#[allow(dead_code)]
185struct AllocationPattern {
186 typicalsize: usize,
187 typical_alignment: usize,
188 expected_lifetime: std::time::Duration,
189 frequency: f64,
190 confidence: f64,
191}
192
193pub struct MemoryMappedStatsProcessor {
195 config: MemoryOptimizationConfig,
196 mapped_files: HashMap<String, MemoryMappedFile>,
197 cache_manager: CacheManager,
198}
199
200#[allow(dead_code)]
202struct MemoryMappedFile {
203 file_path: String,
204 filesize: usize,
205 mapped_ptr: *const u8,
206 access_pattern: AccessPattern,
207}
208
209#[derive(Debug, Clone)]
211struct AccessPattern {
212 sequential_ratio: f64,
213 random_ratio: f64,
214 hot_regions: Vec<MemoryRegion>,
215}
216
217#[derive(Debug, Clone)]
219struct MemoryRegion {
220 start_offset: usize,
221 end_offset: usize,
222 access_frequency: f64,
223 last_access: std::time::Instant,
224}
225
226#[allow(dead_code)]
228struct CacheManager {
229 cachesize: usize,
230 cache_entries: HashMap<u64, CacheEntry>,
231 access_order: VecDeque<u64>,
232 hit_count: usize,
233 miss_count: usize,
234}
235
236#[allow(dead_code)]
238struct CacheEntry {
239 data: Vec<u8>,
240 access_count: usize,
241 last_access: std::time::Instant,
242 size: usize,
243}
244
245impl<F> StreamingStatsCalculator<F>
246where
247 F: Float + NumCast + Zero + One + Send + Sync + std::fmt::Display,
248{
249 pub fn new(config: MemoryOptimizationConfig) -> Self {
251 Self {
252 config,
253 count: 0,
254 sum: F::zero(),
255 sum_squares: F::zero(),
256 min_value: None,
257 max_value: None,
258 memory_profile: Arc::new(Mutex::new(MemoryProfile::new())),
259 computation_buffer: VecDeque::with_capacity(1000), }
261 }
262
263 pub fn process_chunk(&mut self, chunk: ArrayView1<F>) -> StatsResult<()> {
265 self.update_memory_profile();
266
267 for &value in chunk.iter() {
268 self.count += 1;
269 self.sum = self.sum + value;
270 self.sum_squares = self.sum_squares + value * value;
271
272 match self.min_value {
274 None => self.min_value = Some(value),
275 Some(current_min) => {
276 if value < current_min {
277 self.min_value = Some(value);
278 }
279 }
280 }
281
282 match self.max_value {
283 None => self.max_value = Some(value),
284 Some(current_max) => {
285 if value > current_max {
286 self.max_value = Some(value);
287 }
288 }
289 }
290
291 if self.computation_buffer.len() >= self.config.streaming_chunksize {
293 self.computation_buffer.pop_front();
294 }
295 self.computation_buffer.push_back(value);
296 }
297
298 Ok(())
299 }
300
301 pub fn get_statistics(&self) -> StatsResult<StreamingStatistics<F>> {
303 if self.count == 0 {
304 return Err(StatsError::InvalidArgument(
305 "No data processed yet".to_string(),
306 ));
307 }
308
309 let count_f = F::from(self.count).unwrap();
310 let mean = self.sum / count_f;
311
312 let variance = if self.count > 1 {
313 let count_minus_one = F::from(self.count - 1).unwrap();
314 (self.sum_squares - self.sum * self.sum / count_f) / count_minus_one
315 } else {
316 F::zero()
317 };
318
319 let std_dev = variance.sqrt();
320
321 Ok(StreamingStatistics {
322 count: self.count,
323 mean,
324 variance,
325 std_dev,
326 min: self.min_value.unwrap_or(F::zero()),
327 max: self.max_value.unwrap_or(F::zero()),
328 memory_efficiency: self.calculate_memory_efficiency(),
329 })
330 }
331
332 fn calculate_memory_efficiency(&self) -> f64 {
334 let theoretical_minimum = mem::size_of::<F>() * self.count;
335 let actual_usage = self.estimate_memory_usage();
336
337 if actual_usage > 0 {
338 (theoretical_minimum as f64 / actual_usage as f64 * 100.0).min(100.0)
339 } else {
340 100.0
341 }
342 }
343
344 fn estimate_memory_usage(&self) -> usize {
346 mem::size_of::<Self>() + self.computation_buffer.len() * mem::size_of::<F>()
347 }
348
349 fn update_memory_profile(&mut self) {
351 if let Ok(mut profile) = self.memory_profile.lock() {
352 let current_usage = self.estimate_memory_usage();
353 profile.current_usage = current_usage;
354 if current_usage > profile.peak_usage {
355 profile.peak_usage = current_usage;
356 }
357 profile.allocation_count += 1;
358 profile.efficiency_score = self.calculate_memory_efficiency();
359 }
360 }
361}
362
363#[derive(Debug, Clone)]
365#[allow(dead_code)]
366pub struct StreamingStatistics<F> {
367 pub count: usize,
368 pub mean: F,
369 pub variance: F,
370 pub std_dev: F,
371 pub min: F,
372 pub max: F,
373 pub memory_efficiency: f64,
374}
375
376impl<F> CacheOptimizedMatrix<F>
377where
378 F: Float + NumCast + Zero + One + Clone + 'static + std::fmt::Display,
379{
380 pub fn new(data: Array2<F>, layout: MatrixLayout, cache_linesize: usize) -> Self {
382 let optimal_blocksize =
383 Self::calculate_optimal_blocksize(data.nrows(), data.ncols(), cache_linesize);
384
385 let mut matrix = Self {
386 data,
387 blocksize: optimal_blocksize,
388 memory_layout: layout,
389 cache_linesize,
390 };
391
392 matrix.optimize_layout();
393 matrix
394 }
395
396 pub fn multiply_optimized(
398 &self,
399 other: &CacheOptimizedMatrix<F>,
400 ) -> StatsResult<CacheOptimizedMatrix<F>> {
401 if self.data.ncols() != other.data.nrows() {
402 return Err(StatsError::DimensionMismatch(
403 "Matrix dimensions incompatible for multiplication".to_string(),
404 ));
405 }
406
407 let resultdata = match self.memory_layout {
408 MatrixLayout::BlockedRowMajor | MatrixLayout::BlockedColumnMajor => {
409 self.blocked_multiply(&other.data)?
410 }
411 _ => self.standard_multiply(&other.data)?,
412 };
413
414 Ok(CacheOptimizedMatrix::new(
415 resultdata,
416 self.memory_layout,
417 self.cache_linesize,
418 ))
419 }
420
421 pub fn correlation_matrix(&self) -> StatsResult<CacheOptimizedMatrix<F>> {
423 let (_n_samples_, n_features) = self.data.dim();
424
425 let means = self.compute_column_means_optimized()?;
427
428 let mut correlation = Array2::zeros((n_features, n_features));
430
431 let blocksize = self.blocksize;
432 for i_block in (0..n_features).step_by(blocksize) {
433 for j_block in (0..n_features).step_by(blocksize) {
434 let i_end = (i_block + blocksize).min(n_features);
435 let j_end = (j_block + blocksize).min(n_features);
436
437 for i in i_block..i_end {
438 for j in j_block..j_end {
439 let correlation_value = self.compute_correlation_pair(i, j, &means)?;
440 correlation[[i, j]] = correlation_value;
441 }
442 }
443 }
444 }
445
446 Ok(CacheOptimizedMatrix::new(
447 correlation,
448 MatrixLayout::BlockedRowMajor,
449 self.cache_linesize,
450 ))
451 }
452
453 fn calculate_optimal_blocksize(_rows: usize, cols: usize, cache_linesize: usize) -> usize {
455 let elementsize = mem::size_of::<F>();
456 let elements_per_cache_line = cache_linesize / elementsize;
457
458 let target_block_elements = (32 * 1024) / elementsize; let max_dimension = _rows.max(cols);
461
462 ((target_block_elements as f64).sqrt() as usize)
463 .min(max_dimension)
464 .max(elements_per_cache_line)
465 }
466
467 fn optimize_layout(&mut self) {
469 match self.memory_layout {
470 MatrixLayout::ZOrderCurve => {
471 self.data = self.convert_to_z_order();
472 }
473 MatrixLayout::BlockedRowMajor => {
474 self.data = self.convert_to_blocked_layout(true);
475 }
476 MatrixLayout::BlockedColumnMajor => {
477 self.data = self.convert_to_blocked_layout(false);
478 }
479 _ => {
480 }
482 }
483 }
484
485 fn convert_to_z_order(&self) -> Array2<F> {
487 self.data.clone() }
491
492 fn convert_to_blocked_layout(&self, rowmajor: bool) -> Array2<F> {
494 self.data.clone() }
497
498 fn blocked_multiply(&self, other: &Array2<F>) -> StatsResult<Array2<F>> {
500 let (m, k) = self.data.dim();
501 let (k2, n) = other.dim();
502
503 if k != k2 {
504 return Err(StatsError::DimensionMismatch(
505 "Matrix dimensions incompatible".to_string(),
506 ));
507 }
508
509 let mut result = Array2::zeros((m, n));
510 let blocksize = self.blocksize;
511
512 for i_block in (0..m).step_by(blocksize) {
514 for j_block in (0..n).step_by(blocksize) {
515 for k_block in (0..k).step_by(blocksize) {
516 let i_end = (i_block + blocksize).min(m);
517 let j_end = (j_block + blocksize).min(n);
518 let k_end = (k_block + blocksize).min(k);
519
520 for i in i_block..i_end {
521 for j in j_block..j_end {
522 let mut sum = F::zero();
523 for k_idx in k_block..k_end {
524 sum = sum + self.data[[i, k_idx]] * other[[k_idx, j]];
525 }
526 result[[i, j]] = result[[i, j]] + sum;
527 }
528 }
529 }
530 }
531 }
532
533 Ok(result)
534 }
535
536 fn standard_multiply(&self, other: &Array2<F>) -> StatsResult<Array2<F>> {
538 let result = self.data.dot(other);
539 Ok(result)
540 }
541
542 fn compute_column_means_optimized(&self) -> StatsResult<Array1<F>> {
544 let n_features = self.data.ncols();
545 let n_samples_f = F::from(self.data.nrows()).unwrap();
546 let mut means = Array1::zeros(n_features);
547
548 let blocksize = self.blocksize;
550 for col_block in (0..n_features).step_by(blocksize) {
551 let col_end = (col_block + blocksize).min(n_features);
552
553 for col in col_block..col_end {
554 let column_sum = self.data.column(col).sum();
555 means[col] = column_sum / n_samples_f;
556 }
557 }
558
559 Ok(means)
560 }
561
562 fn compute_correlation_pair(
564 &self,
565 col_i: usize,
566 col_j: usize,
567 means: &Array1<F>,
568 ) -> StatsResult<F> {
569 if col_i == col_j {
570 return Ok(F::one());
571 }
572
573 let n_samples_ = self.data.nrows();
574 let _n_samples_f = F::from(n_samples_).unwrap();
575
576 let mean_i = means[col_i];
577 let mean_j = means[col_j];
578
579 let mut numerator = F::zero();
580 let mut sum_sq_i = F::zero();
581 let mut sum_sq_j = F::zero();
582
583 for row in 0..n_samples_ {
585 let val_i = self.data[[row, col_i]] - mean_i;
586 let val_j = self.data[[row, col_j]] - mean_j;
587
588 numerator = numerator + val_i * val_j;
589 sum_sq_i = sum_sq_i + val_i * val_i;
590 sum_sq_j = sum_sq_j + val_j * val_j;
591 }
592
593 let denominator = (sum_sq_i * sum_sq_j).sqrt();
594 if denominator > F::epsilon() {
595 Ok(numerator / denominator)
596 } else {
597 Ok(F::zero())
598 }
599 }
600}
601
602impl AdaptiveStatsAllocator {
603 pub fn new(config: MemoryOptimizationConfig) -> Self {
605 let mut allocator = Self {
606 config: config.clone(),
607 memory_pools: HashMap::new(),
608 allocation_patterns: Arc::new(RwLock::new(AllocationPatternAnalyzer::new())),
609 global_stats: Arc::new(Mutex::new(MemoryProfile::new())),
610 };
611
612 let _ = allocator.create_memory_pool("float_arrays", config.memory_poolsize / 4);
614 let _ = allocator.create_memory_pool("matrix_operations", config.memory_poolsize / 2);
615 let _ = allocator.create_memory_pool("temporary_buffers", config.memory_poolsize / 4);
616
617 allocator
618 }
619
620 pub fn create_memory_pool(&mut self, poolid: &str, size: usize) -> StatsResult<()> {
622 let pool = Arc::new(Mutex::new(MemoryPool::new(poolid, size)?));
623 self.memory_pools.insert(poolid.to_string(), pool);
624 Ok(())
625 }
626
627 pub fn allocate_optimized(
629 &self,
630 size: usize,
631 alignment: usize,
632 operation_type: &str,
633 ) -> StatsResult<*mut u8> {
634 let predicted_pool = self.predict_optimal_pool(size, alignment, operation_type);
636
637 if let Some(pool) = self.memory_pools.get(&predicted_pool) {
639 if let Ok(mut pool_guard) = pool.lock() {
640 if let Ok(ptr) = pool_guard.allocate(size, alignment) {
641 self.record_allocation_event(size, alignment, operation_type);
642 return Ok(ptr);
643 }
644 }
645 }
646
647 let layout = Layout::from_size_align(size, alignment)
649 .map_err(|e| StatsError::ComputationError(format!("Invalid layout: {}", e)))?;
650
651 let ptr = unsafe { System.alloc(layout) };
652 if ptr.is_null() {
653 return Err(StatsError::ComputationError(
654 "Memory allocation failed".to_string(),
655 ));
656 }
657
658 self.record_allocation_event(size, alignment, operation_type);
659 Ok(ptr)
660 }
661
662 fn predict_optimal_pool(&self, size: usize, alignment: usize, operationtype: &str) -> String {
664 if let Ok(analyzer) = self.allocation_patterns.read() {
665 if let Some(pattern) = analyzer.get_pattern(operationtype) {
666 if pattern.typicalsize <= 1024 {
668 return "temporary_buffers".to_string();
669 } else if pattern.typicalsize <= 64 * 1024 {
670 return "float_arrays".to_string();
671 } else {
672 return "matrix_operations".to_string();
673 }
674 }
675 }
676
677 if size <= 1024 {
679 "temporary_buffers".to_string()
680 } else if size <= 64 * 1024 {
681 "float_arrays".to_string()
682 } else {
683 "matrix_operations".to_string()
684 }
685 }
686
687 fn record_allocation_event(&self, size: usize, alignment: usize, operationtype: &str) {
689 if let Ok(mut analyzer) = self.allocation_patterns.write() {
690 analyzer.record_allocation(size, alignment, operationtype);
691 }
692
693 if let Ok(mut stats) = self.global_stats.lock() {
694 stats.allocation_count += 1;
695 stats.current_usage += size;
696 if stats.current_usage > stats.peak_usage {
697 stats.peak_usage = stats.current_usage;
698 }
699 }
700 }
701
702 pub fn get_memory_profile(&self) -> MemoryProfile {
704 if let Ok(profile) = self.global_stats.lock() {
705 profile.clone()
706 } else {
707 MemoryProfile::new()
708 }
709 }
710
711 pub fn optimize_pools(&mut self) -> StatsResult<()> {
713 let pools_to_create: Vec<(String, usize)> = {
715 if let Ok(analyzer) = self.allocation_patterns.read() {
716 analyzer
717 .pattern_cache
718 .iter()
719 .filter(|(_, pattern)| pattern.confidence > 0.8)
720 .map(|(operation_type, pattern)| {
721 let pool_name = format!("specialized_{}", operation_type);
722 let optimalsize = (pattern.typicalsize * 100).max(64 * 1024);
723 (pool_name, optimalsize)
724 })
725 .collect()
726 } else {
727 Vec::new()
728 }
729 };
730
731 for (pool_name, optimalsize) in pools_to_create {
733 self.create_memory_pool(&pool_name, optimalsize)?;
734 }
735 Ok(())
736 }
737}
738
739impl MemoryPool {
740 fn new(poolid: &str, size: usize) -> StatsResult<Self> {
742 let layout = Layout::from_size_align(size, 64) .map_err(|e| StatsError::ComputationError(format!("Invalid layout: {}", e)))?;
744
745 let base_ptr = unsafe { System.alloc(layout) };
746 if base_ptr.is_null() {
747 return Err(StatsError::ComputationError(
748 "Memory pool allocation failed".to_string(),
749 ));
750 }
751
752 Ok(Self {
753 pool_id: poolid.to_string(),
754 base_ptr,
755 poolsize: size,
756 usedsize: 0,
757 free_blocks: vec![MemoryBlock {
758 offset: 0,
759 size,
760 alignment: 64,
761 allocation_time: std::time::Instant::now(),
762 }],
763 used_blocks: Vec::new(),
764 allocation_count: 0,
765 })
766 }
767
768 fn allocate(&mut self, size: usize, alignment: usize) -> StatsResult<*mut u8> {
770 for (index, block) in self.free_blocks.iter().enumerate() {
772 let aligned_offset = self.align_offset(block.offset, alignment);
773 let totalsize = aligned_offset - block.offset + size;
774
775 if totalsize <= block.size {
776 let used_block = MemoryBlock {
778 offset: aligned_offset,
779 size,
780 alignment,
781 allocation_time: std::time::Instant::now(),
782 };
783
784 let remainingsize = block.size - totalsize;
785 if remainingsize > 0 {
786 let remaining_block = MemoryBlock {
787 offset: aligned_offset + size,
788 size: remainingsize,
789 alignment: 1,
790 allocation_time: std::time::Instant::now(),
791 };
792 self.free_blocks[index] = remaining_block;
793 } else {
794 self.free_blocks.remove(index);
795 }
796
797 self.used_blocks.push(used_block);
798 self.usedsize += size;
799 self.allocation_count += 1;
800
801 let ptr = unsafe { self.base_ptr.add(aligned_offset) };
802 return Ok(ptr);
803 }
804 }
805
806 Err(StatsError::ComputationError(
807 "No suitable block available in pool".to_string(),
808 ))
809 }
810
811 fn align_offset(&self, offset: usize, alignment: usize) -> usize {
813 (offset + alignment - 1) & !(alignment - 1)
814 }
815}
816
817impl AllocationPatternAnalyzer {
818 fn new() -> Self {
819 Self {
820 allocation_history: VecDeque::with_capacity(10000),
821 pattern_cache: HashMap::new(),
822 prediction_accuracy: 0.0,
823 }
824 }
825
826 fn record_allocation(&mut self, size: usize, alignment: usize, operationtype: &str) {
827 let event = AllocationEvent {
828 size,
829 alignment,
830 lifetime: std::time::Duration::from_secs(0), operation_type: operationtype.to_string(),
832 timestamp: std::time::Instant::now(),
833 };
834
835 self.allocation_history.push_back(event);
836
837 if self.allocation_history.len() > 10000 {
839 self.allocation_history.pop_front();
840 }
841
842 if self.allocation_history.len().is_multiple_of(100) {
844 self.update_patterns();
845 }
846 }
847
848 fn update_patterns(&mut self) {
849 let mut operation_groups: HashMap<String, Vec<&AllocationEvent>> = HashMap::new();
850
851 for event in &self.allocation_history {
852 operation_groups
853 .entry(event.operation_type.clone())
854 .or_default()
855 .push(event);
856 }
857
858 for (operation_type, events) in operation_groups {
859 if events.len() >= 10 {
860 let pattern = self.analyze_allocation_pattern(&events);
862 self.pattern_cache.insert(operation_type, pattern);
863 }
864 }
865 }
866
867 fn analyze_allocation_pattern(&self, events: &[&AllocationEvent]) -> AllocationPattern {
868 let sizes: Vec<usize> = events.iter().map(|e| e.size).collect();
869 let alignments: Vec<usize> = events.iter().map(|e| e.alignment).collect();
870
871 let typicalsize = self.calculate_median(&sizes);
872 let typical_alignment = self.calculate_mode(&alignments);
873 let frequency = events.len() as f64 / self.allocation_history.len() as f64;
874
875 let size_variance = self.calculate_variance(&sizes);
877 let confidence = 1.0 / (1.0 + size_variance / typicalsize as f64);
878
879 AllocationPattern {
880 typicalsize,
881 typical_alignment,
882 expected_lifetime: std::time::Duration::from_millis(100), frequency,
884 confidence,
885 }
886 }
887
888 fn calculate_median(&self, values: &[usize]) -> usize {
889 let mut sorted = values.to_vec();
890 sorted.sort_unstable();
891 sorted[sorted.len() / 2]
892 }
893
894 fn calculate_mode(&self, values: &[usize]) -> usize {
895 let mut counts = HashMap::new();
896 for &value in values {
897 *counts.entry(value).or_insert(0) += 1;
898 }
899
900 counts
901 .into_iter()
902 .max_by_key(|(_, count)| *count)
903 .map(|(_, value)| value)
904 .unwrap_or(64) }
906
907 fn calculate_variance(&self, values: &[usize]) -> f64 {
908 let mean = values.iter().sum::<usize>() as f64 / values.len() as f64;
909 let variance = values
910 .iter()
911 .map(|&x| (x as f64 - mean).powi(2))
912 .sum::<f64>()
913 / values.len() as f64;
914 variance
915 }
916
917 fn get_pattern(&self, operationtype: &str) -> Option<&AllocationPattern> {
918 self.pattern_cache.get(operationtype)
919 }
920}
921
922impl MemoryProfile {
923 fn new() -> Self {
924 Self {
925 current_usage: 0,
926 peak_usage: 0,
927 allocation_count: 0,
928 deallocation_count: 0,
929 fragmentation_ratio: 0.0,
930 cache_hit_ratio: 0.0,
931 efficiency_score: 100.0,
932 active_pools: Vec::new(),
933 }
934 }
935}
936
937pub struct MemoryOptimizationSuite {
939 config: MemoryOptimizationConfig,
940 allocator: AdaptiveStatsAllocator,
941 cache_manager: CacheManager,
942}
943
944impl MemoryOptimizationSuite {
945 pub fn new(config: MemoryOptimizationConfig) -> Self {
947 let allocator = AdaptiveStatsAllocator::new(config.clone());
948 let cache_manager = CacheManager::new(config.memory_poolsize / 8); Self {
951 config,
952 allocator,
953 cache_manager,
954 }
955 }
956
957 pub fn optimized_correlation_matrix<F>(&mut self, data: ArrayView2<F>) -> StatsResult<Array2<F>>
959 where
960 F: Float + NumCast + Zero + One + Clone + Send + Sync + 'static + std::fmt::Display,
961 {
962 let (n_samples_, n_features) = data.dim();
963 let datasize = n_samples_ * n_features * mem::size_of::<F>();
964
965 if datasize > self.config.memory_limit {
966 self.streaming_correlation_matrix(data)
968 } else {
969 let cache_optimized = CacheOptimizedMatrix::new(
971 data.to_owned(),
972 MatrixLayout::BlockedRowMajor,
973 self.config.cache_linesize,
974 );
975
976 let result = cache_optimized.correlation_matrix()?;
977 Ok(result.data)
978 }
979 }
980
981 fn streaming_correlation_matrix<F>(&mut self, data: ArrayView2<F>) -> StatsResult<Array2<F>>
983 where
984 F: Float + NumCast + Zero + One + Clone + 'static + std::fmt::Display,
985 {
986 let (n_samples_, n_features) = data.dim();
987 let chunksize = self.config.streaming_chunksize;
988
989 let mut means = vec![F::zero(); n_features];
991 let _variances = vec![F::zero(); n_features];
992
993 for chunk_start in (0..n_samples_).step_by(chunksize) {
995 let chunk_end = (chunk_start + chunksize).min(n_samples_);
996 let chunk = data.slice(scirs2_core::ndarray::s![chunk_start..chunk_end, ..]);
997
998 for (feature_idx, column) in chunk.axis_iter(Axis(1)).enumerate() {
999 for &value in column.iter() {
1000 means[feature_idx] = means[feature_idx] + value;
1001 }
1002 }
1003 }
1004
1005 let n_samples_f = F::from(n_samples_).unwrap();
1006 for mean in &mut means {
1007 *mean = *mean / n_samples_f;
1008 }
1009
1010 let mut correlation_matrix = Array2::zeros((n_features, n_features));
1012
1013 for i in 0..n_features {
1014 for j in i..n_features {
1015 correlation_matrix[[i, j]] =
1016 self.compute_streaming_correlation(&data, i, j, means[i], means[j])?;
1017 correlation_matrix[[j, i]] = correlation_matrix[[i, j]];
1018 }
1019 }
1020
1021 Ok(correlation_matrix)
1022 }
1023
1024 fn compute_streaming_correlation<F>(
1026 &self,
1027 data: &ArrayView2<F>,
1028 feature_i: usize,
1029 feature_j: usize,
1030 mean_i: F,
1031 mean_j: F,
1032 ) -> StatsResult<F>
1033 where
1034 F: Float + NumCast + Zero + One + std::fmt::Display,
1035 {
1036 if feature_i == feature_j {
1037 return Ok(F::one());
1038 }
1039
1040 let mut numerator = F::zero();
1041 let mut sum_sq_i = F::zero();
1042 let mut sum_sq_j = F::zero();
1043
1044 let chunksize = self.config.streaming_chunksize;
1045 let n_samples_ = data.nrows();
1046
1047 for chunk_start in (0..n_samples_).step_by(chunksize) {
1048 let chunk_end = (chunk_start + chunksize).min(n_samples_);
1049
1050 for row in chunk_start..chunk_end {
1051 let val_i = data[[row, feature_i]] - mean_i;
1052 let val_j = data[[row, feature_j]] - mean_j;
1053
1054 numerator = numerator + val_i * val_j;
1055 sum_sq_i = sum_sq_i + val_i * val_i;
1056 sum_sq_j = sum_sq_j + val_j * val_j;
1057 }
1058 }
1059
1060 let denominator = (sum_sq_i * sum_sq_j).sqrt();
1061 if denominator > F::epsilon() {
1062 Ok(numerator / denominator)
1063 } else {
1064 Ok(F::zero())
1065 }
1066 }
1067
1068 pub fn get_optimization_report(&self) -> MemoryOptimizationReport {
1070 let memory_profile = self.allocator.get_memory_profile();
1071 let cache_stats = self.cache_manager.get_statistics();
1072
1073 MemoryOptimizationReport {
1074 config: self.config.clone(),
1075 memory_profile,
1076 cache_statistics: cache_stats,
1077 optimization_recommendations: self.generate_optimization_recommendations(),
1078 }
1079 }
1080
1081 fn generate_optimization_recommendations(&self) -> Vec<MemoryOptimizationRecommendation> {
1083 let mut recommendations = Vec::new();
1084 let profile = self.allocator.get_memory_profile();
1085
1086 if profile.efficiency_score < 70.0 {
1088 recommendations.push(MemoryOptimizationRecommendation {
1089 priority: 5,
1090 category: "Memory Efficiency".to_string(),
1091 description: "Low memory efficiency detected".to_string(),
1092 suggestion: "Consider using streaming algorithms for large datasets".to_string(),
1093 expected_impact: "20-50% memory reduction".to_string(),
1094 });
1095 }
1096
1097 if profile.cache_hit_ratio < 0.8 {
1099 recommendations.push(MemoryOptimizationRecommendation {
1100 priority: 4,
1101 category: "Cache Performance".to_string(),
1102 description: "Low cache hit ratio detected".to_string(),
1103 suggestion: "Use cache-optimized matrix layouts".to_string(),
1104 expected_impact: "10-30% performance improvement".to_string(),
1105 });
1106 }
1107
1108 if profile.fragmentation_ratio > 0.3 {
1110 recommendations.push(MemoryOptimizationRecommendation {
1111 priority: 3,
1112 category: "Memory Fragmentation".to_string(),
1113 description: "High memory fragmentation detected".to_string(),
1114 suggestion: "Enable memory pool optimization".to_string(),
1115 expected_impact: "Reduced allocation overhead".to_string(),
1116 });
1117 }
1118
1119 recommendations
1120 }
1121}
1122
1123impl CacheManager {
1124 fn new(cachesize: usize) -> Self {
1125 Self {
1126 cachesize,
1127 cache_entries: HashMap::new(),
1128 access_order: VecDeque::new(),
1129 hit_count: 0,
1130 miss_count: 0,
1131 }
1132 }
1133
1134 fn get_statistics(&self) -> CacheStatistics {
1135 let total_accesses = self.hit_count + self.miss_count;
1136 let hit_ratio = if total_accesses > 0 {
1137 self.hit_count as f64 / total_accesses as f64
1138 } else {
1139 0.0
1140 };
1141
1142 CacheStatistics {
1143 hit_ratio,
1144 total_entries: self.cache_entries.len(),
1145 totalsize: self.cache_entries.values().map(|e| e.size).sum(),
1146 total_accesses,
1147 }
1148 }
1149}
1150
1151#[derive(Debug, Clone, Serialize, Deserialize)]
1153#[allow(dead_code)]
1154pub struct MemoryOptimizationReport {
1155 pub config: MemoryOptimizationConfig,
1156 pub memory_profile: MemoryProfile,
1157 pub cache_statistics: CacheStatistics,
1158 pub optimization_recommendations: Vec<MemoryOptimizationRecommendation>,
1159}
1160
1161#[derive(Debug, Clone, Serialize, Deserialize)]
1163#[allow(dead_code)]
1164pub struct CacheStatistics {
1165 pub hit_ratio: f64,
1166 pub total_entries: usize,
1167 pub totalsize: usize,
1168 pub total_accesses: usize,
1169}
1170
1171#[derive(Debug, Clone, Serialize, Deserialize)]
1173#[allow(dead_code)]
1174pub struct MemoryOptimizationRecommendation {
1175 pub priority: u8,
1176 pub category: String,
1177 pub description: String,
1178 pub suggestion: String,
1179 pub expected_impact: String,
1180}
1181
1182#[cfg(test)]
1183mod tests {
1184 use super::*;
1185 use scirs2_core::ndarray::array;
1186
1187 #[test]
1188 fn test_streaming_stats_calculator() {
1189 let config = MemoryOptimizationConfig::default();
1190 let mut calculator = StreamingStatsCalculator::new(config);
1191
1192 let chunk1 = array![1.0, 2.0, 3.0];
1193 let chunk2 = array![4.0, 5.0, 6.0];
1194
1195 calculator.process_chunk(chunk1.view()).unwrap();
1196 calculator.process_chunk(chunk2.view()).unwrap();
1197
1198 let stats = calculator.get_statistics().unwrap();
1199 assert_eq!(stats.count, 6);
1200 assert!((stats.mean - 3.5).abs() < 1e-10);
1201 }
1202
1203 #[test]
1204 fn test_cache_optimized_matrix() {
1205 let data = array![[1.0, 2.0], [3.0, 4.0]];
1206 let matrix = CacheOptimizedMatrix::new(data, MatrixLayout::BlockedRowMajor, 64);
1207
1208 assert_eq!(matrix.data.nrows(), 2);
1209 assert_eq!(matrix.data.ncols(), 2);
1210 }
1211
1212 #[test]
1213 fn test_adaptive_allocator() {
1214 let config = MemoryOptimizationConfig::default();
1215 let allocator = AdaptiveStatsAllocator::new(config);
1216
1217 let ptr = allocator
1218 .allocate_optimized(1024, 8, "test_operation")
1219 .unwrap();
1220 assert!(!ptr.is_null());
1221 }
1222
1223 #[test]
1224 fn test_memory_optimization_suite() {
1225 let config = MemoryOptimizationConfig::default();
1226 let mut suite = MemoryOptimizationSuite::new(config);
1227
1228 let data = array![[1.0, 2.0, 3.0], [4.0, 5.0, 6.0], [7.0, 8.0, 9.0]];
1229 let correlation = suite.optimized_correlation_matrix(data.view()).unwrap();
1230
1231 assert_eq!(correlation.nrows(), 3);
1232 assert_eq!(correlation.ncols(), 3);
1233
1234 for i in 0..3 {
1236 assert!((correlation[[i, i]] - 1.0).abs() < 1e-10);
1237 }
1238 }
1239}