KVCache simulator for LLM serving cluster routing research
Discrete-event simulator for evaluating KV cache-aware routing policies in prefill-disaggregated LLM serving clusters. Models a two-tier KV cache hierarchy (L0 GPU HBM + L1 CPU DRAM) with RDMA/PCIe link contention, architecture-derived roofline compute (MoE, MLA, DSA), and a cluster-wide meta-store for prefix-aware routing decisions. Includes 11 routing policies (random, round_robin, least_loaded, least_tokens, ttl_aware, precise, min_pd, cache_load, cache_score, estimated_ttft, prefix_affinity), HuggingFace config.json auto-parsing, built-in GPU hardware presets (H100/H800/H20/A100/B200), and ablation tooling for systematic policy comparison across real Alibaba serving traces. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
90
src/router/random.rs
Normal file
90
src/router/random.rs
Normal file
@@ -0,0 +1,90 @@
|
||||
use rand::{Rng, SeedableRng};
|
||||
use rand_chacha::ChaCha8Rng;
|
||||
|
||||
use crate::cluster::meta_store::MetaStore;
|
||||
use crate::instance::Instance;
|
||||
use crate::router::{CandidateInfo, RouteDecision, Router};
|
||||
use crate::trace::RequestRecord;
|
||||
use crate::types::InstanceId;
|
||||
|
||||
pub struct RandomRouter {
|
||||
rng: ChaCha8Rng,
|
||||
}
|
||||
|
||||
impl RandomRouter {
|
||||
pub fn new(seed: u64) -> Self {
|
||||
Self { rng: ChaCha8Rng::seed_from_u64(seed) }
|
||||
}
|
||||
}
|
||||
|
||||
impl Router for RandomRouter {
|
||||
fn name(&self) -> &'static str {
|
||||
"random"
|
||||
}
|
||||
|
||||
fn route(
|
||||
&mut self,
|
||||
req: &RequestRecord,
|
||||
instances: &[Instance],
|
||||
_meta: &MetaStore,
|
||||
_now: f64,
|
||||
) -> RouteDecision {
|
||||
let n = instances.len();
|
||||
let chosen = self.rng.gen_range(0..n) as InstanceId;
|
||||
RouteDecision {
|
||||
req_id: req.req_id,
|
||||
mode: "random",
|
||||
chosen,
|
||||
probe_overhead_s: 0.0,
|
||||
candidates: vec![CandidateInfo {
|
||||
instance: chosen,
|
||||
predicted_prefix: 0,
|
||||
load_blocks: instances[chosen as usize].kv_blocks_used,
|
||||
queue_len: instances[chosen as usize].queue_len(),
|
||||
}],
|
||||
reason: "uniform random",
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct RoundRobinRouter {
|
||||
next: u32,
|
||||
}
|
||||
|
||||
impl RoundRobinRouter {
|
||||
pub fn new() -> Self {
|
||||
Self::default()
|
||||
}
|
||||
}
|
||||
|
||||
impl Router for RoundRobinRouter {
|
||||
fn name(&self) -> &'static str {
|
||||
"round_robin"
|
||||
}
|
||||
|
||||
fn route(
|
||||
&mut self,
|
||||
req: &RequestRecord,
|
||||
instances: &[Instance],
|
||||
_meta: &MetaStore,
|
||||
_now: f64,
|
||||
) -> RouteDecision {
|
||||
let n = instances.len() as u32;
|
||||
let chosen = self.next % n;
|
||||
self.next = self.next.wrapping_add(1);
|
||||
RouteDecision {
|
||||
req_id: req.req_id,
|
||||
mode: "round_robin",
|
||||
chosen,
|
||||
probe_overhead_s: 0.0,
|
||||
candidates: vec![CandidateInfo {
|
||||
instance: chosen,
|
||||
predicted_prefix: 0,
|
||||
load_blocks: instances[chosen as usize].kv_blocks_used,
|
||||
queue_len: instances[chosen as usize].queue_len(),
|
||||
}],
|
||||
reason: "round robin",
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user