use ferrum_types::{DataType, Device, RequestId, Result};
use serde::{Deserialize, Serialize};
use std::{any::Any, sync::Arc, time::Instant};
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct RecurrentStateTensorSpec {
pub layer_index: usize,
pub name: String,
pub shape: Vec<usize>,
pub dtype: DataType,
}
impl RecurrentStateTensorSpec {
pub fn new(
layer_index: usize,
name: impl Into<String>,
shape: Vec<usize>,
dtype: DataType,
) -> Self {
Self {
layer_index,
name: name.into(),
shape,
dtype,
}
}
pub fn checked_num_elements(&self) -> Option<usize> {
self.shape
.iter()
.copied()
.try_fold(1usize, usize::checked_mul)
}
pub fn num_elements(&self) -> usize {
self.checked_num_elements().unwrap_or(usize::MAX)
}
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct RecurrentStateSpec {
pub request_id: RequestId,
pub num_layers: usize,
pub tensors: Vec<RecurrentStateTensorSpec>,
pub device: Device,
pub max_batch_slots: usize,
}
impl RecurrentStateSpec {
pub fn estimated_memory_bytes(&self) -> usize {
let state_bytes_per_slot = self.tensors.iter().fold(0usize, |total, tensor| {
total.saturating_add(
tensor
.num_elements()
.saturating_mul(tensor.dtype.size_bytes()),
)
});
state_bytes_per_slot.saturating_mul(self.max_batch_slots)
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
pub enum RecurrentStateResumePolicy {
RecomputeOnResume,
SnapshotOnPreempt,
}
#[derive(Debug, Clone)]
pub struct RecurrentStateHandleStats {
pub memory_bytes: usize,
pub state_tensors: usize,
pub batch_slots: usize,
pub last_access: Instant,
}
pub trait RecurrentStateHandle: Send + Sync + std::fmt::Debug {
fn request_id(&self) -> RequestId;
fn device(&self) -> Device;
fn num_layers(&self) -> usize;
fn state_bytes(&self) -> usize;
fn clone_handle(&self) -> Result<Arc<dyn RecurrentStateHandle>>;
fn as_any(&self) -> &dyn Any;
fn stats(&self) -> RecurrentStateHandleStats;
fn is_valid(&self) -> bool;
fn cache_id(&self) -> String;
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct RecurrentStateManagerStats {
pub total_memory_bytes: usize,
pub used_memory_bytes: usize,
pub active_states: usize,
pub active_state_tensors: usize,
pub total_batch_slots: usize,
pub used_batch_slots: usize,
pub allocation_count: u64,
pub allocation_failures: u64,
pub eviction_count: u64,
}
#[async_trait::async_trait]
pub trait RecurrentStateManager: Send + Sync {
async fn allocate(&self, spec: &RecurrentStateSpec) -> Result<Arc<dyn RecurrentStateHandle>>;
async fn deallocate(&self, request_id: RequestId) -> Result<()>;
fn can_allocate(&self, spec: &RecurrentStateSpec) -> bool;
fn get_handle(&self, request_id: RequestId) -> Option<Arc<dyn RecurrentStateHandle>>;
fn list_handles(&self) -> Vec<(RequestId, Arc<dyn RecurrentStateHandle>)>;
fn stats(&self) -> RecurrentStateManagerStats;
async fn reset(&self) -> Result<()>;
}