use std::collections::{HashMap, BTreeMap, VecDeque};
use std::time::{Instant, Duration, SystemTime, UNIX_EPOCH};
use scirs2_core::error::{CoreError, Result};
use serde::{Serialize, Deserialize};
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct MemoryStatistics {
pub total_allocations: usize,
pub total_deallocations: usize,
pub peak_memory_usage: usize,
pub average_memory_usage: f64,
pub current_memory_usage: usize,
pub memory_churn_rate: f64,
pub allocation_rate: f64,
pub deallocation_rate: f64,
pub fragmentation_index: f64,
pub efficiency_score: f64,
pub cache_hit_ratio: f64,
pub bandwidth_utilization: f64,
pub pressure_incidents: usize,
pub optimization_opportunities: usize,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct HistoricalDataPoint {
pub timestamp: SystemTime,
pub statistics: MemoryStatistics,
pub context: AnalyticsContext,
pub custom_metrics: HashMap<String, f64>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct AnalyticsContext {
pub session_id: String,
pub workload_type: String,
pub model_architecture: Option<String>,
pub batch_size: Option<usize>,
pub device_info: DeviceInfo,
pub environment: EnvironmentInfo,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct DeviceInfo {
pub device_type: String,
pub total_memory: usize,
pub compute_capability: Option<String>,
pub driver_version: Option<String>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct EnvironmentInfo {
pub framework_version: String,
pub rust_version: String,
pub optimization_level: String,
pub feature_flags: Vec<String>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct PerformanceBaseline {
pub name: String,
pub created_at: SystemTime,
pub reference_statistics: MemoryStatistics,
pub confidence_interval: ConfidenceInterval,
pub sample_size: usize,
pub measurement_duration: Duration,
pub conditions: BaselineConditions,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ConfidenceInterval {
pub confidence_level: f64,
pub lower_bound: f64,
pub upper_bound: f64,
pub margin_of_error: f64,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct BaselineConditions {
pub workload_description: String,
pub hardware_configuration: String,
pub software_environment: String,
pub expected_variance: f64,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct TrendAnalysis {
pub metric_name: String,
pub trend_direction: TrendDirection,
pub trend_strength: f64,
pub slope: f64,
pub correlation_coefficient: f64,
pub statistical_significance: f64,
pub prediction: TrendPrediction,
pub analysis_period: Duration,
pub data_points: usize,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub enum TrendDirection {
Increasing,
Decreasing,
Stable,
Volatile,
Cyclical,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct TrendPrediction {
pub projected_value: f64,
pub confidence: f64,
pub time_horizon: Duration,
pub factors: Vec<String>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct AnomalyDetection {
pub anomaly_type: AnomalyType,
pub severity: AnomalySeverity,
pub detected_at: SystemTime,
pub affected_metrics: Vec<String>,
pub anomaly_score: f64,
pub threshold_value: f64,
pub actual_value: f64,
pub description: String,
pub recommended_actions: Vec<String>,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub enum AnomalyType {
SuddenSpike,
SuddenDrop,
GradualIncrease,
GradualDecrease,
Oscillation,
Flatline,
OutOfBounds,
PatternBreak,
}
#[derive(Debug, Clone, PartialEq, PartialOrd, Serialize, Deserialize)]
pub enum AnomalySeverity {
Critical,
High,
Medium,
Low,
Informational,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct AnalyticsReport {
pub report_id: String,
pub generated_at: SystemTime,
pub period: ReportPeriod,
pub summary: ReportSummary,
pub detailed_statistics: MemoryStatistics,
pub trend_analyses: Vec<TrendAnalysis>,
pub anomalies: Vec<AnomalyDetection>,
pub performance_comparison: Option<PerformanceComparison>,
pub recommendations: Vec<AnalyticsRecommendation>,
pub visualizations: Vec<VisualizationData>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ReportPeriod {
pub start_time: SystemTime,
pub end_time: SystemTime,
pub duration: Duration,
pub data_points: usize,
pub sampling_rate: Duration,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ReportSummary {
pub key_findings: Vec<String>,
pub performance_highlights: Vec<String>,
pub areas_of_concern: Vec<String>,
pub overall_health_score: f64,
pub change_from_baseline: f64,
pub notable_patterns: Vec<String>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct PerformanceComparison {
pub baseline_name: String,
pub current_vs_baseline: HashMap<String, ComparisonResult>,
pub overall_improvement: f64,
pub regression_areas: Vec<String>,
pub improvement_areas: Vec<String>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ComparisonResult {
pub current_value: f64,
pub baseline_value: f64,
pub percentage_change: f64,
pub statistical_significance: f64,
pub interpretation: String,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct AnalyticsRecommendation {
pub priority: RecommendationPriority,
pub category: RecommendationCategory,
pub title: String,
pub description: String,
pub supporting_evidence: Vec<String>,
pub expected_impact: ImpactEstimate,
pub implementation_effort: EffortEstimate,
pub timeline: Duration,
}
#[derive(Debug, Clone, PartialEq, PartialOrd, Serialize, Deserialize)]
pub enum RecommendationPriority {
Immediate,
High,
Medium,
Low,
Planning,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub enum RecommendationCategory {
PerformanceOptimization,
ResourceManagement,
InfrastructureUpgrade,
ConfigurationTuning,
MonitoringImprovement,
CapacityPlanning,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ImpactEstimate {
pub performance_improvement: f64,
pub cost_reduction: f64,
pub risk_mitigation: f64,
pub confidence_level: f64,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct EffortEstimate {
pub development_time: Duration,
pub complexity_score: f64,
pub required_expertise: Vec<String>,
pub dependencies: Vec<String>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct VisualizationData {
pub chart_type: ChartType,
pub title: String,
pub x_axis: AxisDefinition,
pub y_axis: AxisDefinition,
pub data_series: Vec<DataSeries>,
pub annotations: Vec<ChartAnnotation>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub enum ChartType {
TimeSeries,
Histogram,
ScatterPlot,
Heatmap,
BarChart,
PieChart,
BoxPlot,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct AxisDefinition {
pub label: String,
pub unit: String,
pub scale: ScaleType,
pub range: Option<(f64, f64)>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub enum ScaleType {
Linear,
Logarithmic,
Categorical,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct DataSeries {
pub name: String,
pub data_points: Vec<(f64, f64)>,
pub style: SeriesStyle,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct SeriesStyle {
pub color: String,
pub line_style: LineStyle,
pub marker_style: MarkerStyle,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub enum LineStyle {
Solid,
Dashed,
Dotted,
None,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub enum MarkerStyle {
Circle,
Square,
Triangle,
Diamond,
None,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ChartAnnotation {
pub annotation_type: AnnotationType,
pub position: (f64, f64),
pub text: String,
pub style: AnnotationStyle,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub enum AnnotationType {
Point,
Line,
Rectangle,
Text,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct AnnotationStyle {
pub color: String,
pub font_size: f64,
pub transparency: f64,
}
pub struct MemoryAnalytics {
historical_data: VecDeque<HistoricalDataPoint>,
baselines: HashMap<String, PerformanceBaseline>,
trend_analyses: HashMap<String, TrendAnalysis>,
anomaly_detectors: HashMap<String, AnomalyDetector>,
custom_metrics: HashMap<String, Vec<f64>>,
configuration: AnalyticsConfiguration,
current_statistics: Option<MemoryStatistics>,
}
#[derive(Debug, Clone)]
pub struct AnalyticsConfiguration {
pub max_history_size: usize,
pub retention_period: Duration,
pub sampling_interval: Duration,
pub anomaly_sensitivity: f64,
pub trend_analysis_window: Duration,
pub statistical_confidence: f64,
pub enable_prediction: bool,
pub enable_auto_baseline: bool,
}
impl Default for AnalyticsConfiguration {
fn default() -> Self {
Self {
max_history_size: 10000,
retention_period: Duration::from_secs(30 * 24 * 60 * 60),
sampling_interval: Duration::from_secs(60),
anomaly_sensitivity: 0.95,
trend_analysis_window: Duration::from_secs(7 * 24 * 60 * 60),
statistical_confidence: 0.95,
enable_prediction: true,
enable_auto_baseline: true,
}
}
}
pub struct AnomalyDetector {
metric_name: String,
baseline_mean: f64,
baseline_std: f64,
sensitivity: f64,
history: VecDeque<f64>,
thresholds: AnomalyThresholds,
}
#[derive(Debug, Clone)]
pub struct AnomalyThresholds {
pub critical_upper: f64,
pub critical_lower: f64,
pub warning_upper: f64,
pub warning_lower: f64,
pub change_rate_threshold: f64,
}
impl Duration {
fn from_days(days: u64) -> Self {
Duration::from_secs(days * 24 * 3600)
}
}
impl MemoryAnalytics {
pub fn new() -> Self {
Self::with_config(AnalyticsConfiguration::default())
}
pub fn with_config(config: AnalyticsConfiguration) -> Self {
Self {
historical_data: VecDeque::new(),
baselines: HashMap::new(),
trend_analyses: HashMap::new(),
anomaly_detectors: HashMap::new(),
custom_metrics: HashMap::new(),
configuration: config,
current_statistics: None,
}
}
pub fn record_statistics(&mut self, statistics: MemoryStatistics, context: AnalyticsContext) -> Result<()> {
let data_point = HistoricalDataPoint {
timestamp: SystemTime::now(),
statistics: statistics.clone(),
context,
custom_metrics: HashMap::new(),
};
self.historical_data.push_back(data_point);
self.current_statistics = Some(statistics);
if self.historical_data.len() > self.configuration.max_history_size {
self.historical_data.pop_front();
}
self.clean_old_data();
self.update_anomaly_detectors()?;
if self.configuration.enable_auto_baseline {
self.update_auto_baselines()?;
}
Ok(())
}
pub fn create_baseline(
&mut self,
name: String,
description: String,
sample_duration: Duration,
) -> Result<()> {
let recent_data = self.get_recent_data(sample_duration);
if recent_data.is_empty() {
return Err(CoreError::InvalidOperation("Insufficient data for baseline creation".to_string()));
}
let statistics = self.calculate_aggregate_statistics(&recent_data);
let confidence_interval = self.calculate_confidence_interval(&recent_data, 0.95);
let baseline = PerformanceBaseline {
name: name.clone(),
created_at: SystemTime::now(),
reference_statistics: statistics,
confidence_interval,
sample_size: recent_data.len(),
measurement_duration: sample_duration,
conditions: BaselineConditions {
workload_description: "Auto-generated baseline".to_string(),
hardware_configuration: "Current configuration".to_string(),
software_environment: "Current environment".to_string(),
expected_variance: 0.1,
},
};
self.baselines.insert(name, baseline);
Ok(())
}
pub fn analyze_trends(&mut self, analysis_period: Duration) -> Result<Vec<TrendAnalysis>> {
let data = self.get_recent_data(analysis_period);
if data.len() < 10 {
return Ok(vec![]); }
let mut analyses = Vec::new();
let metrics = vec![
("peak_memory_usage", |s: &MemoryStatistics| s.peak_memory_usage as f64),
("average_memory_usage", |s: &MemoryStatistics| s.average_memory_usage),
("allocation_rate", |s: &MemoryStatistics| s.allocation_rate),
("fragmentation_index", |s: &MemoryStatistics| s.fragmentation_index),
("efficiency_score", |s: &MemoryStatistics| s.efficiency_score),
("cache_hit_ratio", |s: &MemoryStatistics| s.cache_hit_ratio),
];
for (metric_name, extractor) in metrics {
let values: Vec<f64> = data.iter().map(|d| extractor(&d.statistics)).collect();
let trend = self.calculate_trend(&values, metric_name.to_string(), analysis_period)?;
analyses.push(trend);
}
for analysis in &analyses {
self.trend_analyses.insert(analysis.metric_name.clone(), analysis.clone());
}
Ok(analyses)
}
pub fn detect_anomalies(&mut self) -> Result<Vec<AnomalyDetection>> {
let mut anomalies = Vec::new();
for (metric_name, detector) in &mut self.anomaly_detectors {
if let Some(current_stats) = &self.current_statistics {
let current_value = self.extract_metric_value(current_stats, metric_name);
if let Some(anomaly) = detector.check_anomaly(current_value)? {
anomalies.push(anomaly);
}
}
}
anomalies.sort_by(|a, b| b.severity.partial_cmp(&a.severity).unwrap_or(std::cmp::Ordering::Equal));
Ok(anomalies)
}
pub fn generate_report(&mut self, period: Duration) -> Result<AnalyticsReport> {
let start_time = SystemTime::now() - period;
let end_time = SystemTime::now();
let data = self.get_recent_data(period);
if data.is_empty() {
return Err(CoreError::InvalidOperation("No data available for report generation".to_string()));
}
let detailed_statistics = self.calculate_aggregate_statistics(&data);
let trend_analyses = self.analyze_trends(period)?;
let anomalies = self.detect_anomalies()?;
let summary = self.generate_summary(&detailed_statistics, &trend_analyses, &anomalies);
let performance_comparison = self.compare_with_baseline(&detailed_statistics);
let recommendations = self.generate_recommendations(&detailed_statistics, &trend_analyses, &anomalies)?;
let visualizations = self.prepare_visualizations(&data, &trend_analyses);
let report = AnalyticsReport {
report_id: format!("report_{}", SystemTime::now().duration_since(UNIX_EPOCH).expect("time should be after UNIX_EPOCH").as_secs()),
generated_at: SystemTime::now(),
period: ReportPeriod {
start_time,
end_time,
duration: period,
data_points: data.len(),
sampling_rate: self.configuration.sampling_interval,
},
summary,
detailed_statistics,
trend_analyses,
anomalies,
performance_comparison,
recommendations,
visualizations,
};
Ok(report)
}
fn get_recent_data(&self, duration: Duration) -> Vec<&HistoricalDataPoint> {
let cutoff = SystemTime::now() - duration;
self.historical_data
.iter()
.filter(|data| data.timestamp >= cutoff)
.collect()
}
fn calculate_aggregate_statistics(&self, data: &[&HistoricalDataPoint]) -> MemoryStatistics {
if data.is_empty() {
return MemoryStatistics {
total_allocations: 0,
total_deallocations: 0,
peak_memory_usage: 0,
average_memory_usage: 0.0,
current_memory_usage: 0,
memory_churn_rate: 0.0,
allocation_rate: 0.0,
deallocation_rate: 0.0,
fragmentation_index: 0.0,
efficiency_score: 0.0,
cache_hit_ratio: 0.0,
bandwidth_utilization: 0.0,
pressure_incidents: 0,
optimization_opportunities: 0,
};
}
let count = data.len() as f64;
MemoryStatistics {
total_allocations: data.iter().map(|d| d.statistics.total_allocations).sum(),
total_deallocations: data.iter().map(|d| d.statistics.total_deallocations).sum(),
peak_memory_usage: data.iter().map(|d| d.statistics.peak_memory_usage).max().unwrap_or(0),
average_memory_usage: data.iter().map(|d| d.statistics.average_memory_usage).sum::<f64>() / count,
current_memory_usage: data.last().map(|d| d.statistics.current_memory_usage).unwrap_or(0),
memory_churn_rate: data.iter().map(|d| d.statistics.memory_churn_rate).sum::<f64>() / count,
allocation_rate: data.iter().map(|d| d.statistics.allocation_rate).sum::<f64>() / count,
deallocation_rate: data.iter().map(|d| d.statistics.deallocation_rate).sum::<f64>() / count,
fragmentation_index: data.iter().map(|d| d.statistics.fragmentation_index).sum::<f64>() / count,
efficiency_score: data.iter().map(|d| d.statistics.efficiency_score).sum::<f64>() / count,
cache_hit_ratio: data.iter().map(|d| d.statistics.cache_hit_ratio).sum::<f64>() / count,
bandwidth_utilization: data.iter().map(|d| d.statistics.bandwidth_utilization).sum::<f64>() / count,
pressure_incidents: data.iter().map(|d| d.statistics.pressure_incidents).sum(),
optimization_opportunities: data.iter().map(|d| d.statistics.optimization_opportunities).sum(),
}
}
fn calculate_confidence_interval(&self, data: &[&HistoricalDataPoint], confidence_level: f64) -> ConfidenceInterval {
let values: Vec<f64> = data.iter().map(|d| d.statistics.efficiency_score).collect();
if values.is_empty() {
return ConfidenceInterval {
confidence_level,
lower_bound: 0.0,
upper_bound: 0.0,
margin_of_error: 0.0,
};
}
let mean = values.iter().sum::<f64>() / values.len() as f64;
let variance = values.iter().map(|&x| (x - mean).powi(2)).sum::<f64>() / values.len() as f64;
let std_dev = variance.sqrt();
let std_error = std_dev / (values.len() as f64).sqrt();
let t_critical = 1.96; let margin_of_error = t_critical * std_error;
ConfidenceInterval {
confidence_level,
lower_bound: mean - margin_of_error,
upper_bound: mean + margin_of_error,
margin_of_error,
}
}
fn calculate_trend(&self, values: &[f64], metric_name: String, period: Duration) -> Result<TrendAnalysis> {
if values.len() < 3 {
return Err(CoreError::InvalidOperation("Insufficient data for trend analysis".to_string()));
}
let n = values.len() as f64;
let x_values: Vec<f64> = (0..values.len()).map(|i| i as f64).collect();
let x_mean = x_values.iter().sum::<f64>() / n;
let y_mean = values.iter().sum::<f64>() / n;
let mut numerator = 0.0;
let mut denominator = 0.0;
for i in 0..values.len() {
let x_diff = x_values[i] - x_mean;
let y_diff = values[i] - y_mean;
numerator += x_diff * y_diff;
denominator += x_diff * x_diff;
}
let slope = if denominator != 0.0 { numerator / denominator } else { 0.0 };
let correlation = if denominator > 0.0 {
let y_variance = values.iter().map(|&y| (y - y_mean).powi(2)).sum::<f64>();
numerator / (denominator * y_variance).sqrt()
} else {
0.0
};
let trend_direction = if slope.abs() < 0.001 {
TrendDirection::Stable
} else if slope > 0.0 {
TrendDirection::Increasing
} else {
TrendDirection::Decreasing
};
let trend_strength = correlation.abs();
let prediction = TrendPrediction {
projected_value: y_mean + slope * n,
confidence: trend_strength,
time_horizon: Duration::from_secs(3600), factors: vec!["Linear trend extrapolation".to_string()],
};
Ok(TrendAnalysis {
metric_name,
trend_direction,
trend_strength,
slope,
correlation_coefficient: correlation,
statistical_significance: trend_strength, prediction,
analysis_period: period,
data_points: values.len(),
})
}
fn clean_old_data(&mut self) {
let cutoff = SystemTime::now() - self.configuration.retention_period;
while let Some(front) = self.historical_data.front() {
if front.timestamp < cutoff {
self.historical_data.pop_front();
} else {
break;
}
}
}
fn update_anomaly_detectors(&mut self) -> Result<()> {
if let Some(stats) = &self.current_statistics {
let key_metrics = vec![
"peak_memory_usage",
"allocation_rate",
"fragmentation_index",
"efficiency_score",
"cache_hit_ratio",
];
for metric in key_metrics {
if !self.anomaly_detectors.contains_key(metric) {
let detector = AnomalyDetector::new(
metric.to_string(),
self.configuration.anomaly_sensitivity,
);
self.anomaly_detectors.insert(metric.to_string(), detector);
}
if let Some(detector) = self.anomaly_detectors.get_mut(metric) {
let value = self.extract_metric_value(stats, metric);
detector.update(value);
}
}
}
Ok(())
}
fn extract_metric_value(&self, stats: &MemoryStatistics, metric_name: &str) -> f64 {
match metric_name {
"peak_memory_usage" => stats.peak_memory_usage as f64,
"allocation_rate" => stats.allocation_rate,
"fragmentation_index" => stats.fragmentation_index,
"efficiency_score" => stats.efficiency_score,
"cache_hit_ratio" => stats.cache_hit_ratio,
"bandwidth_utilization" => stats.bandwidth_utilization,
_ => 0.0,
}
}
fn update_auto_baselines(&mut self) -> Result<()> {
if self.historical_data.len() > 1000 {
let week_duration = Duration::from_secs(7 * 24 * 60 * 60);
if !self.baselines.contains_key("auto_weekly") {
self.create_baseline("auto_weekly".to_string(), "Automatic weekly baseline".to_string(), week_duration)?;
}
}
Ok(())
}
fn generate_summary(
&self,
statistics: &MemoryStatistics,
trends: &[TrendAnalysis],
anomalies: &[AnomalyDetection],
) -> ReportSummary {
let mut key_findings = Vec::new();
let mut performance_highlights = Vec::new();
let mut areas_of_concern = Vec::new();
let health_score = (statistics.efficiency_score + statistics.cache_hit_ratio) / 2.0;
if health_score > 0.8 {
performance_highlights.push("Excellent overall memory performance".to_string());
} else if health_score > 0.6 {
key_findings.push("Good memory performance with room for optimization".to_string());
} else {
areas_of_concern.push("Memory performance below optimal levels".to_string());
}
for trend in trends {
if trend.trend_direction == TrendDirection::Increasing && trend.metric_name == "fragmentation_index" {
areas_of_concern.push("Increasing memory fragmentation detected".to_string());
} else if trend.trend_direction == TrendDirection::Increasing && trend.metric_name == "efficiency_score" {
performance_highlights.push("Memory efficiency showing positive trend".to_string());
}
}
let critical_anomalies = anomalies.iter().filter(|a| a.severity == AnomalySeverity::Critical).count();
if critical_anomalies > 0 {
areas_of_concern.push(format!("{} critical anomalies require immediate attention", critical_anomalies));
}
ReportSummary {
key_findings,
performance_highlights,
areas_of_concern,
overall_health_score: health_score,
change_from_baseline: 0.0, notable_patterns: vec![], }
}
fn compare_with_baseline(&self, statistics: &MemoryStatistics) -> Option<PerformanceComparison> {
if let Some(baseline) = self.baselines.get("auto_weekly") {
let mut comparisons = HashMap::new();
let metrics = vec![
("efficiency_score", statistics.efficiency_score, baseline.reference_statistics.efficiency_score),
("cache_hit_ratio", statistics.cache_hit_ratio, baseline.reference_statistics.cache_hit_ratio),
("fragmentation_index", statistics.fragmentation_index, baseline.reference_statistics.fragmentation_index),
];
for (metric_name, current, baseline_val) in metrics {
let percentage_change = ((current - baseline_val) / baseline_val) * 100.0;
comparisons.insert(metric_name.to_string(), ComparisonResult {
current_value: current,
baseline_value: baseline_val,
percentage_change,
statistical_significance: 0.95, interpretation: if percentage_change > 5.0 {
"Significant improvement".to_string()
} else if percentage_change < -5.0 {
"Significant regression".to_string()
} else {
"No significant change".to_string()
},
});
}
Some(PerformanceComparison {
baseline_name: "auto_weekly".to_string(),
current_vs_baseline: comparisons,
overall_improvement: 0.0, regression_areas: vec![],
improvement_areas: vec![],
})
} else {
None
}
}
fn generate_recommendations(
&self,
_statistics: &MemoryStatistics,
trends: &[TrendAnalysis],
anomalies: &[AnomalyDetection],
) -> Result<Vec<AnalyticsRecommendation>> {
let mut recommendations = Vec::new();
for trend in trends {
if trend.trend_direction == TrendDirection::Increasing && trend.metric_name == "fragmentation_index" {
recommendations.push(AnalyticsRecommendation {
priority: RecommendationPriority::High,
category: RecommendationCategory::PerformanceOptimization,
title: "Address increasing memory fragmentation".to_string(),
description: "Memory fragmentation is trending upward, which may lead to performance degradation".to_string(),
supporting_evidence: vec![format!("Fragmentation index increased by {:.2}% over analysis period", trend.slope * 100.0)],
expected_impact: ImpactEstimate {
performance_improvement: 15.0,
cost_reduction: 5.0,
risk_mitigation: 20.0,
confidence_level: trend.trend_strength,
},
implementation_effort: EffortEstimate {
development_time: Duration::from_secs(3 * 24 * 60 * 60),
complexity_score: 6.0,
required_expertise: vec!["Memory management".to_string()],
dependencies: vec![],
},
timeline: Duration::from_secs(7 * 24 * 60 * 60),
});
}
}
for anomaly in anomalies {
if anomaly.severity >= AnomalySeverity::High {
recommendations.push(AnalyticsRecommendation {
priority: RecommendationPriority::Immediate,
category: RecommendationCategory::ResourceManagement,
title: format!("Investigate {} anomaly", anomaly.anomaly_type as u8),
description: anomaly.description.clone(),
supporting_evidence: anomaly.recommended_actions.clone(),
expected_impact: ImpactEstimate {
performance_improvement: 25.0,
cost_reduction: 0.0,
risk_mitigation: 40.0,
confidence_level: anomaly.anomaly_score,
},
implementation_effort: EffortEstimate {
development_time: Duration::from_secs(4 * 60 * 60),
complexity_score: 3.0,
required_expertise: vec!["System monitoring".to_string()],
dependencies: vec![],
},
timeline: Duration::from_secs(24 * 60 * 60),
});
}
}
Ok(recommendations)
}
fn prepare_visualizations(&self, data: &[&HistoricalDataPoint], trends: &[TrendAnalysis]) -> Vec<VisualizationData> {
let mut visualizations = Vec::new();
let memory_data: Vec<(f64, f64)> = data.iter().enumerate()
.map(|(i, d)| (i as f64, d.statistics.current_memory_usage as f64))
.collect();
visualizations.push(VisualizationData {
chart_type: ChartType::TimeSeries,
title: "Memory Usage Over Time".to_string(),
x_axis: AxisDefinition {
label: "Time".to_string(),
unit: "hours".to_string(),
scale: ScaleType::Linear,
range: None,
},
y_axis: AxisDefinition {
label: "Memory Usage".to_string(),
unit: "bytes".to_string(),
scale: ScaleType::Linear,
range: None,
},
data_series: vec![DataSeries {
name: "Memory Usage".to_string(),
data_points: memory_data,
style: SeriesStyle {
color: "#1f77b4".to_string(),
line_style: LineStyle::Solid,
marker_style: MarkerStyle::Circle,
},
}],
annotations: vec![],
});
for trend in trends {
if trend.trend_strength > 0.5 {
visualizations.push(VisualizationData {
chart_type: ChartType::TimeSeries,
title: format!("Trend: {}", trend.metric_name),
x_axis: AxisDefinition {
label: "Time".to_string(),
unit: "period".to_string(),
scale: ScaleType::Linear,
range: None,
},
y_axis: AxisDefinition {
label: trend.metric_name.clone(),
unit: "value".to_string(),
scale: ScaleType::Linear,
range: None,
},
data_series: vec![], annotations: vec![ChartAnnotation {
annotation_type: AnnotationType::Text,
position: (0.8, 0.9),
text: format!("Slope: {:.4}", trend.slope),
style: AnnotationStyle {
color: "#ff7f0e".to_string(),
font_size: 12.0,
transparency: 0.8,
},
}],
});
}
}
visualizations
}
}
impl AnomalyDetector {
pub fn new(metric_name: String, sensitivity: f64) -> Self {
Self {
metric_name,
baseline_mean: 0.0,
baseline_std: 1.0,
sensitivity,
history: VecDeque::new(),
thresholds: AnomalyThresholds {
critical_upper: 3.0,
critical_lower: -3.0,
warning_upper: 2.0,
warning_lower: -2.0,
change_rate_threshold: 0.5,
},
}
}
pub fn update(&mut self, value: f64) {
self.history.push_back(value);
if self.history.len() > 1000 {
self.history.pop_front();
}
if self.history.len() > 10 {
let values: Vec<f64> = self.history.iter().cloned().collect();
self.baseline_mean = values.iter().sum::<f64>() / values.len() as f64;
self.baseline_std = (values.iter()
.map(|x| (x - self.baseline_mean).powi(2))
.sum::<f64>() / values.len() as f64).sqrt();
}
}
pub fn check_anomaly(&self, value: f64) -> Result<Option<AnomalyDetection>> {
if self.baseline_std == 0.0 {
return Ok(None); }
let z_score = (value - self.baseline_mean) / self.baseline_std;
let (anomaly_type, severity) = if z_score > self.thresholds.critical_upper {
(AnomalyType::SuddenSpike, AnomalySeverity::Critical)
} else if z_score < self.thresholds.critical_lower {
(AnomalyType::SuddenDrop, AnomalySeverity::Critical)
} else if z_score > self.thresholds.warning_upper {
(AnomalyType::SuddenSpike, AnomalySeverity::High)
} else if z_score < self.thresholds.warning_lower {
(AnomalyType::SuddenDrop, AnomalySeverity::High)
} else {
return Ok(None); };
Ok(Some(AnomalyDetection {
anomaly_type,
severity,
detected_at: SystemTime::now(),
affected_metrics: vec![self.metric_name.clone()],
anomaly_score: z_score.abs(),
threshold_value: if z_score > 0.0 { self.thresholds.warning_upper } else { self.thresholds.warning_lower },
actual_value: value,
description: format!("Anomalous {} value detected: {:.2} (z-score: {:.2})", self.metric_name, value, z_score),
recommended_actions: vec![
"Investigate recent changes in workload or configuration".to_string(),
"Check for resource contention or system issues".to_string(),
"Consider adjusting monitoring thresholds if this becomes normal".to_string(),
],
}))
}
}
impl Default for MemoryAnalytics {
fn default() -> Self {
Self::new()
}
}
#[cfg(test)]
mod tests {
use super::*;
fn create_test_statistics(efficiency: f64, cache_hit: f64) -> MemoryStatistics {
MemoryStatistics {
total_allocations: 1000,
total_deallocations: 900,
peak_memory_usage: 1024 * 1024,
average_memory_usage: 512.0 * 1024.0,
current_memory_usage: 800 * 1024,
memory_churn_rate: 0.1,
allocation_rate: 10.0,
deallocation_rate: 9.0,
fragmentation_index: 0.2,
efficiency_score: efficiency,
cache_hit_ratio: cache_hit,
bandwidth_utilization: 0.7,
pressure_incidents: 0,
optimization_opportunities: 2,
}
}
fn create_test_context() -> AnalyticsContext {
AnalyticsContext {
session_id: "test_session".to_string(),
workload_type: "training".to_string(),
model_architecture: Some("resnet50".to_string()),
batch_size: Some(32),
device_info: DeviceInfo {
device_type: "GPU".to_string(),
total_memory: 8 * 1024 * 1024 * 1024,
compute_capability: Some("7.5".to_string()),
driver_version: Some("11.2".to_string()),
},
environment: EnvironmentInfo {
framework_version: "0.1.0".to_string(),
rust_version: "1.70.0".to_string(),
optimization_level: "release".to_string(),
feature_flags: vec!["cuda".to_string()],
},
}
}
#[test]
fn test_analytics_creation() {
let analytics = MemoryAnalytics::new();
assert_eq!(analytics.historical_data.len(), 0);
assert_eq!(analytics.baselines.len(), 0);
}
#[test]
fn test_statistics_recording() {
let mut analytics = MemoryAnalytics::new();
let stats = create_test_statistics(0.8, 0.9);
let context = create_test_context();
let result = analytics.record_statistics(stats, context);
assert!(result.is_ok());
assert_eq!(analytics.historical_data.len(), 1);
}
#[test]
fn test_baseline_creation() {
let mut analytics = MemoryAnalytics::new();
for i in 0..20 {
let stats = create_test_statistics(0.8 + i as f64 * 0.01, 0.9);
let context = create_test_context();
analytics.record_statistics(stats, context).expect("statistics recording should succeed");
}
let result = analytics.create_baseline(
"test_baseline".to_string(),
"Test baseline".to_string(),
Duration::from_secs(300),
);
assert!(result.is_ok());
assert!(analytics.baselines.contains_key("test_baseline"));
}
#[test]
fn test_trend_analysis() {
let mut analytics = MemoryAnalytics::new();
for i in 0..50 {
let stats = create_test_statistics(0.5 + i as f64 * 0.01, 0.9);
let context = create_test_context();
analytics.record_statistics(stats, context).expect("statistics recording should succeed");
}
let trends = analytics.analyze_trends(Duration::from_secs(1000)).expect("operation should succeed");
assert!(!trends.is_empty());
let efficiency_trend = trends.iter().find(|t| t.metric_name == "efficiency_score");
assert!(efficiency_trend.is_some());
let trend = efficiency_trend.expect("operation should succeed");
assert_eq!(trend.trend_direction, TrendDirection::Increasing);
assert!(trend.slope > 0.0);
}
#[test]
fn test_anomaly_detection() {
let mut analytics = MemoryAnalytics::new();
for _ in 0..100 {
let stats = create_test_statistics(0.8, 0.9);
let context = create_test_context();
analytics.record_statistics(stats, context).expect("statistics recording should succeed");
}
let anomalous_stats = create_test_statistics(0.1, 0.1); analytics.record_statistics(anomalous_stats, create_test_context()).expect("operation should succeed");
let anomalies = analytics.detect_anomalies().expect("anomaly detection should succeed");
assert!(!anomalies.is_empty());
}
#[test]
fn test_report_generation() {
let mut analytics = MemoryAnalytics::new();
for i in 0..100 {
let stats = create_test_statistics(0.8, 0.9);
let context = create_test_context();
analytics.record_statistics(stats, context).expect("statistics recording should succeed");
}
let report = analytics.generate_report(Duration::from_secs(300));
assert!(report.is_ok());
let report = report.expect("operation should succeed");
assert!(!report.report_id.is_empty());
assert!(report.detailed_statistics.efficiency_score > 0.0);
}
#[test]
fn test_anomaly_detector() {
let mut detector = AnomalyDetector::new("test_metric".to_string(), 0.95);
for i in 0..100 {
detector.update(100.0 + i as f64 * 0.1);
}
let anomaly = detector.check_anomaly(200.0).expect("anomaly check should succeed");
assert!(anomaly.is_some());
let anomaly = anomaly.expect("operation should succeed");
assert_eq!(anomaly.anomaly_type, AnomalyType::SuddenSpike);
}
#[test]
fn test_confidence_interval_calculation() {
let analytics = MemoryAnalytics::new();
let mut data_points = Vec::new();
for i in 0..50 {
let stats = create_test_statistics(0.8, 0.9);
let context = create_test_context();
data_points.push(HistoricalDataPoint {
timestamp: SystemTime::now(),
statistics: stats,
context,
custom_metrics: HashMap::new(),
});
}
let data_refs: Vec<&HistoricalDataPoint> = data_points.iter().collect();
let ci = analytics.calculate_confidence_interval(&data_refs, 0.95);
assert_eq!(ci.confidence_level, 0.95);
assert!(ci.lower_bound <= ci.upper_bound);
assert!(ci.margin_of_error >= 0.0);
}
}