use super::errors::{DataError, DataResult};
use super::types::*;
use std::collections::HashMap;
use std::time::SystemTime;
pub struct StrategySelector {
benchmarks: HashMap<String, f64>,
device_capabilities: DeviceCapabilities,
performance_history: Vec<PerformanceMetric>,
}
impl Default for StrategySelector {
fn default() -> Self {
Self::new()
}
}
impl StrategySelector {
pub fn new() -> Self {
Self {
benchmarks: HashMap::new(),
device_capabilities: DeviceCapabilities::new(),
performance_history: Vec::new(),
}
}
pub fn with_capabilities(capabilities: DeviceCapabilities) -> Self {
Self {
benchmarks: HashMap::new(),
device_capabilities: capabilities,
performance_history: Vec::new(),
}
}
pub fn select_strategy(&self, data_spec: &DataSpec) -> DataResult<ProcessingStrategy> {
if data_spec.is_small() {
Ok(ProcessingStrategy::CPU(RayonConfig::new()))
} else if data_spec.is_large()
&& self.device_capabilities.should_use_gpu(data_spec.row_count)
{
Ok(ProcessingStrategy::GPU(ComputeConfig::new()))
} else if self
.device_capabilities
.should_use_hybrid(data_spec.row_count)
{
Ok(ProcessingStrategy::Hybrid(HybridConfig::new()))
} else {
Ok(ProcessingStrategy::Streaming(StreamConfig::new()))
}
}
pub fn record_performance(&mut self, metric: PerformanceMetric) {
self.performance_history.push(metric);
if self.performance_history.len() > 1000 {
self.performance_history
.drain(0..self.performance_history.len() - 1000);
}
}
pub fn get_benchmark(&self, strategy_name: &str) -> Option<f64> {
self.benchmarks.get(strategy_name).copied()
}
pub fn update_benchmark(&mut self, strategy_name: String, performance: f64) {
self.benchmarks.insert(strategy_name, performance);
}
pub fn get_best_strategy(&self, data_size: usize) -> Option<String> {
let similar_metrics: Vec<&PerformanceMetric> = self
.performance_history
.iter()
.filter(|m| (m.data_size as f64 - data_size as f64).abs() / (data_size as f64) < 0.1)
.collect();
if similar_metrics.is_empty() {
return None;
}
let best_metric = similar_metrics
.iter()
.max_by(|a, b| a.throughput().partial_cmp(&b.throughput()).unwrap())?;
Some(best_metric.strategy.clone())
}
pub fn get_strategy_stats(&self, strategy_name: &str) -> StrategyStats {
let metrics: Vec<&PerformanceMetric> = self
.performance_history
.iter()
.filter(|m| m.strategy == strategy_name)
.collect();
if metrics.is_empty() {
return StrategyStats::new(strategy_name);
}
let mut stats = StrategyStats::new(strategy_name);
for metric in metrics {
stats.add_metric(metric);
}
stats
}
pub fn device_capabilities(&self) -> &DeviceCapabilities {
&self.device_capabilities
}
pub fn update_capabilities(&mut self, capabilities: DeviceCapabilities) {
self.device_capabilities = capabilities;
}
pub fn clear_history(&mut self) {
self.performance_history.clear();
}
pub fn performance_history(&self) -> &[PerformanceMetric] {
&self.performance_history
}
}
#[derive(Debug, Clone)]
pub struct StrategyStats {
pub strategy_name: String,
pub total_executions: usize,
pub average_duration: f64,
pub min_duration: f64,
pub max_duration: f64,
pub average_throughput: f64,
pub average_memory_usage: usize,
pub average_cpu_usage: f64,
pub average_gpu_usage: Option<f64>,
pub success_rate: f64,
pub last_updated: SystemTime,
}
impl StrategyStats {
pub fn new(strategy_name: impl Into<String>) -> Self {
Self {
strategy_name: strategy_name.into(),
total_executions: 0,
average_duration: 0.0,
min_duration: f64::INFINITY,
max_duration: 0.0,
average_throughput: 0.0,
average_memory_usage: 0,
average_cpu_usage: 0.0,
average_gpu_usage: None,
success_rate: 0.0,
last_updated: SystemTime::now(),
}
}
pub fn add_metric(&mut self, metric: &PerformanceMetric) {
self.total_executions += 1;
self.average_duration = (self.average_duration * (self.total_executions - 1) as f64
+ metric.duration)
/ self.total_executions as f64;
self.min_duration = self.min_duration.min(metric.duration);
self.max_duration = self.max_duration.max(metric.duration);
self.average_throughput = (self.average_throughput * (self.total_executions - 1) as f64
+ metric.throughput())
/ self.total_executions as f64;
self.average_memory_usage = (self.average_memory_usage * (self.total_executions - 1)
+ metric.memory_usage)
/ self.total_executions;
self.average_cpu_usage = (self.average_cpu_usage * (self.total_executions - 1) as f64
+ metric.cpu_usage)
/ self.total_executions as f64;
if let Some(gpu_usage) = metric.gpu_usage {
match self.average_gpu_usage {
Some(avg) => {
self.average_gpu_usage = Some(
(avg * (self.total_executions - 1) as f64 + gpu_usage)
/ self.total_executions as f64,
);
}
None => {
self.average_gpu_usage = Some(gpu_usage);
}
}
}
self.last_updated = SystemTime::now();
}
pub fn efficiency_score(&self) -> f64 {
let resource_usage = self.average_cpu_usage + self.average_gpu_usage.unwrap_or(0.0);
if resource_usage > 0.0 {
self.average_throughput / resource_usage
} else {
0.0
}
}
pub fn reliability_score(&self) -> f64 {
if self.total_executions == 0 {
return 0.0;
}
let consistency = if self.max_duration > 0.0 {
1.0 - (self.max_duration - self.min_duration) / self.max_duration
} else {
1.0
};
self.success_rate * consistency
}
pub fn overall_score(&self) -> f64 {
let efficiency = self.efficiency_score();
let reliability = self.reliability_score();
0.6 * efficiency + 0.4 * reliability
}
}
pub struct StrategyRecommender {
selector: StrategySelector,
learning_rate: f64,
exploration_rate: f64,
}
impl StrategyRecommender {
pub fn new() -> Self {
Self {
selector: StrategySelector::new(),
learning_rate: 0.1,
exploration_rate: 0.1,
}
}
pub fn with_parameters(learning_rate: f64, exploration_rate: f64) -> Self {
Self {
selector: StrategySelector::new(),
learning_rate,
exploration_rate,
}
}
pub fn recommend_strategy(&self, data_spec: &DataSpec) -> DataResult<ProcessingStrategy> {
if let Some(best_strategy) = self.selector.get_best_strategy(data_spec.row_count) {
self.strategy_from_name(&best_strategy, data_spec)
} else {
if rand::random::<f64>() < self.exploration_rate {
self.explore_strategy(data_spec)
} else {
self.selector.select_strategy(data_spec)
}
}
}
pub fn learn(&mut self, strategy_name: &str, performance: f64, _data_spec: &DataSpec) {
let current_benchmark = self.selector.get_benchmark(strategy_name).unwrap_or(0.0);
let new_benchmark =
current_benchmark + self.learning_rate * (performance - current_benchmark);
self.selector
.update_benchmark(strategy_name.to_string(), new_benchmark);
}
pub fn selector(&self) -> &StrategySelector {
&self.selector
}
pub fn selector_mut(&mut self) -> &mut StrategySelector {
&mut self.selector
}
pub fn set_learning_rate(&mut self, rate: f64) {
self.learning_rate = rate.clamp(0.0, 1.0);
}
pub fn set_exploration_rate(&mut self, rate: f64) {
self.exploration_rate = rate.clamp(0.0, 1.0);
}
fn explore_strategy(&self, _data_spec: &DataSpec) -> DataResult<ProcessingStrategy> {
let strategies = vec![
ProcessingStrategy::CPU(RayonConfig::new()),
ProcessingStrategy::GPU(ComputeConfig::new()),
ProcessingStrategy::Streaming(StreamConfig::new()),
ProcessingStrategy::Hybrid(HybridConfig::new()),
];
let index = rand::random::<usize>() % strategies.len();
Ok(strategies[index].clone())
}
fn strategy_from_name(
&self,
name: &str,
_data_spec: &DataSpec,
) -> DataResult<ProcessingStrategy> {
match name {
"CPU" => Ok(ProcessingStrategy::CPU(RayonConfig::new())),
"GPU" => Ok(ProcessingStrategy::GPU(ComputeConfig::new())),
"Streaming" => Ok(ProcessingStrategy::Streaming(StreamConfig::new())),
"Hybrid" => Ok(ProcessingStrategy::Hybrid(HybridConfig::new())),
_ => Err(DataError::processing(format!("Unknown strategy: {}", name))),
}
}
}
impl Default for StrategyRecommender {
fn default() -> Self {
Self::new()
}
}