use anyhow::{Context, Result};
use chrono::{DateTime, Utc};
use serde::{Deserialize, Serialize};
use std::collections::{HashMap, VecDeque};
use std::sync::{Arc, Mutex, RwLock};
use std::time::{Duration, Instant};
use tracing::{debug, error, info, warn};
use super::super::types::*;
use crate::device_info::{MobileDeviceDetector, MobileDeviceInfo, ThermalState};
#[derive(Debug)]
pub struct BottleneckDetector {
config: BottleneckDetectionConfig,
active_bottlenecks: HashMap<String, PerformanceBottleneck>,
bottleneck_history: VecDeque<BottleneckDetectionEvent>,
detection_rules: Vec<BottleneckRule>,
severity_calculator: SeverityCalculator,
historical_analyzer: HistoricalAnalyzer,
detection_stats: BottleneckDetectionStats,
}
#[derive(Debug, Clone)]
pub struct BottleneckRule {
pub id: String,
pub name: String,
pub condition: BottleneckCondition,
pub severity: BottleneckSeverity,
pub suggestion: String,
pub confidence: f32,
pub enabled: bool,
}
#[derive(Debug, Clone)]
pub enum BottleneckCondition {
MemoryUsageHigh {
threshold_percent: f32,
duration_ms: u64,
},
CPUUsageHigh {
threshold_percent: f32,
duration_ms: u64,
},
GPUUsageHigh {
threshold_percent: f32,
duration_ms: u64,
},
LatencyHigh {
threshold_ms: f32,
sample_count: u32,
},
ThermalThrottling { severity: ThermalState },
BatteryDrainHigh { threshold_mw: f32, duration_ms: u64 },
NetworkLatencyHigh {
threshold_ms: f32,
sample_count: u32,
},
CacheHitRateLow {
threshold_percent: f32,
sample_count: u32,
},
MemoryPressure { pressure_level: u8 },
Custom { name: String, evaluator: String },
}
#[derive(Debug, Clone)]
pub struct BottleneckDetectionEvent {
pub timestamp: std::time::Instant,
pub bottleneck_type: BottleneckType,
pub severity: BottleneckSeverity,
pub detected_value: f32,
pub threshold_value: f32,
pub duration_ms: u64,
pub rule_id: String,
pub metadata: std::collections::HashMap<String, String>,
}
#[derive(Debug, Clone, Default)]
pub struct BottleneckDetectionStats {
pub total_detections: u64,
pub true_positives: u64,
pub false_positives: u64,
pub avg_detection_latency_ms: f32,
pub accuracy_rate: f32,
}
#[derive(Debug)]
pub struct SeverityCalculator {
rules: Vec<SeverityRule>,
}
#[derive(Debug)]
pub struct HistoricalAnalyzer {
window_size: usize,
trend_detector: TrendDetector,
statistical_model: StatisticalModel,
}
#[derive(Debug)]
struct SeverityRule;
#[derive(Debug)]
struct TrendDetector;
#[derive(Debug)]
struct StatisticalModel;
impl BottleneckDetector {
pub fn new(config: BottleneckDetectionConfig) -> Result<Self> {
let detection_rules = Self::initialize_default_rules();
Ok(Self {
config,
active_bottlenecks: HashMap::new(),
bottleneck_history: VecDeque::new(),
detection_rules,
severity_calculator: SeverityCalculator { rules: Vec::new() },
historical_analyzer: HistoricalAnalyzer {
window_size: 100,
trend_detector: TrendDetector,
statistical_model: StatisticalModel,
},
detection_stats: BottleneckDetectionStats::default(),
})
}
pub fn detect_bottlenecks(&mut self, metrics: &MobileMetricsSnapshot) -> Result<Vec<PerformanceBottleneck>> {
let mut detected_bottlenecks = Vec::new();
for rule in &self.detection_rules {
if !rule.enabled {
continue;
}
if self.evaluate_rule(rule, metrics)? {
let bottleneck = self.create_bottleneck_from_rule(rule, metrics)?;
detected_bottlenecks.push(bottleneck.clone());
self.active_bottlenecks.insert(rule.id.clone(), bottleneck);
let event = BottleneckDetectionEvent {
timestamp: Instant::now(),
bottleneck_type: self.rule_to_bottleneck_type(rule),
severity: rule.severity.clone(),
detected_value: self.extract_metric_value(rule, metrics)?,
threshold_value: self.extract_threshold_value(rule)?,
duration_ms: 0, rule_id: rule.id.clone(),
metadata: HashMap::new(),
};
self.bottleneck_history.push_back(event);
self.detection_stats.total_detections += 1;
debug!("Bottleneck detected: {} ({})", rule.name, rule.id);
}
}
Ok(detected_bottlenecks)
}
fn initialize_default_rules() -> Vec<BottleneckRule> {
vec![
BottleneckRule {
id: "memory_usage_high".to_string(),
name: "High Memory Usage".to_string(),
condition: BottleneckCondition::MemoryUsageHigh {
threshold_percent: 85.0,
duration_ms: 5000,
},
severity: BottleneckSeverity::High,
suggestion: "Consider reducing batch size or enabling memory optimization".to_string(),
confidence: 0.9,
enabled: true,
},
BottleneckRule {
id: "cpu_usage_high".to_string(),
name: "High CPU Usage".to_string(),
condition: BottleneckCondition::CPUUsageHigh {
threshold_percent: 90.0,
duration_ms: 3000,
},
severity: BottleneckSeverity::High,
suggestion: "Consider optimizing model operations or reducing thread count".to_string(),
confidence: 0.85,
enabled: true,
},
BottleneckRule {
id: "inference_latency_high".to_string(),
name: "High Inference Latency".to_string(),
condition: BottleneckCondition::LatencyHigh {
threshold_ms: 500.0,
sample_count: 10,
},
severity: BottleneckSeverity::Medium,
suggestion: "Consider enabling quantization or hardware acceleration".to_string(),
confidence: 0.8,
enabled: true,
},
BottleneckRule {
id: "thermal_throttling".to_string(),
name: "Thermal Throttling".to_string(),
condition: BottleneckCondition::ThermalThrottling {
severity: ThermalState::Hot,
},
severity: BottleneckSeverity::Critical,
suggestion: "Reduce inference frequency or implement thermal management".to_string(),
confidence: 0.95,
enabled: true,
},
]
}
fn evaluate_rule(&self, rule: &BottleneckRule, metrics: &MobileMetricsSnapshot) -> Result<bool> {
match &rule.condition {
BottleneckCondition::MemoryUsageHigh { threshold_percent, .. } => {
Ok(metrics.memory_usage_percent > *threshold_percent)
}
BottleneckCondition::CPUUsageHigh { threshold_percent, .. } => {
Ok(metrics.cpu_usage_percent > *threshold_percent)
}
BottleneckCondition::LatencyHigh { threshold_ms, .. } => {
Ok(metrics.inference_latency_ms > *threshold_ms)
}
BottleneckCondition::ThermalThrottling { severity } => {
Ok(metrics.thermal_state >= *severity)
}
_ => Ok(false), }
}
fn create_bottleneck_from_rule(
&self,
rule: &BottleneckRule,
metrics: &MobileMetricsSnapshot,
) -> Result<PerformanceBottleneck> {
Ok(PerformanceBottleneck {
bottleneck_type: self.rule_to_bottleneck_type(rule),
severity: rule.severity.clone(),
description: rule.name.clone(),
impact_score: self.calculate_impact_score(rule, metrics)?,
suggestions: vec![rule.suggestion.clone()],
detected_at: Instant::now(),
confidence: rule.confidence,
affected_components: self.identify_affected_components(rule, metrics),
estimated_performance_loss: self.estimate_performance_loss(rule, metrics)?,
resolution_priority: self.calculate_resolution_priority(rule)?,
})
}
fn rule_to_bottleneck_type(&self, rule: &BottleneckRule) -> BottleneckType {
match &rule.condition {
BottleneckCondition::MemoryUsageHigh { .. } => BottleneckType::Memory,
BottleneckCondition::CPUUsageHigh { .. } => BottleneckType::CPU,
BottleneckCondition::GPUUsageHigh { .. } => BottleneckType::GPU,
BottleneckCondition::LatencyHigh { .. } => BottleneckType::Latency,
BottleneckCondition::ThermalThrottling { .. } => BottleneckType::Thermal,
BottleneckCondition::BatteryDrainHigh { .. } => BottleneckType::Battery,
BottleneckCondition::NetworkLatencyHigh { .. } => BottleneckType::Network,
_ => BottleneckType::Other,
}
}
fn extract_metric_value(&self, rule: &BottleneckRule, metrics: &MobileMetricsSnapshot) -> Result<f32> {
match &rule.condition {
BottleneckCondition::MemoryUsageHigh { .. } => Ok(metrics.memory_usage_percent),
BottleneckCondition::CPUUsageHigh { .. } => Ok(metrics.cpu_usage_percent),
BottleneckCondition::LatencyHigh { .. } => Ok(metrics.inference_latency_ms),
_ => Ok(0.0),
}
}
fn extract_threshold_value(&self, rule: &BottleneckRule) -> Result<f32> {
match &rule.condition {
BottleneckCondition::MemoryUsageHigh { threshold_percent, .. } => Ok(*threshold_percent),
BottleneckCondition::CPUUsageHigh { threshold_percent, .. } => Ok(*threshold_percent),
BottleneckCondition::LatencyHigh { threshold_ms, .. } => Ok(*threshold_ms),
_ => Ok(0.0),
}
}
fn calculate_impact_score(&self, rule: &BottleneckRule, metrics: &MobileMetricsSnapshot) -> Result<f32> {
let base_score = match rule.severity {
BottleneckSeverity::Low => 0.3,
BottleneckSeverity::Medium => 0.6,
BottleneckSeverity::High => 0.8,
BottleneckSeverity::Critical => 1.0,
};
let deviation_factor = self.calculate_deviation_factor(rule, metrics)?;
Ok(base_score * (1.0 + deviation_factor * 0.5))
}
fn calculate_deviation_factor(&self, rule: &BottleneckRule, metrics: &MobileMetricsSnapshot) -> Result<f32> {
let current_value = self.extract_metric_value(rule, metrics)?;
let threshold = self.extract_threshold_value(rule)?;
if threshold > 0.0 {
Ok((current_value - threshold) / threshold)
} else {
Ok(0.0)
}
}
fn identify_affected_components(&self, rule: &BottleneckRule, _metrics: &MobileMetricsSnapshot) -> Vec<String> {
match &rule.condition {
BottleneckCondition::MemoryUsageHigh { .. } => vec!["Memory Manager", "Model Storage"],
BottleneckCondition::CPUUsageHigh { .. } => vec!["CPU Scheduler", "Inference Engine"],
BottleneckCondition::LatencyHigh { .. } => vec!["Inference Pipeline", "Model Executor"],
BottleneckCondition::ThermalThrottling { .. } => vec!["Thermal Manager", "CPU/GPU"],
_ => vec!["Unknown"],
}.into_iter().map(|s| s.to_string()).collect()
}
fn estimate_performance_loss(&self, rule: &BottleneckRule, metrics: &MobileMetricsSnapshot) -> Result<f32> {
let deviation_factor = self.calculate_deviation_factor(rule, metrics)?;
let base_loss = match rule.severity {
BottleneckSeverity::Low => 5.0,
BottleneckSeverity::Medium => 15.0,
BottleneckSeverity::High => 30.0,
BottleneckSeverity::Critical => 50.0,
};
Ok((base_loss * (1.0 + deviation_factor)).min(90.0))
}
fn calculate_resolution_priority(&self, rule: &BottleneckRule) -> Result<u8> {
let priority = match rule.severity {
BottleneckSeverity::Critical => 1,
BottleneckSeverity::High => 2,
BottleneckSeverity::Medium => 3,
BottleneckSeverity::Low => 4,
};
Ok(priority)
}
pub fn get_detection_stats(&self) -> &BottleneckDetectionStats {
&self.detection_stats
}
pub fn clear_active_bottlenecks(&mut self) {
self.active_bottlenecks.clear();
}
pub fn get_active_bottlenecks(&self) -> Vec<PerformanceBottleneck> {
self.active_bottlenecks.values().cloned().collect()
}
}
impl Default for BottleneckDetector {
fn default() -> Self {
Self::new(BottleneckDetectionConfig::default())
.expect("Failed to create default bottleneck detector")
}
}