use std::collections::VecDeque;
use std::sync::atomic::{AtomicBool, AtomicU64, AtomicUsize, Ordering};
use std::sync::{Arc, Mutex, RwLock};
use std::time::{Duration, Instant};
use haagenti::holotensor::QualityCurve;
use super::memory::{FragmentId, HoloMemoryManager, MemoryTier};
use super::{HoloInferenceError, Result};
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
pub enum StreamPriority {
Low = 0,
Normal = 1,
High = 2,
Critical = 3,
}
#[derive(Debug, Clone)]
pub struct StreamRequest {
pub fragment_id: FragmentId,
pub priority: StreamPriority,
pub target_tier: MemoryTier,
pub requested_at: Instant,
}
impl StreamRequest {
pub fn new(fragment_id: FragmentId, priority: StreamPriority) -> Self {
Self {
fragment_id,
priority,
target_tier: MemoryTier::Vram,
requested_at: Instant::now(),
}
}
pub fn age_ms(&self) -> u64 {
self.requested_at.elapsed().as_millis() as u64
}
}
#[derive(Debug, Clone, Default)]
pub struct StreamStats {
pub requests_submitted: usize,
pub requests_completed: usize,
pub requests_cancelled: usize,
pub bytes_transferred: usize,
pub transfer_time_us: u64,
pub avg_speed_bps: f64,
pub peak_speed_bps: f64,
pub queue_depth: usize,
pub pause_count: usize,
}
impl StreamStats {
pub fn bandwidth_utilization(&self, max_bandwidth_bps: f64) -> f32 {
if max_bandwidth_bps <= 0.0 {
return 0.0;
}
(self.avg_speed_bps / max_bandwidth_bps) as f32
}
}
pub struct StreamManager {
memory: Arc<HoloMemoryManager>,
queue: Mutex<VecDeque<StreamRequest>>,
in_flight: RwLock<Vec<FragmentId>>,
paused: AtomicBool,
running: AtomicBool,
stats: RwLock<StreamStats>,
bytes_transferred: AtomicUsize,
transfer_time_us: AtomicU64,
max_concurrent: usize,
current_layer: AtomicUsize,
}
impl StreamManager {
pub fn new(memory: Arc<HoloMemoryManager>, max_concurrent: usize) -> Self {
Self {
memory,
queue: Mutex::new(VecDeque::new()),
in_flight: RwLock::new(Vec::new()),
paused: AtomicBool::new(false),
running: AtomicBool::new(true),
stats: RwLock::new(StreamStats::default()),
bytes_transferred: AtomicUsize::new(0),
transfer_time_us: AtomicU64::new(0),
max_concurrent,
current_layer: AtomicUsize::new(0),
}
}
pub fn submit(&self, request: StreamRequest) -> Result<()> {
let mut queue = self
.queue
.lock()
.map_err(|_| HoloInferenceError::Cuda("queue lock poisoned".to_string()))?;
let insert_pos = queue
.iter()
.position(|r| r.priority < request.priority)
.unwrap_or(queue.len());
queue.insert(insert_pos, request);
if let Ok(mut stats) = self.stats.write() {
stats.requests_submitted += 1;
stats.queue_depth = queue.len();
}
Ok(())
}
pub fn submit_batch(&self, requests: Vec<StreamRequest>) -> Result<()> {
for request in requests {
self.submit(request)?;
}
Ok(())
}
pub fn request_layer(
&self,
layer: usize,
num_fragments: u16,
priority: StreamPriority,
) -> Result<()> {
let requests: Vec<_> = (0..num_fragments)
.map(|i| StreamRequest::new(FragmentId::new(layer, 0, i), priority))
.collect();
self.submit_batch(requests)
}
pub fn prefetch_layers(
&self,
start_layer: usize,
count: usize,
fragments_per_layer: u16,
) -> Result<()> {
for layer in start_layer..(start_layer + count) {
let priority = match layer - start_layer {
0 => StreamPriority::High,
1 => StreamPriority::Normal,
_ => StreamPriority::Low,
};
self.request_layer(layer, fragments_per_layer, priority)?;
}
Ok(())
}
pub fn prefetch_quality_aware(
&self,
layers: &[(usize, u16, u16)],
min_quality: f32,
target_quality: f32,
fragments_per_layer: u16,
) -> Result<()> {
let quality_curve = QualityCurve::default();
let mut layer_priorities: Vec<(usize, StreamPriority, u16)> = layers
.iter()
.map(|&(layer, loaded, total)| {
let current_quality = quality_curve.predict(loaded, total);
let priority = if current_quality < min_quality {
StreamPriority::Critical
} else if current_quality < target_quality * 0.8 {
StreamPriority::High
} else if current_quality < target_quality {
StreamPriority::Normal
} else {
StreamPriority::Low
};
let needed = quality_curve.fragments_for_quality(target_quality, total);
let to_load = (needed.saturating_sub(loaded)).min(fragments_per_layer);
(layer, priority, to_load)
})
.filter(|&(_, _, to_load)| to_load > 0)
.collect();
layer_priorities.sort_by(|a, b| b.1.cmp(&a.1));
for (layer, priority, count) in layer_priorities {
self.request_layer(layer, count, priority)?;
}
Ok(())
}
pub fn set_current_layer(&self, layer: usize) {
self.current_layer.store(layer, Ordering::Relaxed);
}
pub fn pause(&self) {
self.paused.store(true, Ordering::Relaxed);
if let Ok(mut stats) = self.stats.write() {
stats.pause_count += 1;
}
}
pub fn resume(&self) {
self.paused.store(false, Ordering::Relaxed);
}
pub fn is_paused(&self) -> bool {
self.paused.load(Ordering::Relaxed)
}
pub fn is_running(&self) -> bool {
self.running.load(Ordering::Relaxed)
}
pub fn stop(&self) {
self.running.store(false, Ordering::Relaxed);
}
pub fn next_request(&self) -> Option<StreamRequest> {
if self.is_paused() || !self.is_running() {
return None;
}
if let Ok(in_flight) = self.in_flight.read() {
if in_flight.len() >= self.max_concurrent {
return None;
}
}
let mut queue = self.queue.lock().ok()?;
queue.pop_front()
}
pub fn mark_in_flight(&self, id: FragmentId) {
if let Ok(mut in_flight) = self.in_flight.write() {
in_flight.push(id);
}
}
pub fn complete(&self, id: FragmentId, bytes: usize, duration: Duration) {
if let Ok(mut in_flight) = self.in_flight.write() {
in_flight.retain(|i| *i != id);
}
self.bytes_transferred.fetch_add(bytes, Ordering::Relaxed);
self.transfer_time_us
.fetch_add(duration.as_micros() as u64, Ordering::Relaxed);
if let Ok(mut stats) = self.stats.write() {
stats.requests_completed += 1;
stats.bytes_transferred += bytes;
stats.transfer_time_us += duration.as_micros() as u64;
if stats.transfer_time_us > 0 {
stats.avg_speed_bps =
(stats.bytes_transferred as f64 * 1_000_000.0) / stats.transfer_time_us as f64;
}
let instant_speed = (bytes as f64 * 1_000_000.0) / duration.as_micros() as f64;
if instant_speed > stats.peak_speed_bps {
stats.peak_speed_bps = instant_speed;
}
if let Ok(queue) = self.queue.lock() {
stats.queue_depth = queue.len();
}
}
}
pub fn cancel(&self, id: &FragmentId) {
if let Ok(mut queue) = self.queue.lock() {
queue.retain(|r| r.fragment_id != *id);
}
if let Ok(mut stats) = self.stats.write() {
stats.requests_cancelled += 1;
}
}
pub fn clear(&self) {
if let Ok(mut queue) = self.queue.lock() {
let cancelled = queue.len();
queue.clear();
if let Ok(mut stats) = self.stats.write() {
stats.requests_cancelled += cancelled;
stats.queue_depth = 0;
}
}
}
pub fn queue_depth(&self) -> usize {
self.queue.lock().map(|q| q.len()).unwrap_or(0)
}
pub fn in_flight_count(&self) -> usize {
self.in_flight.read().map(|f| f.len()).unwrap_or(0)
}
pub fn stats(&self) -> StreamStats {
self.stats.read().map(|s| s.clone()).unwrap_or_default()
}
pub fn needs_streaming(&self, id: &FragmentId) -> bool {
if self.memory.is_in_vram(id) {
return false;
}
if let Ok(queue) = self.queue.lock() {
if queue.iter().any(|r| r.fragment_id == *id) {
return false;
}
}
if let Ok(in_flight) = self.in_flight.read() {
if in_flight.contains(id) {
return false;
}
}
true
}
pub fn estimated_completion_time_ms(&self) -> u64 {
let stats = self.stats();
if stats.avg_speed_bps <= 0.0 {
return 0;
}
let queue_depth = self.queue_depth();
let avg_fragment_size = if stats.requests_completed > 0 {
stats.bytes_transferred / stats.requests_completed
} else {
1024 * 1024 };
let remaining_bytes = queue_depth * avg_fragment_size;
((remaining_bytes as f64 / stats.avg_speed_bps) * 1000.0) as u64
}
}
#[cfg(test)]
mod tests {
use super::super::memory::MemoryConfig;
use super::*;
fn create_test_manager() -> StreamManager {
let memory = Arc::new(HoloMemoryManager::new(MemoryConfig::default()));
StreamManager::new(memory, 4)
}
#[test]
fn test_submit_request() {
let manager = create_test_manager();
let request = StreamRequest::new(FragmentId::new(0, 0, 0), StreamPriority::Normal);
manager.submit(request).unwrap();
assert_eq!(manager.queue_depth(), 1);
}
#[test]
fn test_priority_ordering() {
let manager = create_test_manager();
manager
.submit(StreamRequest::new(
FragmentId::new(0, 0, 0),
StreamPriority::Low,
))
.unwrap();
manager
.submit(StreamRequest::new(
FragmentId::new(0, 0, 1),
StreamPriority::High,
))
.unwrap();
let next = manager.next_request().unwrap();
assert_eq!(next.fragment_id.fragment_index, 1);
assert_eq!(next.priority, StreamPriority::High);
}
#[test]
fn test_pause_resume() {
let manager = create_test_manager();
manager
.submit(StreamRequest::new(
FragmentId::new(0, 0, 0),
StreamPriority::Normal,
))
.unwrap();
manager.pause();
assert!(manager.is_paused());
assert!(manager.next_request().is_none());
manager.resume();
assert!(!manager.is_paused());
assert!(manager.next_request().is_some());
}
#[test]
fn test_completion_tracking() {
let manager = create_test_manager();
let id = FragmentId::new(0, 0, 0);
manager.mark_in_flight(id);
assert_eq!(manager.in_flight_count(), 1);
manager.complete(id, 1024 * 1024, Duration::from_millis(10));
assert_eq!(manager.in_flight_count(), 0);
let stats = manager.stats();
assert_eq!(stats.requests_completed, 1);
assert_eq!(stats.bytes_transferred, 1024 * 1024);
}
#[test]
fn test_prefetch_layers() {
let manager = create_test_manager();
manager.prefetch_layers(5, 3, 16).unwrap();
assert_eq!(manager.queue_depth(), 48);
}
#[test]
fn test_max_concurrent_limit() {
let manager = create_test_manager();
for i in 0..10 {
manager
.submit(StreamRequest::new(
FragmentId::new(0, 0, i),
StreamPriority::Normal,
))
.unwrap();
}
for i in 0..4 {
let req = manager.next_request().unwrap();
manager.mark_in_flight(req.fragment_id);
}
assert!(manager.next_request().is_none());
assert_eq!(manager.in_flight_count(), 4);
}
}