use super::config::CompressionAlgorithm;
#[derive(Debug, Default, Clone)]
pub struct CompressionStats {
pub total_blocks: usize,
pub compressed_blocks: usize,
pub total_uncompressed_size: usize,
pub total_compressed_size: usize,
pub compression_ratio: f64,
pub compression_time: f64,
pub decompression_time: f64,
pub cache_hits: usize,
pub cache_misses: usize,
pub out_of_core_reads: usize,
pub out_of_core_writes: usize,
}
#[derive(Debug, Clone)]
pub struct CompressionMetadata {
pub original_size: usize,
pub compressed_size: usize,
pub compression_ratio: f64,
pub compression_time: f64,
}
#[derive(Debug)]
pub struct MemoryStats {
pub total_memory_budget: usize,
pub current_memory_usage: usize,
pub memory_usage_ratio: f64,
pub compression_stats: CompressionStats,
pub cache_hits: usize,
pub cache_misses: usize,
pub cache_hit_ratio: f64,
pub out_of_core_enabled: bool,
}
#[derive(Debug, Clone)]
pub(crate) struct CacheStats {
pub hits: usize,
pub misses: usize,
pub hit_ratio: f64,
pub total_size_bytes: usize,
pub max_size_bytes: usize,
pub utilization: f64,
}
#[derive(Debug)]
pub(crate) struct CompressionStrategy {
pub algorithm: CompressionAlgorithm,
pub block_size: usize,
pub hierarchical: bool,
pub predicted_ratio: f64,
pub actual_ratio: f64,
pub compression_speed: f64,
pub decompression_speed: f64,
}
#[derive(Debug, Default, Clone)]
pub(crate) struct SparsityPatternAnalysis {
pub avg_nnz_per_row: f64,
pub max_nnz_per_row: usize,
pub min_nnz_per_row: usize,
pub sequential_patterns: usize,
pub clustering_factor: f64,
pub bandwidth: usize,
pub diagonal_dominance: f64,
pub fill_ratio: f64,
}
#[derive(Debug, Default, Clone)]
pub(crate) struct AccessPatternInfo {
pub total_accesses: usize,
pub avg_temporal_locality: f64,
pub avg_spatial_locality: f64,
pub pattern_count: usize,
pub sequential_access_ratio: f64,
pub random_access_ratio: f64,
}
#[derive(Debug, Clone)]
pub struct AlgorithmPerformanceMetrics {
pub algorithm: CompressionAlgorithm,
pub avg_compression_ratio: f64,
pub avg_compression_time: f64,
pub avg_decompression_time: f64,
pub success_rate: f64,
pub memory_overhead: f64,
pub sample_count: usize,
}
#[derive(Debug)]
pub struct PerformanceSummary {
pub total_operations: usize,
pub total_compressed_bytes: usize,
pub total_uncompressed_bytes: usize,
pub overall_compression_ratio: f64,
pub cache_performance: CachePerformance,
pub algorithm_metrics: Vec<AlgorithmPerformanceMetrics>,
pub memory_efficiency: MemoryEfficiency,
pub access_patterns: AccessPatternSummary,
}
#[derive(Debug, Clone)]
pub struct CachePerformance {
pub total_requests: usize,
pub cache_hits: usize,
pub cache_misses: usize,
pub hit_rate: f64,
pub avg_response_time_us: f64,
pub eviction_count: usize,
pub prefetch_accuracy: f64,
}
#[derive(Debug, Clone)]
pub struct MemoryEfficiency {
pub memory_budget_utilization: f64,
pub compression_space_savings: f64,
pub fragmentation_ratio: f64,
pub out_of_core_usage: f64,
pub memory_pressure_events: usize,
}
#[derive(Debug, Clone)]
pub struct AccessPatternSummary {
pub predominant_pattern: AccessPatternType,
pub temporal_locality_score: f64,
pub spatial_locality_score: f64,
pub predictability_score: f64,
pub hotspot_concentration: f64,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
pub enum AccessPatternType {
Sequential,
Random,
Clustered,
Mixed,
#[default]
Unknown,
}
impl CompressionStats {
pub fn new() -> Self {
Self::default()
}
pub fn update_compression(
&mut self,
original_size: usize,
compressed_size: usize,
compression_time: f64,
) {
self.total_blocks += 1;
self.compressed_blocks += 1;
self.total_uncompressed_size += original_size;
self.total_compressed_size += compressed_size;
self.compression_time += compression_time;
self.compression_ratio = if self.total_uncompressed_size > 0 {
self.total_compressed_size as f64 / self.total_uncompressed_size as f64
} else {
1.0
};
}
pub fn update_decompression(&mut self, decompression_time: f64) {
self.decompression_time += decompression_time;
}
pub fn record_cache_hit(&mut self) {
self.cache_hits += 1;
}
pub fn record_cache_miss(&mut self) {
self.cache_misses += 1;
}
pub fn record_out_of_core_read(&mut self) {
self.out_of_core_reads += 1;
}
pub fn record_out_of_core_write(&mut self) {
self.out_of_core_writes += 1;
}
pub fn cache_hit_ratio(&self) -> f64 {
let total_accesses = self.cache_hits + self.cache_misses;
if total_accesses > 0 {
self.cache_hits as f64 / total_accesses as f64
} else {
0.0
}
}
pub fn avg_compression_time(&self) -> f64 {
if self.compressed_blocks > 0 {
self.compression_time / self.compressed_blocks as f64
} else {
0.0
}
}
pub fn avg_decompression_time(&self) -> f64 {
if self.compressed_blocks > 0 {
self.decompression_time / self.compressed_blocks as f64
} else {
0.0
}
}
pub fn compression_efficiency(&self) -> f64 {
let avg_time = self.avg_compression_time();
if avg_time > 0.0 {
1.0 / avg_time
} else {
0.0
}
}
pub fn space_savings(&self) -> usize {
self.total_uncompressed_size
.saturating_sub(self.total_compressed_size)
}
pub fn space_savings_ratio(&self) -> f64 {
if self.total_uncompressed_size > 0 {
self.space_savings() as f64 / self.total_uncompressed_size as f64
} else {
0.0
}
}
pub fn reset(&mut self) {
*self = Self::default();
}
pub fn merge(&mut self, other: &CompressionStats) {
self.total_blocks += other.total_blocks;
self.compressed_blocks += other.compressed_blocks;
self.total_uncompressed_size += other.total_uncompressed_size;
self.total_compressed_size += other.total_compressed_size;
self.compression_time += other.compression_time;
self.decompression_time += other.decompression_time;
self.cache_hits += other.cache_hits;
self.cache_misses += other.cache_misses;
self.out_of_core_reads += other.out_of_core_reads;
self.out_of_core_writes += other.out_of_core_writes;
self.compression_ratio = if self.total_uncompressed_size > 0 {
self.total_compressed_size as f64 / self.total_uncompressed_size as f64
} else {
1.0
};
}
}
impl CompressionMetadata {
pub fn new(original_size: usize, compressed_size: usize, compression_time: f64) -> Self {
let compression_ratio = if original_size > 0 {
compressed_size as f64 / original_size as f64
} else {
1.0
};
Self {
original_size,
compressed_size,
compression_ratio,
compression_time,
}
}
pub fn space_savings(&self) -> usize {
self.original_size.saturating_sub(self.compressed_size)
}
pub fn space_savings_ratio(&self) -> f64 {
if self.original_size > 0 {
self.space_savings() as f64 / self.original_size as f64
} else {
0.0
}
}
pub fn compression_speed(&self) -> f64 {
if self.compression_time > 0.0 {
self.original_size as f64 / self.compression_time
} else {
0.0
}
}
pub fn is_effective(&self, threshold: f64) -> bool {
self.compression_ratio < threshold
}
}
impl MemoryStats {
pub fn new(memory_budget: usize, out_of_core_enabled: bool) -> Self {
Self {
total_memory_budget: memory_budget,
current_memory_usage: 0,
memory_usage_ratio: 0.0,
compression_stats: CompressionStats::default(),
cache_hits: 0,
cache_misses: 0,
cache_hit_ratio: 0.0,
out_of_core_enabled,
}
}
pub fn update_memory_usage(&mut self, current_usage: usize) {
self.current_memory_usage = current_usage;
self.memory_usage_ratio = if self.total_memory_budget > 0 {
current_usage as f64 / self.total_memory_budget as f64
} else {
0.0
};
}
pub fn update_cache_stats(&mut self, hits: usize, misses: usize) {
self.cache_hits = hits;
self.cache_misses = misses;
let total = hits + misses;
self.cache_hit_ratio = if total > 0 {
hits as f64 / total as f64
} else {
0.0
};
}
pub fn has_memory_pressure(&self, threshold: f64) -> bool {
self.memory_usage_ratio > threshold
}
pub fn available_memory(&self) -> usize {
self.total_memory_budget
.saturating_sub(self.current_memory_usage)
}
pub fn memory_efficiency_score(&self) -> f64 {
let compression_benefit = self.compression_stats.space_savings_ratio();
let cache_benefit = self.cache_hit_ratio;
let usage_efficiency = 1.0 - self.memory_usage_ratio.min(1.0);
(compression_benefit + cache_benefit + usage_efficiency) / 3.0
}
}
impl SparsityPatternAnalysis {
pub fn new() -> Self {
Self::default()
}
pub fn analyze_pattern(
&mut self,
rows: usize,
cols: usize,
indptr: &[usize],
indices: &[usize],
) {
let nnz = indices.len();
self.fill_ratio = nnz as f64 / (rows * cols) as f64;
let mut nnz_per_row = Vec::new();
for i in 0..rows {
if i + 1 < indptr.len() {
let row_nnz = indptr[i + 1] - indptr[i];
nnz_per_row.push(row_nnz);
}
}
if !nnz_per_row.is_empty() {
self.avg_nnz_per_row =
nnz_per_row.iter().sum::<usize>() as f64 / nnz_per_row.len() as f64;
self.max_nnz_per_row = *nnz_per_row.iter().max().expect("Operation failed");
self.min_nnz_per_row = *nnz_per_row.iter().min().expect("Operation failed");
}
if !indices.is_empty() {
let min_col = *indices.iter().min().expect("Operation failed");
let max_col = *indices.iter().max().expect("Operation failed");
self.bandwidth = max_col - min_col + 1;
}
self.sequential_patterns = self.count_sequential_patterns(indptr, indices);
self.clustering_factor = self.calculate_clustering_factor(indptr, indices);
self.diagonal_dominance = self.calculate_diagonal_dominance(rows, indptr, indices);
}
fn count_sequential_patterns(&self, indptr: &[usize], indices: &[usize]) -> usize {
let mut sequential_count = 0;
for i in 0..indptr.len().saturating_sub(1) {
let start = indptr[i];
let end = indptr[i + 1];
if end > start + 1 {
let row_indices = &indices[start..end];
let mut is_sequential = true;
for j in 1..row_indices.len() {
if row_indices[j] != row_indices[j - 1] + 1 {
is_sequential = false;
break;
}
}
if is_sequential {
sequential_count += 1;
}
}
}
sequential_count
}
fn calculate_clustering_factor(&self, indptr: &[usize], indices: &[usize]) -> f64 {
if indices.is_empty() {
return 0.0;
}
let mut cluster_score = 0.0;
let mut total_comparisons = 0;
for i in 0..indptr.len().saturating_sub(1) {
let start = indptr[i];
let end = indptr[i + 1];
if end > start + 1 {
let row_indices = &indices[start..end];
for j in 1..row_indices.len() {
let distance = row_indices[j].saturating_sub(row_indices[j - 1]);
if distance <= 5 {
cluster_score += 1.0;
}
total_comparisons += 1;
}
}
}
if total_comparisons > 0 {
cluster_score / total_comparisons as f64
} else {
0.0
}
}
fn calculate_diagonal_dominance(
&self,
rows: usize,
indptr: &[usize],
indices: &[usize],
) -> f64 {
let mut diagonal_elements = 0;
for i in 0..rows.min(indptr.len().saturating_sub(1)) {
let start = indptr[i];
let end = indptr[i + 1];
for j in start..end {
if j < indices.len() && indices[j] == i {
diagonal_elements += 1;
break;
}
}
}
diagonal_elements as f64 / rows as f64
}
pub fn pattern_type(&self) -> AccessPatternType {
if self.sequential_patterns as f64 / (self.avg_nnz_per_row + 1.0) > 0.7 {
AccessPatternType::Sequential
} else if self.clustering_factor > 0.6 {
AccessPatternType::Clustered
} else if self.diagonal_dominance > 0.8 {
AccessPatternType::Sequential } else {
AccessPatternType::Mixed
}
}
}
impl AccessPatternInfo {
pub fn new() -> Self {
Self::default()
}
pub fn update(&mut self, temporal_locality: f64, spatial_locality: f64, is_sequential: bool) {
self.total_accesses += 1;
let n = self.total_accesses as f64;
self.avg_temporal_locality =
((n - 1.0) * self.avg_temporal_locality + temporal_locality) / n;
self.avg_spatial_locality = ((n - 1.0) * self.avg_spatial_locality + spatial_locality) / n;
if is_sequential {
self.sequential_access_ratio = ((n - 1.0) * self.sequential_access_ratio + 1.0) / n;
} else {
self.sequential_access_ratio = ((n - 1.0) * self.sequential_access_ratio) / n;
self.random_access_ratio = 1.0 - self.sequential_access_ratio;
}
self.pattern_count += 1;
}
pub fn locality_score(&self) -> f64 {
(self.avg_temporal_locality + self.avg_spatial_locality) / 2.0
}
pub fn predominant_pattern(&self) -> AccessPatternType {
if self.sequential_access_ratio > 0.7 {
AccessPatternType::Sequential
} else if self.random_access_ratio > 0.7 {
AccessPatternType::Random
} else if self.locality_score() > 0.6 {
AccessPatternType::Clustered
} else {
AccessPatternType::Mixed
}
}
}
impl std::fmt::Display for AccessPatternType {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
AccessPatternType::Sequential => write!(f, "Sequential"),
AccessPatternType::Random => write!(f, "Random"),
AccessPatternType::Clustered => write!(f, "Clustered"),
AccessPatternType::Mixed => write!(f, "Mixed"),
AccessPatternType::Unknown => write!(f, "Unknown"),
}
}
}