use std::collections::VecDeque;
use std::collections::{BTreeMap, HashMap};
use std::sync::{Arc, Mutex};
use std::time::{Duration, Instant};
use tokio::sync::Semaphore;
#[derive(Debug, Clone)]
#[allow(dead_code)]
pub enum AiTaskType {
TextGeneration {
prompt: String,
max_tokens: Option<usize>,
temperature: f32,
},
ImageClassification {
image_data: Vec<u8>,
top_k: Option<usize>,
},
Embedding { text: String, model_name: String },
Translation {
text: String,
source_lang: String,
target_lang: String,
},
}
#[derive(Debug, Clone)]
#[allow(dead_code)]
pub enum AiTaskResult {
TextGeneration {
generated_text: String,
tokens_used: usize,
processing_time: Duration,
},
ImageClassification {
predictions: Vec<(String, f32)>,
processing_time: Duration,
},
Embedding {
vector: Vec<f32>,
dimensions: usize,
processing_time: Duration,
},
Translation {
translated_text: String,
source_lang: String,
target_lang: String,
processing_time: Duration,
},
}
#[derive(Debug, Clone)]
pub struct BatchConfig {
pub max_batch_size: usize,
#[allow(dead_code)]
pub batch_timeout_ms: u64,
pub max_concurrent_batches: usize,
#[allow(dead_code)]
pub enable_dynamic_batching: bool,
#[allow(dead_code)]
pub warmup_batch_size: usize,
}
impl Default for BatchConfig {
fn default() -> Self {
Self {
max_batch_size: 100,
batch_timeout_ms: 100,
max_concurrent_batches: 10,
enable_dynamic_batching: true,
warmup_batch_size: 10,
}
}
}
pub struct AiBatchProcessor {
config: BatchConfig,
pending_tasks: Arc<Mutex<VecDeque<(usize, AiTaskType)>>>,
active_batches: Arc<Mutex<usize>>,
batch_semaphore: Arc<Semaphore>,
next_task_id: Arc<Mutex<usize>>,
stats: Arc<Mutex<BatchStats>>,
results: Arc<Mutex<Vec<(usize, AiTaskResult)>>>,
}
#[derive(Debug, Clone, Default)]
pub struct BatchStats {
pub total_tasks_processed: usize,
pub total_batches_processed: usize,
pub total_processing_time: Duration,
pub average_batch_size: f64,
pub peak_memory_usage: usize,
#[allow(dead_code)]
pub cache_hits: usize,
#[allow(dead_code)]
pub cache_misses: usize,
}
impl BatchStats {
pub fn record_batch(
&mut self,
batch_size: usize,
processing_time: Duration,
memory_used: usize,
) {
self.total_tasks_processed += batch_size;
self.total_batches_processed += 1;
self.total_processing_time += processing_time;
self.average_batch_size =
self.total_tasks_processed as f64 / self.total_batches_processed as f64;
self.peak_memory_usage = self.peak_memory_usage.max(memory_used);
}
#[allow(dead_code)]
pub fn cache_hit_rate(&self) -> f64 {
let total: _ = self.cache_hits + self.cache_misses;
if total > 0 {
self.cache_hits as f64 / total as f64
} else {
0.0
}
}
}
#[allow(dead_code)]
impl AiBatchProcessor {
pub fn new(config: BatchConfig) -> Self {
let max_concurrent_batches = config.max_concurrent_batches;
Self {
config,
pending_tasks: Arc::new(Mutex::new(VecDeque::new())),
active_batches: Arc::new(Mutex::new(0)),
batch_semaphore: Arc::new(Semaphore::new(max_concurrent_batches)),
next_task_id: Arc::new(Mutex::new(0)),
stats: Arc::new(Mutex::new(BatchStats::default())),
results: Arc::new(Mutex::new(Vec::new())),
}
}
pub async fn add_task(&self, task: AiTaskType) -> usize {
let task_id: _ = {
let mut next_id = self.next_task_id.lock().unwrap();
let id: _ = *next_id;
*next_id += 1;
id
};
{
let mut tasks = self.pending_tasks.lock().unwrap();
tasks.push_back((task_id, task));
}
self.try_process_batch().await;
task_id
}
pub async fn add_tasks(&self, tasks: Vec<AiTaskType>) -> Vec<usize> {
let mut task_ids = Vec::with_capacity(tasks.len());
for task in tasks {
let task_id: _ = self.add_task(task).await;
task_ids.push(task_id);
}
task_ids
}
async fn try_process_batch(&self) {
if *self.active_batches.lock().unwrap() >= self.config.max_concurrent_batches {
return; }
if self.pending_tasks_count() == 0 {
return; }
let permit: _ = self.batch_semaphore.clone().try_acquire_owned();
if permit.is_err() {
return; }
let permit: _ = permit.unwrap();
let pending_tasks: _ = self.pending_tasks.clone();
let active_batches: _ = self.active_batches.clone();
let stats: _ = self.stats.clone();
let config: _ = self.config.clone();
let results: _ = self.results.clone();
tokio::spawn(async move {
let _permit: _ = permit;
*active_batches.lock().unwrap() += 1;
let batch_results: _ = Self::run_batch(pending_tasks, stats, config).await;
{
let mut all_results = results.lock().unwrap();
all_results.extend(batch_results);
}
*active_batches.lock().unwrap() -= 1;
});
}
async fn run_batch(
pending_tasks: Arc<Mutex<VecDeque<(usize, AiTaskType)>>>,
stats: Arc<Mutex<BatchStats>>,
config: BatchConfig,
) -> Vec<(usize, AiTaskResult)> {
let start_time: _ = Instant::now();
let batch_size: _ = config.max_batch_size;
let mut batch_tasks = Vec::with_capacity(batch_size);
{
let mut tasks = pending_tasks.lock().unwrap();
for _ in 0..batch_size {
if let Some(task) = tasks.pop_front() {
batch_tasks.push(task);
} else {
break;
}
}
}
let results: _ = Self::process_batch(&batch_tasks).await;
{
let mut stats_guard = stats.lock().unwrap();
stats_guard.record_batch(
batch_tasks.len(),
start_time.elapsed(),
batch_tasks.len() * 1024, );
}
println!(
"处理批次: {} 个任务, 耗时: {:.2}ms",
batch_tasks.len(),
start_time.elapsed().as_secs_f64() * 1000.0
);
results
}
async fn process_batch(tasks: &[(usize, AiTaskType)]) -> Vec<(usize, AiTaskResult)> {
let mut results = Vec::with_capacity(tasks.len());
for (task_id, task) in tasks {
let result: _ = Self::process_single_task(task).await;
results.push((*task_id, result));
}
results
}
async fn process_single_task(task: &AiTaskType) -> AiTaskResult {
let start_time: _ = Instant::now();
let result: _ = match task {
AiTaskType::TextGeneration {
prompt,
max_tokens: _,
temperature: _,
} => {
tokio::task::yield_now().await;
AiTaskResult::TextGeneration {
generated_text: format!("Generated text for: {}", prompt),
tokens_used: prompt.len() / 4,
processing_time: start_time.elapsed(),
}
}
AiTaskType::ImageClassification {
image_data: _,
top_k,
} => {
tokio::task::yield_now().await;
let predictions: _ = vec![
("cat".to_string(), 0.85),
("dog".to_string(), 0.75),
("bird".to_string(), 0.65),
];
let predictions: _ = if let Some(k) = top_k {
predictions.into_iter().take(*k).collect()
} else {
predictions
};
AiTaskResult::ImageClassification {
predictions,
processing_time: start_time.elapsed(),
}
}
AiTaskType::Embedding {
text: _,
model_name: _,
} => {
tokio::task::yield_now().await;
let dimensions: _ = 384;
let vector: _ = vec![0.1; dimensions];
AiTaskResult::Embedding {
vector,
dimensions,
processing_time: start_time.elapsed(),
}
}
AiTaskType::Translation {
text,
source_lang,
target_lang,
} => {
tokio::task::yield_now().await;
let translated: _ = format!(
"Translated: {} from {} to {}",
text, source_lang, target_lang
);
AiTaskResult::Translation {
translated_text: translated,
source_lang: source_lang.clone(),
target_lang: target_lang.clone(),
processing_time: start_time.elapsed(),
}
}
};
result
}
pub fn get_stats(&self) -> BatchStats {
self.stats.lock().unwrap().clone()
}
pub fn reset_stats(&self) {
*self.stats.lock().unwrap() = BatchStats::default();
}
pub fn pending_tasks_count(&self) -> usize {
self.pending_tasks.lock().unwrap().len()
}
pub fn active_batches_count(&self) -> usize {
*self.active_batches.lock().unwrap()
}
pub async fn flush(&self) -> Vec<(usize, AiTaskResult)> {
self.process_all_tasks().await;
while *self.active_batches.lock().unwrap() > 0 {
tokio::time::sleep(Duration::from_millis(5)).await;
}
let all_results: _ = {
let results: _ = self.results.lock().unwrap();
results.clone()
};
all_results
}
async fn process_all_tasks(&self) {
loop {
if self.pending_tasks_count() == 0 {
break;
}
let permit: _ = self.batch_semaphore.clone().try_acquire_owned();
if permit.is_err() {
tokio::time::sleep(Duration::from_millis(1)).await;
continue;
}
let permit: _ = permit.unwrap();
let pending_tasks: _ = self.pending_tasks.clone();
let active_batches: _ = self.active_batches.clone();
let stats: _ = self.stats.clone();
let config: _ = self.config.clone();
let results: _ = self.results.clone();
let handle: _ = tokio::spawn(async move {
let _permit: _ = permit;
*active_batches.lock().unwrap() += 1;
let batch_results: _ = Self::run_batch(pending_tasks, stats, config).await;
{
let mut all_results = results.lock().unwrap();
all_results.extend(batch_results);
}
*active_batches.lock().unwrap() -= 1;
});
let _: _ = handle.await;
}
}
}
#[allow(dead_code)]
pub fn create_text_generation_processor(max_batch_size: usize) -> AiBatchProcessor {
let config: _ = BatchConfig {
max_batch_size,
batch_timeout_ms: 50, max_concurrent_batches: 20,
enable_dynamic_batching: true,
warmup_batch_size: 5,
};
AiBatchProcessor::new(config)
}
#[allow(dead_code)]
pub fn create_image_classification_processor(max_batch_size: usize) -> AiBatchProcessor {
let config: _ = BatchConfig {
max_batch_size,
batch_timeout_ms: 200, max_concurrent_batches: 5,
enable_dynamic_batching: false, warmup_batch_size: 2,
};
AiBatchProcessor::new(config)
}
#[allow(dead_code)]
pub fn create_embedding_processor(max_batch_size: usize) -> AiBatchProcessor {
let config: _ = BatchConfig {
max_batch_size,
batch_timeout_ms: 100,
max_concurrent_batches: 15,
enable_dynamic_batching: true,
warmup_batch_size: 8,
};
AiBatchProcessor::new(config)
}
#[cfg(test)]
mod tests {
#[tokio::test]
async fn test_add_single_task() {
let processor: _ = AiBatchProcessor::new(BatchConfig::default());
let task: _ = AiTaskType::TextGeneration {
prompt: "Hello, world!".to_string(),
max_tokens: Some(100),
temperature: 0.7,
};
let task_id: _ = processor.add_task(task).await;
assert_eq!(task_id, 0);
tokio::time::sleep(Duration::from_millis(10)).await;
assert_eq!(processor.pending_tasks_count(), 0); }
#[tokio::test]
async fn test_add_multiple_tasks() {
let processor: _ = AiBatchProcessor::new(BatchConfig::default());
let tasks: _ = vec![
AiTaskType::TextGeneration {
prompt: "Task 1".to_string(),
max_tokens: Some(50),
temperature: 0.5,
},
AiTaskType::TextGeneration {
prompt: "Task 2".to_string(),
max_tokens: Some(50),
temperature: 0.5,
},
];
let task_ids: _ = processor.add_tasks(tasks).await;
assert_eq!(task_ids.len(), 2);
assert_eq!(task_ids, vec![0, 1]);
}
#[test]
fn test_batch_stats() {
let mut stats = BatchStats::default();
stats.record_batch(10, Duration::from_millis(100), 10240);
stats.record_batch(20, Duration::from_millis(200), 20480);
assert_eq!(stats.total_tasks_processed, 30);
assert_eq!(stats.total_batches_processed, 2);
assert_eq!(stats.average_batch_size, 15.0);
assert_eq!(stats.peak_memory_usage, 20480);
}
#[tokio::test]
async fn test_processor_creation() {
let processor: _ = create_text_generation_processor(50);
assert_eq!(processor.config.max_batch_size, 50);
assert_eq!(processor.config.batch_timeout_ms, 50);
assert!(processor.config.enable_dynamic_batching);
}
#[tokio::test]
async fn test_flush_processor() {
let processor: _ = AiBatchProcessor::new(BatchConfig::default());
let task: _ = AiTaskType::Embedding {
text: "Test embedding".to_string(),
model_name: "test-model".to_string(),
};
let _task_id: _ = processor.add_task(task).await;
tokio::time::sleep(Duration::from_millis(50)).await;
processor.flush().await;
assert_eq!(processor.pending_tasks_count(), 0);
assert_eq!(processor.active_batches_count(), 0);
}
}