#![allow(dead_code)]
use crate::{SparseFormat, SparseTensor, TorshResult};
use std::collections::HashMap;
use std::time::{Duration, Instant};
use super::core::PerformanceMeasurement;
#[derive(Debug, Clone)]
pub struct MemoryAnalysis {
pub format: SparseFormat,
pub nnz: usize,
pub estimated_memory: usize,
pub dense_memory: usize,
pub compression_ratio: f32,
pub overhead_per_nnz: f32,
pub matrix_dimensions: (usize, usize),
pub memory_breakdown: MemoryBreakdown,
pub metrics: HashMap<String, f64>,
}
#[derive(Debug, Clone)]
pub struct MemoryBreakdown {
pub values_memory: usize,
pub indices_memory: usize,
pub structure_memory: usize,
pub metadata_memory: usize,
}
impl MemoryAnalysis {
pub fn new(format: SparseFormat, nnz: usize, matrix_dimensions: (usize, usize)) -> Self {
Self {
format,
nnz,
estimated_memory: 0,
dense_memory: 0,
compression_ratio: 1.0,
overhead_per_nnz: 0.0,
matrix_dimensions,
memory_breakdown: MemoryBreakdown::default(),
metrics: HashMap::new(),
}
}
pub fn compression_effectiveness(&self) -> f32 {
if self.dense_memory == 0 {
return 0.0;
}
1.0 - (self.estimated_memory as f32 / self.dense_memory as f32)
}
pub fn sparsity_ratio(&self) -> f32 {
let total_elements = self.matrix_dimensions.0 * self.matrix_dimensions.1;
if total_elements == 0 {
return 0.0;
}
1.0 - (self.nnz as f32 / total_elements as f32)
}
pub fn is_memory_efficient(&self) -> bool {
self.compression_ratio > 2.0 }
pub fn memory_efficiency_rating(&self) -> String {
match self.compression_ratio {
r if r >= 10.0 => "Excellent".to_string(),
r if r >= 5.0 => "Good".to_string(),
r if r >= 2.0 => "Fair".to_string(),
_ => "Poor".to_string(),
}
}
pub fn add_metric(&mut self, key: String, value: f64) {
self.metrics.insert(key, value);
}
}
impl Default for MemoryBreakdown {
fn default() -> Self {
Self {
values_memory: 0,
indices_memory: 0,
structure_memory: 0,
metadata_memory: 0,
}
}
}
impl MemoryBreakdown {
pub fn total_memory(&self) -> usize {
self.values_memory + self.indices_memory + self.structure_memory + self.metadata_memory
}
pub fn memory_distribution(&self) -> HashMap<String, f64> {
let total = self.total_memory() as f64;
if total == 0.0 {
return HashMap::new();
}
let mut distribution = HashMap::new();
distribution.insert(
"values".to_string(),
(self.values_memory as f64 / total) * 100.0,
);
distribution.insert(
"indices".to_string(),
(self.indices_memory as f64 / total) * 100.0,
);
distribution.insert(
"structure".to_string(),
(self.structure_memory as f64 / total) * 100.0,
);
distribution.insert(
"metadata".to_string(),
(self.metadata_memory as f64 / total) * 100.0,
);
distribution
}
}
#[derive(Debug, Clone)]
pub struct CachePerformanceResult {
pub operation: String,
pub measurements: Vec<PerformanceMeasurement>,
pub cache_efficiency_score: f64,
pub recommendations: Vec<String>,
pub cache_miss_ratio: f64,
pub access_pattern: MemoryAccessPattern,
}
#[derive(Debug, Clone)]
pub enum MemoryAccessPattern {
Sequential,
Random,
Strided { stride: usize },
Blocked { block_size: usize },
Mixed,
}
impl std::fmt::Display for MemoryAccessPattern {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
MemoryAccessPattern::Sequential => write!(f, "Sequential"),
MemoryAccessPattern::Random => write!(f, "Random"),
MemoryAccessPattern::Strided { stride } => write!(f, "Strided (stride: {})", stride),
MemoryAccessPattern::Blocked { block_size } => {
write!(f, "Blocked (block size: {})", block_size)
}
MemoryAccessPattern::Mixed => write!(f, "Mixed"),
}
}
}
#[derive(Debug)]
pub struct MemoryTracker {
baseline_memory: usize,
peak_memory: usize,
current_memory: usize,
samples: Vec<(Instant, usize)>,
sampling_interval: Duration,
}
impl Default for MemoryTracker {
fn default() -> Self {
Self::new()
}
}
impl MemoryTracker {
pub fn new() -> Self {
let current_memory = get_current_memory_usage();
Self {
baseline_memory: current_memory,
peak_memory: current_memory,
current_memory,
samples: Vec::new(),
sampling_interval: Duration::from_millis(10),
}
}
pub fn track_operation<F, R>(&mut self, operation: F) -> TorshResult<(R, MemoryUsageResult)>
where
F: FnOnce() -> TorshResult<R>,
{
self.reset();
let start_time = Instant::now();
let start_memory = get_current_memory_usage();
self.baseline_memory = start_memory;
self.current_memory = start_memory;
self.peak_memory = start_memory;
self.add_sample(start_time, start_memory);
let result = operation()?;
let end_memory = get_current_memory_usage();
self.current_memory = end_memory;
self.add_sample(Instant::now(), end_memory);
let usage_result = MemoryUsageResult {
baseline_memory: self.baseline_memory,
peak_memory: self.peak_memory,
final_memory: end_memory,
memory_delta: end_memory as i64 - start_memory as i64,
peak_delta: self.peak_memory.saturating_sub(start_memory),
samples: self.samples.clone(),
};
Ok((result, usage_result))
}
pub fn reset(&mut self) {
self.samples.clear();
self.current_memory = get_current_memory_usage();
self.baseline_memory = self.current_memory;
self.peak_memory = self.current_memory;
}
fn add_sample(&mut self, timestamp: Instant, memory_usage: usize) {
self.samples.push((timestamp, memory_usage));
if memory_usage > self.peak_memory {
self.peak_memory = memory_usage;
}
}
pub fn memory_growth_rate(&self) -> f64 {
if self.samples.len() < 2 {
return 0.0;
}
let first = &self.samples[0];
let last = &self.samples[self.samples.len() - 1];
let time_diff = last.0.duration_since(first.0).as_secs_f64();
let memory_diff = last.1 as i64 - first.1 as i64;
if time_diff > 0.0 {
memory_diff as f64 / time_diff
} else {
0.0
}
}
}
#[derive(Debug, Clone)]
pub struct MemoryUsageResult {
pub baseline_memory: usize,
pub peak_memory: usize,
pub final_memory: usize,
pub memory_delta: i64,
pub peak_delta: usize,
pub samples: Vec<(Instant, usize)>,
}
impl MemoryUsageResult {
pub fn has_potential_leak(&self, threshold_bytes: usize) -> bool {
self.memory_delta > threshold_bytes as i64
}
pub fn efficiency_score(&self) -> f64 {
if self.peak_delta == 0 {
return 1.0;
}
let final_delta = self.memory_delta.max(0) as usize;
if self.peak_delta <= final_delta {
1.0
} else {
final_delta as f64 / self.peak_delta as f64
}
}
}
pub fn analyze_sparse_memory(sparse: &dyn SparseTensor) -> TorshResult<MemoryAnalysis> {
let format = sparse.format();
let shape = sparse.shape();
let nnz = sparse.nnz();
let mut analysis = MemoryAnalysis::new(format, nnz, (shape.dims()[0], shape.dims()[1]));
analysis.memory_breakdown = calculate_memory_breakdown(sparse)?;
analysis.estimated_memory = analysis.memory_breakdown.total_memory();
let element_size = match sparse.dtype() {
torsh_core::DType::F32 => 4,
torsh_core::DType::F64 => 8,
torsh_core::DType::I32 => 4,
torsh_core::DType::I64 => 8,
_ => 4, };
analysis.dense_memory = shape.dims()[0] * shape.dims()[1] * element_size;
if analysis.estimated_memory > 0 {
analysis.compression_ratio =
analysis.dense_memory as f32 / analysis.estimated_memory as f32;
}
if nnz > 0 {
analysis.overhead_per_nnz = analysis.estimated_memory as f32 / nnz as f32;
}
analysis.add_metric(
"sparsity_ratio".to_string(),
analysis.sparsity_ratio() as f64,
);
analysis.add_metric(
"compression_effectiveness".to_string(),
analysis.compression_effectiveness() as f64,
);
Ok(analysis)
}
fn calculate_memory_breakdown(sparse: &dyn SparseTensor) -> TorshResult<MemoryBreakdown> {
let nnz = sparse.nnz();
let shape = sparse.shape();
let element_size = match sparse.dtype() {
torsh_core::DType::F32 => 4,
torsh_core::DType::F64 => 8,
torsh_core::DType::I32 => 4,
torsh_core::DType::I64 => 8,
_ => 4,
};
let index_size = 4;
let mut breakdown = MemoryBreakdown::default();
match sparse.format() {
SparseFormat::Coo => {
breakdown.values_memory = nnz * element_size;
breakdown.indices_memory = nnz * 2 * index_size; breakdown.structure_memory = 0;
breakdown.metadata_memory = 32; }
SparseFormat::Csr => {
breakdown.values_memory = nnz * element_size;
breakdown.indices_memory = nnz * index_size; breakdown.structure_memory = (shape.dims()[0] + 1) * index_size; breakdown.metadata_memory = 32;
}
SparseFormat::Csc => {
breakdown.values_memory = nnz * element_size;
breakdown.indices_memory = nnz * index_size; breakdown.structure_memory = (shape.dims()[1] + 1) * index_size; breakdown.metadata_memory = 32;
}
SparseFormat::Bsr => {
breakdown.values_memory = nnz * element_size;
breakdown.indices_memory = nnz * index_size; breakdown.structure_memory = nnz * index_size; breakdown.metadata_memory = 64; }
SparseFormat::Dia => {
breakdown.values_memory = nnz * element_size;
breakdown.indices_memory = 0; breakdown.structure_memory = nnz * index_size; breakdown.metadata_memory = 32;
}
SparseFormat::Dsr => {
breakdown.values_memory = nnz * element_size;
breakdown.indices_memory = nnz * index_size;
breakdown.structure_memory = nnz * index_size;
breakdown.metadata_memory = 32;
}
SparseFormat::Ell => {
breakdown.values_memory = nnz * element_size;
breakdown.indices_memory = nnz * index_size;
breakdown.structure_memory = 0;
breakdown.metadata_memory = 32;
}
SparseFormat::Rle => {
breakdown.values_memory = nnz * element_size;
breakdown.indices_memory = nnz * index_size;
breakdown.structure_memory = nnz * index_size; breakdown.metadata_memory = 32;
}
SparseFormat::Symmetric => {
breakdown.values_memory = nnz * element_size;
breakdown.indices_memory = nnz * index_size;
breakdown.structure_memory = nnz * index_size; breakdown.metadata_memory = 64; }
}
Ok(breakdown)
}
pub fn benchmark_cache_performance(
sparse: &dyn SparseTensor,
operation_name: String,
) -> TorshResult<CachePerformanceResult> {
let mut measurements = Vec::new();
let _cache_metrics: HashMap<String, f64> = HashMap::new();
for iteration in 0..5 {
let measurement_name = format!("{}_cache_iteration_{}", operation_name, iteration);
let mut measurement = PerformanceMeasurement::new(measurement_name);
let start = Instant::now();
simulate_cache_sensitive_operation(sparse)?;
measurement.duration = start.elapsed();
measurements.push(measurement);
}
let cache_efficiency_score = calculate_cache_efficiency(&measurements);
let cache_miss_ratio = estimate_cache_miss_ratio(sparse);
let access_pattern = analyze_access_pattern(sparse);
let recommendations = generate_cache_recommendations(cache_efficiency_score, &access_pattern);
Ok(CachePerformanceResult {
operation: operation_name,
measurements,
cache_efficiency_score,
recommendations,
cache_miss_ratio,
access_pattern,
})
}
fn simulate_cache_sensitive_operation(sparse: &dyn SparseTensor) -> TorshResult<()> {
let nnz = sparse.nnz();
let mut sum = 0.0f64;
match sparse.format() {
SparseFormat::Csr => {
for i in 0..nnz.min(1000) {
sum += (i as f64).sin(); }
}
SparseFormat::Csc => {
for i in 0..nnz.min(1000) {
sum += (i as f64).cos(); }
}
SparseFormat::Coo => {
for i in 0..nnz.min(1000) {
sum += (i as f64).tan(); }
}
_ => {
for i in 0..nnz.min(1000) {
sum += (i as f64).sqrt(); }
}
}
std::hint::black_box(sum);
Ok(())
}
fn calculate_cache_efficiency(measurements: &[PerformanceMeasurement]) -> f64 {
if measurements.len() < 2 {
return 0.5; }
let times: Vec<f64> = measurements
.iter()
.map(|m| m.duration.as_secs_f64())
.collect();
let mean = times.iter().sum::<f64>() / times.len() as f64;
let variance = times.iter().map(|t| (t - mean).powi(2)).sum::<f64>() / times.len() as f64;
let coefficient_of_variation = if mean > 0.0 {
variance.sqrt() / mean
} else {
1.0
};
(1.0 - coefficient_of_variation.min(1.0)).max(0.0)
}
fn estimate_cache_miss_ratio(sparse: &dyn SparseTensor) -> f64 {
let shape = sparse.shape();
let nnz = sparse.nnz();
let sparsity = 1.0 - (nnz as f64 / (shape.dims()[0] * shape.dims()[1]) as f64);
match sparse.format() {
SparseFormat::Csr => 0.1 + sparsity * 0.3, SparseFormat::Csc => 0.1 + sparsity * 0.3,
SparseFormat::Coo => 0.2 + sparsity * 0.5, _ => 0.15 + sparsity * 0.4, }
}
fn analyze_access_pattern(sparse: &dyn SparseTensor) -> MemoryAccessPattern {
let shape = sparse.shape();
let nnz = sparse.nnz();
match sparse.format() {
SparseFormat::Csr => {
if nnz < shape.dims()[0] * 2 {
MemoryAccessPattern::Random
} else {
MemoryAccessPattern::Sequential
}
}
SparseFormat::Csc => {
if nnz < shape.dims()[1] * 2 {
MemoryAccessPattern::Random
} else {
MemoryAccessPattern::Sequential
}
}
SparseFormat::Coo => {
MemoryAccessPattern::Random
}
_ => {
MemoryAccessPattern::Random
}
}
}
fn generate_cache_recommendations(
efficiency_score: f64,
access_pattern: &MemoryAccessPattern,
) -> Vec<String> {
let mut recommendations = Vec::new();
if efficiency_score < 0.5 {
recommendations.push("Consider reordering data to improve cache locality".to_string());
}
match access_pattern {
MemoryAccessPattern::Random => {
recommendations.push(
"Random access pattern detected - consider data layout optimization".to_string(),
);
recommendations.push("Try blocking algorithms to improve spatial locality".to_string());
}
MemoryAccessPattern::Sequential => {
recommendations
.push("Good sequential access pattern - consider prefetching".to_string());
}
MemoryAccessPattern::Strided { stride } => {
if *stride > 8 {
recommendations.push(format!(
"Large stride ({}) detected - consider data reordering",
stride
));
}
}
MemoryAccessPattern::Blocked { .. } => {
recommendations
.push("Blocked access pattern - ensure block size matches cache line".to_string());
}
MemoryAccessPattern::Mixed => {
recommendations
.push("Mixed access pattern - consider hybrid optimization strategies".to_string());
}
}
if recommendations.is_empty() {
recommendations.push("Cache performance appears optimal".to_string());
}
recommendations
}
pub fn generate_memory_optimization_recommendations(
memory_analyses: &[MemoryAnalysis],
total_memory_budget: Option<usize>,
target_operations: &[String],
) -> Vec<String> {
let mut recommendations = Vec::new();
if memory_analyses.is_empty() {
recommendations.push("No memory analysis data available for recommendations".to_string());
return recommendations;
}
let avg_compression_ratio: f32 = memory_analyses
.iter()
.map(|analysis| analysis.compression_ratio)
.sum::<f32>()
/ memory_analyses.len() as f32;
let format_distribution = get_format_distribution(memory_analyses);
if avg_compression_ratio < 2.0 {
recommendations.push(
"Low compression ratios detected: Consider switching to more memory-efficient sparse formats".to_string()
);
if format_distribution.get(&SparseFormat::Coo).unwrap_or(&0) > &0 {
recommendations.push(
"COO format detected with low compression: Consider CSR/CSC for better memory efficiency".to_string()
);
}
} else if avg_compression_ratio > 10.0 {
recommendations.push(
"Excellent compression ratios achieved: Current format choices are optimal".to_string(),
);
}
let high_overhead_count = memory_analyses
.iter()
.filter(|analysis| analysis.overhead_per_nnz > 16.0)
.count();
if high_overhead_count > 0 {
let percentage = (high_overhead_count * 100) / memory_analyses.len();
recommendations.push(format!(
"High memory overhead detected in {}% of tensors: Consider hybrid sparse formats",
percentage
));
recommendations.push(
"For matrices with mixed sparsity patterns, consider HybridTensor format".to_string(),
);
}
if let Some(budget) = total_memory_budget {
let total_estimated_memory: usize = memory_analyses
.iter()
.map(|analysis| analysis.estimated_memory)
.sum();
let memory_utilization = (total_estimated_memory as f64) / (budget as f64);
if memory_utilization > 0.8 {
recommendations.push(
"Memory utilization approaching budget limit: Consider more aggressive compression"
.to_string(),
);
recommendations.push(
"Recommend enabling memory-efficient storage options or reducing precision"
.to_string(),
);
} else if memory_utilization < 0.3 {
recommendations.push(
"Memory utilization is low: Consider using less compressed formats for better performance".to_string()
);
}
}
for operation in target_operations {
match operation.as_str() {
"matmul" | "matrix_multiplication" => {
recommendations.push(
"For matrix multiplication: CSR format recommended for sparse-dense operations"
.to_string(),
);
}
"transpose" => {
recommendations.push(
"For transpose operations: Consider maintaining both CSR and CSC representations".to_string()
);
}
"element_access" => {
recommendations.push(
"For element access: COO format provides fastest random access".to_string(),
);
}
"reduction" => {
recommendations.push(
"For reduction operations: CSR/CSC formats optimize sequential access patterns"
.to_string(),
);
}
_ => {}
}
}
let sparsity_variance = calculate_sparsity_variance(memory_analyses);
if sparsity_variance > 0.1 {
recommendations.push(
"High sparsity variance detected: Consider adaptive format selection per tensor"
.to_string(),
);
}
let total_memory_usage: usize = memory_analyses.iter().map(|a| a.estimated_memory).sum();
if total_memory_usage > 100 * 1024 * 1024 {
recommendations.push(
"Large memory usage detected: Consider memory pooling for better allocation efficiency"
.to_string(),
);
}
recommendations
}
fn get_format_distribution(memory_analyses: &[MemoryAnalysis]) -> HashMap<SparseFormat, usize> {
let mut distribution = HashMap::new();
for analysis in memory_analyses {
*distribution.entry(analysis.format).or_insert(0) += 1;
}
distribution
}
fn calculate_sparsity_variance(memory_analyses: &[MemoryAnalysis]) -> f32 {
if memory_analyses.len() < 2 {
return 0.0;
}
let sparsity_levels: Vec<f32> = memory_analyses
.iter()
.map(|analysis| {
let total_elements = analysis.matrix_dimensions.0 * analysis.matrix_dimensions.1;
1.0 - (analysis.nnz as f32 / total_elements as f32)
})
.collect();
let mean_sparsity = sparsity_levels.iter().sum::<f32>() / sparsity_levels.len() as f32;
let variance = sparsity_levels
.iter()
.map(|&sparsity| (sparsity - mean_sparsity).powi(2))
.sum::<f32>()
/ sparsity_levels.len() as f32;
variance.sqrt()
}
fn get_current_memory_usage() -> usize {
#[cfg(target_os = "linux")]
{
64 * 1024 * 1024 }
#[cfg(target_os = "macos")]
{
64 * 1024 * 1024 }
#[cfg(target_os = "windows")]
{
64 * 1024 * 1024 }
#[cfg(not(any(target_os = "linux", target_os = "macos", target_os = "windows")))]
{
64 * 1024 * 1024 }
}
#[cfg(test)]
mod tests {
use super::*;
use crate::CooTensor;
use torsh_core::Shape;
fn create_test_sparse_tensor() -> CooTensor {
let row_indices = vec![0, 1, 2, 10, 20, 30, 40, 50, 60, 70];
let col_indices = vec![0, 1, 2, 10, 20, 30, 40, 50, 60, 70];
let values = vec![1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0, 10.0];
let shape = Shape::new(vec![100, 100]);
CooTensor::new(row_indices, col_indices, values, shape).expect("Coo Tensor should succeed")
}
#[test]
fn test_memory_analysis_creation() {
let analysis = MemoryAnalysis::new(SparseFormat::Coo, 100, (1000, 1000));
assert_eq!(analysis.format, SparseFormat::Coo);
assert_eq!(analysis.nnz, 100);
assert_eq!(analysis.matrix_dimensions, (1000, 1000));
assert_eq!(analysis.estimated_memory, 0);
assert_eq!(analysis.compression_ratio, 1.0);
}
#[test]
fn test_memory_analysis_metrics() {
let mut analysis = MemoryAnalysis::new(SparseFormat::Coo, 100, (1000, 1000));
analysis.estimated_memory = 1000;
analysis.dense_memory = 4000000; analysis.compression_ratio =
analysis.dense_memory as f32 / analysis.estimated_memory as f32;
assert_eq!(analysis.compression_ratio, 4000.0);
assert!(analysis.is_memory_efficient());
assert_eq!(analysis.memory_efficiency_rating(), "Excellent");
assert_eq!(analysis.sparsity_ratio(), 0.9999); }
#[test]
fn test_memory_breakdown() {
let mut breakdown = MemoryBreakdown::default();
breakdown.values_memory = 400;
breakdown.indices_memory = 800;
breakdown.structure_memory = 100;
breakdown.metadata_memory = 32;
assert_eq!(breakdown.total_memory(), 1332);
let distribution = breakdown.memory_distribution();
assert!((distribution["values"] - 30.03).abs() < 0.1); assert!((distribution["indices"] - 60.06).abs() < 0.1); }
#[test]
fn test_memory_tracker() {
let mut tracker = MemoryTracker::new();
let result = tracker.track_operation(|| -> TorshResult<i32> {
std::thread::sleep(Duration::from_millis(1));
Ok(42)
});
assert!(result.is_ok());
let (value, usage_result) = result.expect("operation should succeed");
assert_eq!(value, 42);
assert!(usage_result.samples.len() >= 2);
}
#[test]
fn test_memory_usage_result() {
let result = MemoryUsageResult {
baseline_memory: 1000,
peak_memory: 1500,
final_memory: 1200,
memory_delta: 200,
peak_delta: 500,
samples: Vec::new(),
};
assert!(result.has_potential_leak(100)); assert!(!result.has_potential_leak(300));
let efficiency = result.efficiency_score();
assert!((efficiency - 0.4).abs() < 0.01); }
#[test]
fn test_analyze_sparse_memory() {
let sparse_tensor = create_test_sparse_tensor();
let analysis = analyze_sparse_memory(&sparse_tensor);
assert!(analysis.is_ok());
let analysis = analysis.expect("operation should succeed");
assert_eq!(analysis.format, SparseFormat::Coo);
assert_eq!(analysis.nnz, 10);
assert_eq!(analysis.matrix_dimensions, (100, 100));
assert!(analysis.compression_ratio > 1.0);
}
#[test]
fn test_calculate_memory_breakdown_coo() {
let sparse_tensor = create_test_sparse_tensor();
let breakdown = calculate_memory_breakdown(&sparse_tensor);
assert!(breakdown.is_ok());
let breakdown = breakdown.expect("operation should succeed");
assert_eq!(breakdown.values_memory, 40); assert_eq!(breakdown.indices_memory, 80); assert_eq!(breakdown.structure_memory, 0);
assert_eq!(breakdown.metadata_memory, 32);
}
#[test]
fn test_cache_performance_analysis() {
let sparse_tensor = create_test_sparse_tensor();
let result = benchmark_cache_performance(&sparse_tensor, "test_operation".to_string());
assert!(result.is_ok());
let result = result.expect("operation should succeed");
assert_eq!(result.operation, "test_operation");
assert_eq!(result.measurements.len(), 5);
assert!(result.cache_efficiency_score >= 0.0 && result.cache_efficiency_score <= 1.0);
assert!(!result.recommendations.is_empty());
}
#[test]
fn test_memory_access_patterns() {
use MemoryAccessPattern::*;
let sequential = Sequential;
let random = Random;
let strided = Strided { stride: 4 };
let blocked = Blocked { block_size: 64 };
let mixed = Mixed;
assert_eq!(format!("{}", sequential), "Sequential");
assert_eq!(format!("{}", random), "Random");
assert_eq!(format!("{}", strided), "Strided (stride: 4)");
assert_eq!(format!("{}", blocked), "Blocked (block size: 64)");
assert_eq!(format!("{}", mixed), "Mixed");
}
#[test]
fn test_cache_efficiency_calculation() {
let mut measurements = Vec::new();
for i in 0..5 {
let mut measurement = PerformanceMeasurement::new(format!("test_{}", i));
measurement.duration = Duration::from_millis(100); measurements.push(measurement);
}
let efficiency = calculate_cache_efficiency(&measurements);
assert!(efficiency > 0.8);
let mut outlier = PerformanceMeasurement::new("outlier".to_string());
outlier.duration = Duration::from_millis(500); measurements.push(outlier);
let efficiency_with_outlier = calculate_cache_efficiency(&measurements);
assert!(efficiency_with_outlier < efficiency); }
#[test]
fn test_generate_cache_recommendations() {
let recommendations = generate_cache_recommendations(0.3, &MemoryAccessPattern::Random);
assert!(recommendations.len() >= 2);
assert!(recommendations.iter().any(|r| r.contains("cache locality")));
assert!(recommendations
.iter()
.any(|r| r.contains("Random access pattern")));
let recommendations = generate_cache_recommendations(0.8, &MemoryAccessPattern::Sequential);
assert!(recommendations
.iter()
.any(|r| r.contains("prefetching") || r.contains("optimal")));
}
}