use anyhow::Result;
use std::collections::{BTreeMap, HashMap};
use std::sync::{Arc, Mutex};
use super::{AIInferenceEngine, InferenceResult};
use std::collections::VecDeque;
use std::time::{Duration, Instant};
#[derive(Debug, Clone)]
pub struct DynamicBatchConfig {
pub min_batch_size: usize,
pub max_batch_size: usize,
pub wait_timeout_ms: u64,
pub performance_window: usize,
}
impl Default for DynamicBatchConfig {
fn default() -> Self {
Self {
min_batch_size: 1,
max_batch_size: 64,
wait_timeout_ms: 10,
performance_window: 100,
}
}
}
#[derive(Debug, Clone, Default)]
struct BatchPerformanceStats {
pub total_processed: u64,
pub throughput: f64,
pub avg_latency_ms: f64,
}
pub struct DynamicBatchProcessor {
inference_engine: Arc<AIInferenceEngine>,
config: DynamicBatchConfig,
input_queue: Arc<Mutex<VecDeque<Tensor>>>,
result_queue: Arc<Mutex<VecDeque<InferenceResult>>>,
current_batch_size: usize,
performance_stats: Arc<Mutex<BatchPerformanceStats>>,
running: Arc<Mutex<bool>>,
}
impl DynamicBatchProcessor {
pub async fn new(
inference_engine: Arc<AIInferenceEngine>,
config: DynamicBatchConfig,
) -> Result<Self> {
let initial_batch_size: _ = (config.min_batch_size + config.max_batch_size) / 2;
Ok(Self {
inference_engine,
config,
input_queue: Arc::new(Mutex::new(VecDeque::new())),
result_queue: Arc::new(Mutex::new(VecDeque::new())),
current_batch_size: initial_batch_size,
performance_stats: Arc::new(Mutex::new(BatchPerformanceStats::default())),
running: Arc::new(Mutex::new(false)),
})
}
pub async fn submit_inference(&self, input: Tensor) -> Result<usize> {
let mut queue = self.input_queue.lock().await;
let task_id: _ = queue.len();
queue.push_back(input);
Ok(task_id)
}
pub async fn start_processing(&self) -> Result<()> {
{
let mut running = self.running.lock().await;
if *running {
return Ok(());
}
*running = true;
}
let input_queue: _ = Arc::clone(&self.input_queue);
let result_queue: _ = Arc::clone(&self.result_queue);
let inference_engine: _ = Arc::clone(&self.inference_engine);
let config: _ = self.config.clone();
let performance_stats: _ = Arc::clone(&self.performance_stats);
let running: _ = Arc::clone(&self.running);
tokio::spawn(async move {
let mut last_batch_time = Instant::now();
loop {
{
let running_guard: _ = running.lock().await;
if !*running_guard {
break;
}
}
let current_batch_size: _ = 8; let should_process: _ = {
let queue: _ = input_queue.lock().await;
let timeout_reached: _ =
last_batch_time.elapsed() > Duration::from_millis(config.wait_timeout_ms);
let batch_full: _ = queue.len() >= current_batch_size;
timeout_reached || batch_full
};
if should_process {
let mut batch = Vec::new();
{
let mut queue = input_queue.lock().await;
for _ in 0..current_batch_size {
if let Some(input) = queue.pop_front() {
batch.push(input);
} else {
break;
}
}
}
if !batch.is_empty() {
let batch_start: _ = Instant::now();
let model_id: _ = "test_model".to_string();
let batch_size: _ = batch.len();
for (i, input) in batch.into_iter().enumerate() {
let result: _ = inference_engine.infer(&model_id, &input).await;
match result {
Ok(inference_result) => {
let mut queue = result_queue.lock().await;
queue.push_back(inference_result);
}
Err(e) => {
eprintln!("推理失败 (任务 {}): {}", i, e);
}
}
}
let batch_duration: _ = batch_start.elapsed();
{
let mut stats = performance_stats.lock().await;
stats.total_processed += batch_size as u64;
stats.avg_latency_ms = batch_duration.as_secs_f64() * 1000.0;
stats.throughput = batch_size as f64 / batch_duration.as_secs_f64();
}
last_batch_time = Instant::now();
}
}
tokio::time::sleep(Duration::from_millis(1)).await;
}
});
Ok(())
}
pub async fn stop_processing(&self) {
let mut running = self.running.lock().await;
*running = false;
}
pub async fn adjust_batch_size(&self) -> Result<()> {
let stats: _ = self.performance_stats.lock().await;
if stats.avg_latency_ms < 5.0 && self.current_batch_size < self.config.max_batch_size {
let new_size: _ = (self.current_batch_size * 2).min(self.config.max_batch_size);
println!(
"🔧 动态调整批次大小: {} -> {}",
self.current_batch_size, new_size
);
} else if stats.avg_latency_ms > 20.0
&& self.current_batch_size > self.config.min_batch_size
{
let new_size: _ = (self.current_batch_size / 2).max(self.config.min_batch_size);
println!(
"🔧 动态调整批次大小: {} -> {}",
self.current_batch_size, new_size
);
}
Ok(())
}
pub async fn get_performance_stats(&self) -> BatchPerformanceStats {
self.performance_stats.lock().await.clone()
}
pub async fn get_result(&self) -> Option<InferenceResult> {
let mut queue = self.result_queue.lock().await;
queue.pop_front()
}
}
#[cfg(test)]
mod tests {
#[tokio::test]
async fn test_dynamic_batch_processor_creation() {
let config: _ = DynamicBatchConfig::default();
let engine: _ = AIInferenceEngine::new().await.unwrap();
let processor: _ = DynamicBatchProcessor::new(Arc::new(Mutex::new(engine)), config).await;
assert!(processor.is_ok());
}
#[tokio::test]
async fn test_dynamic_batch_submission() {
let config: _ = DynamicBatchConfig::default();
let engine: _ = AIInferenceEngine::new().await.unwrap();
let processor: _ = DynamicBatchProcessor::new(Arc::new(Mutex::new(engine)), config)
.await
.unwrap();
let input: _ = Tensor::new(vec![1.0, 2.0, 3.0, 4.0], vec![2, 2]).unwrap();
let task_id: _ = processor.submit_inference(input).await.unwrap();
assert_eq!(task_id, 0);
}
#[tokio::test]
async fn test_dynamic_batch_processing() {
let config: _ = DynamicBatchConfig {
min_batch_size: 1,
max_batch_size: 4,
wait_timeout_ms: 50,
performance_window: 10,
};
let engine: _ = AIInferenceEngine::new().await.unwrap();
let processor: _ = DynamicBatchProcessor::new(Arc::new(Mutex::new(engine)), config)
.await
.unwrap();
processor.start_processing().await.unwrap();
for i in 0..3 {
let input: _ = Tensor::new(
vec![i as f32, (i + 1) as f32, (i + 2) as f32, (i + 3) as f32],
vec![2, 2],
)
.unwrap();
let _: _ = processor.submit_inference(input).await;
}
sleep(Duration::from_millis(200)).await;
processor.stop_processing().await;
let stats: _ = processor.get_performance_stats().await;
assert!(stats.total_processed > 0);
}
}