use std::collections::HashMap;
use std::path::{Path, PathBuf};
use std::sync::Arc;
use cudarc::driver::CudaDevice;
use super::config::{LoadingStrategy, TieredConfig};
use super::error::TieredError;
use super::ram_cache::{CpuLayerWeights, LayerLayout, TensorLayout};
use super::stats::TieredStats;
use super::store::TieredWeightStore;
use super::vram_cache::SharedWeights;
use crate::adaptive_tiering::{AllocationPlan, MemoryTier, TensorAllocation};
use crate::cuda_inference::arch::ModelConfig;
use crate::cuda_inference::tensor::{GpuDType, GpuTensor};
use crate::cuda_inference::weight_store::{
LayerWeights, QuantFormat, QuantizedWeight, RMSNormWeights,
};
pub trait WeightLoader {
fn load(&self, store: &mut TieredWeightStore) -> Result<(), TieredError>;
fn progress(&self) -> f32;
}
pub struct EagerLoader {
model_dir: PathBuf,
config: ModelConfig,
plan: AllocationPlan,
device: Arc<CudaDevice>,
stats: Arc<TieredStats>,
layers_loaded: std::sync::atomic::AtomicUsize,
}
impl EagerLoader {
pub fn new(
model_dir: impl AsRef<Path>,
config: ModelConfig,
plan: AllocationPlan,
device: Arc<CudaDevice>,
stats: Arc<TieredStats>,
) -> Self {
Self {
model_dir: model_dir.as_ref().to_path_buf(),
config,
plan,
device,
stats,
layers_loaded: std::sync::atomic::AtomicUsize::new(0),
}
}
fn load_shared(&self) -> Result<SharedWeights, TieredError> {
tracing::info!("Loading shared weights...");
let embed_tokens = self.load_tensor("model.embed_tokens.weight")?;
let final_norm_weight = self.load_tensor("model.norm.weight")?;
let final_norm = RMSNormWeights {
weight: final_norm_weight,
};
let lm_head = if self.config.tie_word_embeddings {
None
} else {
Some(self.load_tensor("lm_head.weight")?)
};
Ok(SharedWeights::new(embed_tokens, final_norm, lm_head))
}
fn load_layer(&self, layer_idx: usize) -> Result<LayerWeights, TieredError> {
let prefix = format!("model.layers.{}", layer_idx);
let q_proj = self.load_quantized_weight(&format!("{}.self_attn.q_proj", prefix))?;
let k_proj = self.load_quantized_weight(&format!("{}.self_attn.k_proj", prefix))?;
let v_proj = self.load_quantized_weight(&format!("{}.self_attn.v_proj", prefix))?;
let o_proj = self.load_quantized_weight(&format!("{}.self_attn.o_proj", prefix))?;
let gate_proj = self.load_quantized_weight(&format!("{}.mlp.gate_proj", prefix))?;
let up_proj = self.load_quantized_weight(&format!("{}.mlp.up_proj", prefix))?;
let down_proj = self.load_quantized_weight(&format!("{}.mlp.down_proj", prefix))?;
let input_norm = RMSNormWeights {
weight: self.load_tensor(&format!("{}.input_layernorm.weight", prefix))?,
};
let post_attn_norm = RMSNormWeights {
weight: self.load_tensor(&format!("{}.post_attention_layernorm.weight", prefix))?,
};
self.layers_loaded
.fetch_add(1, std::sync::atomic::Ordering::Relaxed);
Ok(LayerWeights {
index: layer_idx,
q_proj,
k_proj,
v_proj,
o_proj,
gate_proj,
up_proj,
down_proj,
input_norm,
post_attn_norm,
})
}
fn load_tensor(&self, name: &str) -> Result<GpuTensor, TieredError> {
Err(TieredError::ModelLoad(format!(
"load_tensor not yet implemented for: {}",
name
)))
}
fn load_quantized_weight(&self, prefix: &str) -> Result<QuantizedWeight, TieredError> {
Err(TieredError::ModelLoad(format!(
"load_quantized_weight not yet implemented for: {}",
prefix
)))
}
fn load_layer_to_cpu(&self, layer_idx: usize) -> Result<CpuLayerWeights, TieredError> {
Err(TieredError::ModelLoad(format!(
"load_layer_to_cpu not yet implemented for layer: {}",
layer_idx
)))
}
}
impl WeightLoader for EagerLoader {
fn load(&self, store: &mut TieredWeightStore) -> Result<(), TieredError> {
tracing::info!(num_layers = self.config.num_layers, "Starting eager load");
let shared = self.load_shared()?;
store.set_shared(shared);
let num_layers = self.config.num_layers;
for layer_idx in 0..num_layers {
let tier = self.get_layer_tier(layer_idx);
match tier {
MemoryTier::Vram => {
let layer = self.load_layer(layer_idx)?;
let priority = self.get_layer_priority(layer_idx);
store.vram_cache_mut().insert(layer_idx, layer, priority)?;
},
MemoryTier::Ram => {
let cpu_weights = self.load_layer_to_cpu(layer_idx)?;
let priority = self.get_layer_priority(layer_idx);
store
.ram_cache_mut()
.insert(layer_idx, cpu_weights, priority)?;
},
MemoryTier::Nvme => {
return Err(TieredError::Config(
"EagerLoader cannot load to NVMe tier".into(),
));
},
}
if (layer_idx + 1) % 10 == 0 || layer_idx == num_layers - 1 {
tracing::info!(
layer = layer_idx + 1,
total = num_layers,
"Loading progress"
);
}
}
tracing::info!("Eager load complete");
Ok(())
}
fn progress(&self) -> f32 {
let loaded = self
.layers_loaded
.load(std::sync::atomic::Ordering::Relaxed);
loaded as f32 / self.config.num_layers as f32
}
}
impl EagerLoader {
fn get_layer_tier(&self, layer_idx: usize) -> MemoryTier {
let prefix = format!("model.layers.{layer_idx}.");
self.plan
.allocations
.iter()
.find(|(name, _)| name.starts_with(&prefix))
.map(|(_, alloc)| alloc.tier)
.unwrap_or(MemoryTier::Vram)
}
fn get_layer_priority(&self, layer_idx: usize) -> f32 {
let prefix = format!("model.layers.{layer_idx}.");
self.plan
.allocations
.iter()
.find(|(name, _)| name.starts_with(&prefix))
.map(|(_, alloc)| alloc.priority)
.unwrap_or(0.5)
}
fn vram_cache_mut<'a>(
&self,
store: &'a mut TieredWeightStore,
) -> &'a mut super::vram_cache::VramCache {
todo!("Need mut accessor")
}
fn ram_cache_mut<'a>(
&self,
store: &'a mut TieredWeightStore,
) -> &'a mut super::ram_cache::RamCache {
todo!("Need mut accessor")
}
}
pub struct ProgressiveLoader {
model_dir: PathBuf,
config: ModelConfig,
plan: AllocationPlan,
device: Arc<CudaDevice>,
prefetch_depth: usize,
stats: Arc<TieredStats>,
}
impl ProgressiveLoader {
pub fn new(
model_dir: impl AsRef<Path>,
config: ModelConfig,
plan: AllocationPlan,
device: Arc<CudaDevice>,
prefetch_depth: usize,
stats: Arc<TieredStats>,
) -> Self {
Self {
model_dir: model_dir.as_ref().to_path_buf(),
config,
plan,
device,
prefetch_depth,
stats,
}
}
fn initialize_nvme_cache(&self, store: &mut TieredWeightStore) -> Result<(), TieredError> {
let nvme = store
.nvme_cache_mut()
.ok_or_else(|| TieredError::Config("Progressive loading requires NVMe cache".into()))?;
let found = nvme.scan_existing()?;
tracing::info!(found, "Scanned NVMe cache");
Ok(())
}
}
impl WeightLoader for ProgressiveLoader {
fn load(&self, store: &mut TieredWeightStore) -> Result<(), TieredError> {
tracing::info!(
num_layers = self.config.num_layers,
prefetch_depth = self.prefetch_depth,
"Starting progressive load"
);
self.initialize_nvme_cache(store)?;
tracing::info!("Progressive loader initialized - layers will be loaded on demand");
Ok(())
}
fn progress(&self) -> f32 {
0.0
}
}
pub fn create_loader(
strategy: LoadingStrategy,
model_dir: impl AsRef<Path>,
config: ModelConfig,
plan: AllocationPlan,
tiered_config: &TieredConfig,
device: Arc<CudaDevice>,
stats: Arc<TieredStats>,
) -> Box<dyn WeightLoader> {
match strategy {
LoadingStrategy::Eager | LoadingStrategy::EagerQuantized { .. } => {
Box::new(EagerLoader::new(model_dir, config, plan, device, stats))
},
LoadingStrategy::Progressive => Box::new(ProgressiveLoader::new(
model_dir,
config,
plan,
device,
tiered_config.progressive.prefetch_depth,
stats,
)),
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_loader_factory() {
}
}