use std::collections::VecDeque;
use std::sync::{Arc, Mutex};
use std::time::{Duration, Instant};
use super::async_ctx::AsyncContext;
use super::base::ExecutionBase;
use super::parallel::ParallelContext;
#[derive(Clone)]
pub struct HybridContext {
pub(super) parallel_context: ParallelContext,
pub(super) async_context: AsyncContext,
pub(super) performance_history: Arc<Mutex<PerformanceHistory>>,
pub(super) config: HybridConfig,
}
impl Default for HybridContext {
fn default() -> Self {
Self::new()
}
}
#[derive(Debug, Clone)]
pub struct HybridConfig {
pub parallel_threshold: usize,
pub async_threshold: usize,
pub adaptation_factor: f64,
pub history_window: usize,
}
impl Default for HybridConfig {
fn default() -> Self {
Self {
parallel_threshold: 1000,
async_threshold: 10000,
adaptation_factor: 0.1,
history_window: 10,
}
}
}
#[derive(Debug)]
pub struct PerformanceHistory {
pub(super) parallel_times: VecDeque<Duration>,
pub(super) async_times: VecDeque<Duration>,
pub(super) last_decision: Option<ExecutionStrategy>,
pub(super) decision_count: usize,
}
#[derive(Debug, Clone, Copy, PartialEq)]
pub enum ExecutionStrategy {
Parallel,
Async,
}
impl Default for PerformanceHistory {
fn default() -> Self {
Self::new()
}
}
impl PerformanceHistory {
pub fn new() -> Self {
Self {
parallel_times: VecDeque::new(),
async_times: VecDeque::new(),
last_decision: None,
decision_count: 0,
}
}
pub fn record_parallel_time(&mut self, duration: Duration) {
self.parallel_times.push_back(duration);
self.last_decision = Some(ExecutionStrategy::Parallel);
self.decision_count += 1;
}
pub fn record_async_time(&mut self, duration: Duration) {
self.async_times.push_back(duration);
self.last_decision = Some(ExecutionStrategy::Async);
self.decision_count += 1;
}
pub fn recommend_strategy(
&self,
item_count: usize,
config: &HybridConfig,
) -> ExecutionStrategy {
if item_count < config.parallel_threshold {
return ExecutionStrategy::Async;
}
if item_count > config.async_threshold {
return ExecutionStrategy::Parallel;
}
if self.parallel_times.is_empty() && self.async_times.is_empty() {
if item_count < (config.parallel_threshold + config.async_threshold) / 2 {
ExecutionStrategy::Async
} else {
ExecutionStrategy::Parallel
}
} else {
let parallel_avg = if self.parallel_times.is_empty() {
Duration::from_secs(1) } else {
let sum: Duration = self.parallel_times.iter().sum();
sum / self.parallel_times.len() as u32
};
let async_avg = if self.async_times.is_empty() {
Duration::from_secs(1) } else {
let sum: Duration = self.async_times.iter().sum();
sum / self.async_times.len() as u32
};
let factor = config.adaptation_factor;
if parallel_avg.as_secs_f64() * factor < async_avg.as_secs_f64() {
ExecutionStrategy::Parallel
} else {
ExecutionStrategy::Async
}
}
}
}
impl HybridContext {
pub fn new() -> Self {
Self {
parallel_context: ParallelContext::new(),
async_context: AsyncContext::new(),
performance_history: Arc::new(Mutex::new(PerformanceHistory::new())),
config: HybridConfig::default(),
}
}
pub fn with_config(config: HybridConfig) -> Self {
Self {
parallel_context: ParallelContext::new(),
async_context: AsyncContext::new(),
performance_history: Arc::new(Mutex::new(PerformanceHistory::new())),
config,
}
}
pub fn choose_strategy(&self, item_count: usize) -> ExecutionStrategy {
let history = self.performance_history.lock().unwrap();
history.recommend_strategy(item_count, &self.config)
}
pub fn execute_iter<T, F, R>(
&self,
items: Vec<T>,
func: F,
) -> Result<Vec<R>, Box<dyn std::error::Error + Send + Sync>>
where
T: Send + 'static,
F: Fn(T) -> R + Send + Sync + 'static,
R: Send + 'static,
{
let strategy = self.choose_strategy(items.len());
let start = Instant::now();
let result = match strategy {
ExecutionStrategy::Parallel => self.parallel_context.execute_iter(items, func),
ExecutionStrategy::Async => self.async_context.execute_iter(items, func),
};
let duration = start.elapsed();
if let Ok(mut history) = self.performance_history.lock() {
match strategy {
ExecutionStrategy::Parallel => {
history.record_parallel_time(duration);
while history.parallel_times.len() > self.config.history_window {
history.parallel_times.pop_front();
}
}
ExecutionStrategy::Async => {
history.record_async_time(duration);
while history.async_times.len() > self.config.history_window {
history.async_times.pop_front();
}
}
}
}
result
}
pub fn execute<F, R>(&self, func: F) -> Result<R, Box<dyn std::error::Error + Send + Sync>>
where
F: FnOnce() -> R + Send,
R: Send,
{
let strategy = self.choose_strategy(1); match strategy {
ExecutionStrategy::Parallel => self.parallel_context.execute(func),
ExecutionStrategy::Async => self.async_context.execute(func),
}
}
}
impl ExecutionBase for HybridContext {
fn context_type(&self) -> &'static str {
"Hybrid"
}
}
pub fn owned_chunks<T>(items: Vec<T>, chunk_size: usize) -> Vec<Vec<T>> {
let chunk_size = chunk_size.max(1);
let item_count = items.len();
let mut remaining = item_count;
let mut iter = items.into_iter();
let mut chunks = Vec::with_capacity(item_count.div_ceil(chunk_size));
while remaining > 0 {
let take = remaining.min(chunk_size);
chunks.push(iter.by_ref().take(take).collect());
remaining -= take;
}
chunks
}