use std::collections::{HashMap, HashSet, VecDeque};
use std::sync::{Arc, Mutex};
use crate::gguf::GgmlType;
#[repr(u8)]
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum ExpertKind {
Gate = 0,
Up = 1,
Down = 2,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct ExpertKey {
pub layer_idx: u16,
pub kind: ExpertKind,
pub expert_idx: u16,
}
impl ExpertKey {
#[inline]
pub const fn new(layer_idx: usize, kind: ExpertKind, expert_idx: usize) -> Self {
Self {
layer_idx: layer_idx as u16,
kind,
expert_idx: expert_idx as u16,
}
}
}
pub trait ExpertByteSource: Send + Sync {
fn as_bytes(&self) -> &[u8];
}
impl ExpertByteSource for Vec<u8> {
#[inline]
fn as_bytes(&self) -> &[u8] {
self
}
}
impl ExpertByteSource for Box<[u8]> {
#[inline]
fn as_bytes(&self) -> &[u8] {
self
}
}
#[cfg(feature = "gguf")]
impl ExpertByteSource for memmap2::Mmap {
#[inline]
fn as_bytes(&self) -> &[u8] {
self.as_ref()
}
}
#[cfg(all(unix, feature = "gguf"))]
pub fn advise_random(mmap_bytes: &[u8]) -> bool {
let ret = unsafe {
libc::madvise(
mmap_bytes.as_ptr().cast::<libc::c_void>().cast_mut(),
mmap_bytes.len(),
libc::MADV_RANDOM,
)
};
ret == 0
}
#[cfg(not(all(unix, feature = "gguf")))]
pub fn advise_random(_mmap_bytes: &[u8]) -> bool {
false
}
#[cfg(all(unix, feature = "gguf"))]
pub fn advise_willneed(mmap_bytes: &[u8]) -> bool {
if mmap_bytes.is_empty() {
return true;
}
let ret = unsafe {
libc::madvise(
mmap_bytes.as_ptr().cast::<libc::c_void>().cast_mut(),
mmap_bytes.len(),
libc::MADV_WILLNEED,
)
};
ret == 0
}
#[cfg(not(all(unix, feature = "gguf")))]
pub fn advise_willneed(_mmap_bytes: &[u8]) -> bool {
false
}
#[derive(Debug, Clone, Copy)]
pub struct ExpertLayerInfo {
pub base_offset: usize,
pub bytes_per_expert: usize,
pub n_experts: usize,
pub qtype: GgmlType,
}
pub struct LruExpertCache {
entries: HashMap<ExpertKey, Arc<Vec<u8>>>,
lru: VecDeque<ExpertKey>,
pinned: HashSet<ExpertKey>,
current_bytes: usize,
budget_bytes: usize,
hits: u64,
misses: u64,
}
impl LruExpertCache {
pub fn with_budget(budget_bytes: usize) -> Self {
Self {
entries: HashMap::new(),
lru: VecDeque::new(),
pinned: HashSet::new(),
current_bytes: 0,
budget_bytes,
hits: 0,
misses: 0,
}
}
pub fn get(&mut self, key: &ExpertKey) -> Option<Arc<Vec<u8>>> {
let hit = self.entries.get(key)?.clone();
if let Some(pos) = self.lru.iter().position(|k| k == key) {
self.lru.remove(pos);
}
self.lru.push_front(*key);
self.hits += 1;
Some(hit)
}
pub fn insert(&mut self, key: ExpertKey, bytes: Arc<Vec<u8>>) {
let incoming_bytes = bytes.len();
if let Some(old) = self.entries.remove(&key) {
self.current_bytes = self.current_bytes.saturating_sub(old.len());
if let Some(pos) = self.lru.iter().position(|k| k == &key) {
self.lru.remove(pos);
}
}
while self.current_bytes + incoming_bytes > self.budget_bytes && !self.lru.is_empty() {
let mut victim_pos: Option<usize> = None;
for i in (0..self.lru.len()).rev() {
if !self.pinned.contains(&self.lru[i]) {
victim_pos = Some(i);
break;
}
}
match victim_pos {
Some(pos) => {
let victim = self.lru.remove(pos).expect("index in bounds");
if let Some(evicted) = self.entries.remove(&victim) {
self.current_bytes = self.current_bytes.saturating_sub(evicted.len());
}
}
None => break, }
}
self.entries.insert(key, bytes);
self.lru.push_front(key);
self.current_bytes += incoming_bytes;
self.misses += 1;
}
pub fn pin(&mut self, key: ExpertKey) {
self.pinned.insert(key);
}
pub fn unpin(&mut self, key: &ExpertKey) {
self.pinned.remove(key);
}
pub fn pinned_len(&self) -> usize {
self.pinned.len()
}
pub fn hits(&self) -> u64 {
self.hits
}
pub fn misses(&self) -> u64 {
self.misses
}
pub fn current_bytes(&self) -> usize {
self.current_bytes
}
pub fn len(&self) -> usize {
self.entries.len()
}
pub fn is_empty(&self) -> bool {
self.entries.is_empty()
}
}
pub struct StreamingExpertPool {
source: Arc<dyn ExpertByteSource>,
layer_info: Vec<[ExpertLayerInfo; 3]>,
cache: Mutex<LruExpertCache>,
}
impl StreamingExpertPool {
pub fn new(
source: Arc<dyn ExpertByteSource>,
layer_info: Vec<[ExpertLayerInfo; 3]>,
budget_bytes: usize,
) -> Self {
Self {
source,
layer_info,
cache: Mutex::new(LruExpertCache::with_budget(budget_bytes)),
}
}
pub fn qtype(&self, layer_idx: usize, kind: ExpertKind) -> GgmlType {
self.layer_info[layer_idx][kind as usize].qtype
}
pub fn bytes_per_expert(&self, layer_idx: usize, kind: ExpertKind) -> usize {
self.layer_info[layer_idx][kind as usize].bytes_per_expert
}
pub fn get_or_load(
&self,
layer_idx: usize,
kind: ExpertKind,
expert_idx: usize,
) -> Arc<Vec<u8>> {
let key = ExpertKey::new(layer_idx, kind, expert_idx);
{
let mut cache = self.cache.lock().unwrap();
if let Some(hit) = cache.get(&key) {
return hit;
}
}
let info = &self.layer_info[layer_idx][kind as usize];
assert!(
expert_idx < info.n_experts,
"expert_idx {expert_idx} out of range (n_experts = {})",
info.n_experts
);
let offset = info.base_offset + expert_idx * info.bytes_per_expert;
let source_bytes = self.source.as_bytes();
assert!(
offset + info.bytes_per_expert <= source_bytes.len(),
"expert slab out of source range (offset {offset} + len {} > source len {})",
info.bytes_per_expert,
source_bytes.len()
);
let slab = Arc::new(source_bytes[offset..offset + info.bytes_per_expert].to_vec());
let mut cache = self.cache.lock().unwrap();
cache.insert(key, slab.clone());
slab
}
pub fn prefetch(&self, keys: &[ExpertKey]) {
for &k in keys {
let _ = self.get_or_load(k.layer_idx as usize, k.kind, k.expert_idx as usize);
}
}
#[cfg(feature = "parallel")]
pub fn prefetch_parallel(&self, keys: &[ExpertKey]) {
use rayon::prelude::*;
keys.par_iter().for_each(|&k| {
let _ = self.get_or_load(k.layer_idx as usize, k.kind, k.expert_idx as usize);
});
}
#[cfg(not(feature = "parallel"))]
pub fn prefetch_parallel(&self, keys: &[ExpertKey]) {
self.prefetch(keys);
}
pub fn prefetch_predicted_next_layer(
&self,
current_router_logits: &[f32],
next_layer_idx: usize,
top_k: usize,
predictor: &dyn NextLayerPredictor,
) -> usize {
let predicted = predictor.predict(current_router_logits, top_k);
let mut keys = Vec::with_capacity(predicted.len() * 3);
for e in predicted {
keys.push(ExpertKey::new(next_layer_idx, ExpertKind::Gate, e));
keys.push(ExpertKey::new(next_layer_idx, ExpertKind::Up, e));
keys.push(ExpertKey::new(next_layer_idx, ExpertKind::Down, e));
}
let n = keys.len();
self.prefetch_parallel(&keys);
n
}
pub fn pin_experts(&self, keys: &[ExpertKey]) {
let mut cache = self.cache.lock().unwrap();
for &k in keys {
cache.pin(k);
}
}
pub fn unpin_experts(&self, keys: &[ExpertKey]) {
let mut cache = self.cache.lock().unwrap();
for k in keys {
cache.unpin(k);
}
}
pub fn cache_stats(&self) -> CacheStats {
let cache = self.cache.lock().unwrap();
CacheStats {
hits: cache.hits(),
misses: cache.misses(),
current_bytes: cache.current_bytes(),
entries: cache.len(),
pinned: cache.pinned_len(),
}
}
}
pub trait NextLayerPredictor: Send + Sync {
fn predict(&self, current_router_logits: &[f32], top_k: usize) -> Vec<usize>;
}
pub struct PersistenceHeuristic;
impl NextLayerPredictor for PersistenceHeuristic {
fn predict(&self, current_router_logits: &[f32], top_k: usize) -> Vec<usize> {
let mut indexed: Vec<(usize, f32)> = current_router_logits
.iter()
.enumerate()
.map(|(i, &v)| (i, v))
.collect();
indexed.sort_by(|a, b| b.1.partial_cmp(&a.1).unwrap_or(std::cmp::Ordering::Equal));
indexed.into_iter().take(top_k).map(|(i, _)| i).collect()
}
}
#[derive(Debug, Clone, Copy)]
pub struct CacheStats {
pub hits: u64,
pub misses: u64,
pub current_bytes: usize,
pub entries: usize,
pub pinned: usize,
}
#[must_use]
pub const fn kimi_k3_active_bytes(
num_moe_layers: usize,
num_experts_per_tok: usize,
latent_hidden: usize,
moe_intermediate: usize,
bytes_per_weight_x100: usize,
) -> usize {
let per_slab = latent_hidden * moe_intermediate * bytes_per_weight_x100 / 100;
num_moe_layers * num_experts_per_tok * 3 * per_slab
}
#[must_use]
pub const fn recommended_budget_bytes(active_bytes: usize, safety_multiplier_x10: usize) -> usize {
active_bytes * safety_multiplier_x10 / 10
}
#[cfg(test)]
mod tests {
use super::*;
fn make_pool(n_experts: usize, bytes_per_expert: usize, budget: usize) -> StreamingExpertPool {
let total = 3 * n_experts * bytes_per_expert;
let data: Vec<u8> = (0..total).map(|i| (i % 251) as u8).collect();
let source: Arc<dyn ExpertByteSource> = Arc::new(data);
let mut per_kind = [ExpertLayerInfo {
base_offset: 0,
bytes_per_expert,
n_experts,
qtype: GgmlType::Q4_K,
}; 3];
per_kind[ExpertKind::Gate as usize].base_offset = 0;
per_kind[ExpertKind::Up as usize].base_offset = n_experts * bytes_per_expert;
per_kind[ExpertKind::Down as usize].base_offset = 2 * n_experts * bytes_per_expert;
StreamingExpertPool::new(source, vec![per_kind], budget)
}
#[test]
fn get_or_load_returns_deterministic_slab_from_source() {
let pool = make_pool(4, 32, 1024);
let slab = pool.get_or_load(0, ExpertKind::Gate, 2);
assert_eq!(slab.len(), 32);
for (i, &b) in slab.iter().enumerate() {
assert_eq!(b, ((64 + i) % 251) as u8, "byte {i} mismatch");
}
}
#[test]
fn second_lookup_is_a_cache_hit() {
let pool = make_pool(4, 32, 1024);
let _slab1 = pool.get_or_load(0, ExpertKind::Up, 1);
let stats_after_load = pool.cache_stats();
assert_eq!(stats_after_load.misses, 1);
assert_eq!(stats_after_load.hits, 0);
let _slab2 = pool.get_or_load(0, ExpertKind::Up, 1);
let stats_after_hit = pool.cache_stats();
assert_eq!(stats_after_hit.misses, 1, "no extra miss on second lookup");
assert_eq!(stats_after_hit.hits, 1);
}
#[test]
fn evicts_lru_when_budget_exceeded() {
let pool = make_pool(4, 32, 64);
let _e0 = pool.get_or_load(0, ExpertKind::Gate, 0);
let _e1 = pool.get_or_load(0, ExpertKind::Gate, 1);
assert_eq!(pool.cache_stats().current_bytes, 64);
let _e2 = pool.get_or_load(0, ExpertKind::Gate, 2);
let stats = pool.cache_stats();
assert_eq!(stats.current_bytes, 64);
assert_eq!(
stats.entries, 2,
"cache holds exactly 2 entries after eviction"
);
let _e0_again = pool.get_or_load(0, ExpertKind::Gate, 0);
let stats2 = pool.cache_stats();
assert_eq!(stats2.misses, 4, "expert 0 reload is a fresh miss");
}
#[test]
fn get_bumps_lru_position() {
let pool = make_pool(4, 32, 64);
let _e0 = pool.get_or_load(0, ExpertKind::Gate, 0);
let _e1 = pool.get_or_load(0, ExpertKind::Gate, 1);
let _e0_again = pool.get_or_load(0, ExpertKind::Gate, 0);
let _e2 = pool.get_or_load(0, ExpertKind::Gate, 2);
let stats = pool.cache_stats();
assert_eq!(stats.entries, 2);
let hits_before = pool.cache_stats().hits;
let _ = pool.get_or_load(0, ExpertKind::Gate, 0);
let _ = pool.get_or_load(0, ExpertKind::Gate, 2);
let hits_after = pool.cache_stats().hits;
assert_eq!(hits_after - hits_before, 2, "both 0 and 2 should hit");
}
#[test]
fn different_kinds_do_not_collide() {
let pool = make_pool(4, 32, 1024);
let gate = pool.get_or_load(0, ExpertKind::Gate, 1);
let up = pool.get_or_load(0, ExpertKind::Up, 1);
let down = pool.get_or_load(0, ExpertKind::Down, 1);
assert_eq!(gate[0], 32_u8);
assert_eq!(up[0], 160_u8);
assert_eq!(down[0], (288 % 251) as u8);
assert_eq!(pool.cache_stats().entries, 3);
assert_eq!(pool.cache_stats().misses, 3);
}
#[test]
fn arc_lets_caller_outlive_eviction() {
let pool = make_pool(4, 32, 64);
let e0 = pool.get_or_load(0, ExpertKind::Gate, 0);
let expected: Vec<u8> = e0.iter().copied().collect();
let _e1 = pool.get_or_load(0, ExpertKind::Gate, 1);
let _e2 = pool.get_or_load(0, ExpertKind::Gate, 2);
let stats = pool.cache_stats();
assert!(!stats.entries == 0 || stats.entries <= 2);
assert_eq!(*e0, expected, "arc-held slab must survive eviction");
}
#[test]
fn prefetch_batch_warms_all_keys() {
let pool = make_pool(4, 32, 1024);
let keys = vec![
ExpertKey::new(0, ExpertKind::Gate, 0),
ExpertKey::new(0, ExpertKind::Gate, 1),
ExpertKey::new(0, ExpertKind::Up, 2),
ExpertKey::new(0, ExpertKind::Down, 3),
];
pool.prefetch(&keys);
let after_prefetch = pool.cache_stats();
assert_eq!(after_prefetch.misses, 4, "prefetch loads every key");
assert_eq!(after_prefetch.entries, 4);
for key in &keys {
let _ = pool.get_or_load(key.layer_idx as usize, key.kind, key.expert_idx as usize);
}
let after_get = pool.cache_stats();
assert_eq!(after_get.misses, 4, "no fresh misses after prefetch");
assert_eq!(after_get.hits, 4);
}
#[test]
fn prefetch_is_noop_for_already_cached_keys() {
let pool = make_pool(4, 32, 1024);
let _ = pool.get_or_load(0, ExpertKind::Gate, 0);
assert_eq!(pool.cache_stats().misses, 1);
let keys = vec![
ExpertKey::new(0, ExpertKind::Gate, 0), ExpertKey::new(0, ExpertKind::Gate, 1), ];
pool.prefetch(&keys);
let stats = pool.cache_stats();
assert_eq!(
stats.misses, 2,
"only the fresh key increments miss counter"
);
assert_eq!(
stats.hits, 1,
"cached key increments hit counter via LRU bump"
);
}
#[test]
fn prefetch_parallel_matches_sequential_semantics() {
let pool_seq = make_pool(8, 32, 4096);
let pool_par = make_pool(8, 32, 4096);
let keys: Vec<ExpertKey> = (0..8)
.flat_map(|e| {
[ExpertKind::Gate, ExpertKind::Up, ExpertKind::Down]
.into_iter()
.map(move |k| ExpertKey::new(0, k, e))
})
.collect();
pool_seq.prefetch(&keys);
pool_par.prefetch_parallel(&keys);
let s_seq = pool_seq.cache_stats();
let s_par = pool_par.cache_stats();
assert_eq!(
s_seq.entries, s_par.entries,
"prefetch_parallel entry count must match sequential"
);
assert_eq!(
s_seq.current_bytes, s_par.current_bytes,
"prefetch_parallel byte total must match sequential"
);
}
#[test]
fn pinned_entry_survives_budget_pressure() {
let pool = make_pool(4, 32, 64);
let pinned_key = ExpertKey::new(0, ExpertKind::Gate, 0);
pool.pin_experts(&[pinned_key]);
assert_eq!(pool.cache_stats().pinned, 1);
let _ = pool.get_or_load(0, ExpertKind::Gate, 0); let _ = pool.get_or_load(0, ExpertKind::Gate, 1);
let _ = pool.get_or_load(0, ExpertKind::Gate, 2); let _ = pool.get_or_load(0, ExpertKind::Gate, 3);
let hits_before = pool.cache_stats().hits;
let _ = pool.get_or_load(0, ExpertKind::Gate, 0);
let hits_after = pool.cache_stats().hits;
assert_eq!(
hits_after - hits_before,
1,
"pinned key must remain in cache"
);
}
#[test]
fn unpin_makes_entry_evictable_again() {
let pool = make_pool(4, 32, 64);
let key = ExpertKey::new(0, ExpertKind::Gate, 0);
pool.pin_experts(&[key]);
let _ = pool.get_or_load(0, ExpertKind::Gate, 0);
let _ = pool.get_or_load(0, ExpertKind::Gate, 1);
pool.unpin_experts(&[key]);
assert_eq!(pool.cache_stats().pinned, 0);
let _ = pool.get_or_load(0, ExpertKind::Gate, 2);
let _ = pool.get_or_load(0, ExpertKind::Gate, 3);
let misses_before = pool.cache_stats().misses;
let _ = pool.get_or_load(0, ExpertKind::Gate, 0);
let misses_after = pool.cache_stats().misses;
assert_eq!(
misses_after - misses_before,
1,
"unpinned key must be evictable"
);
}
#[test]
fn pinning_all_entries_bypasses_eviction_bound() {
let pool = make_pool(4, 32, 64);
let keys = [
ExpertKey::new(0, ExpertKind::Gate, 0),
ExpertKey::new(0, ExpertKind::Gate, 1),
ExpertKey::new(0, ExpertKind::Gate, 2),
];
pool.pin_experts(&keys);
for k in &keys {
let _ = pool.get_or_load(0, ExpertKind::Gate, k.expert_idx as usize);
}
let stats = pool.cache_stats();
assert_eq!(stats.entries, 3, "all 3 pinned keys stay in cache");
assert!(
stats.current_bytes > 0,
"pin overrides byte-budget lower bound"
);
}
#[test]
fn persistence_heuristic_picks_top_k_by_logit() {
let logits = vec![0.1, 0.9, 0.3, 0.7, 0.5];
let predicted = PersistenceHeuristic.predict(&logits, 3);
assert_eq!(predicted, vec![1, 3, 4]);
}
#[test]
fn persistence_heuristic_handles_top_k_bigger_than_n_experts() {
let logits = vec![0.1, 0.5];
let predicted = PersistenceHeuristic.predict(&logits, 8);
assert_eq!(predicted, vec![1, 0]);
}
#[test]
fn prefetch_predicted_next_layer_warms_next_layer() {
let n_experts = 4;
let bytes_per_expert = 32;
let n_layers = 2;
let total = n_layers * 3 * n_experts * bytes_per_expert;
let data: Vec<u8> = (0..total).map(|i| (i % 251) as u8).collect();
let source: Arc<dyn ExpertByteSource> = Arc::new(data);
let mut layer_info = Vec::with_capacity(n_layers);
for layer_idx in 0..n_layers {
let base = layer_idx * 3 * n_experts * bytes_per_expert;
let mut per_kind = [ExpertLayerInfo {
base_offset: 0,
bytes_per_expert,
n_experts,
qtype: GgmlType::Q4_K,
}; 3];
per_kind[ExpertKind::Gate as usize].base_offset = base;
per_kind[ExpertKind::Up as usize].base_offset = base + n_experts * bytes_per_expert;
per_kind[ExpertKind::Down as usize].base_offset =
base + 2 * n_experts * bytes_per_expert;
layer_info.push(per_kind);
}
let pool = StreamingExpertPool::new(source, layer_info, 4096);
let logits = vec![0.1, 0.9, 0.3, 0.7];
let top_k = 2;
let n_prefetched = pool.prefetch_predicted_next_layer(
&logits,
1,
top_k,
&PersistenceHeuristic,
);
assert_eq!(n_prefetched, top_k * 3, "3 kinds per predicted expert");
let hits_before = pool.cache_stats().hits;
for &e in &[1usize, 3] {
let _ = pool.get_or_load(1, ExpertKind::Gate, e);
let _ = pool.get_or_load(1, ExpertKind::Up, e);
let _ = pool.get_or_load(1, ExpertKind::Down, e);
}
let hits_after = pool.cache_stats().hits;
assert_eq!(
hits_after - hits_before,
6,
"persistence heuristic correctly pre-warmed next-layer routing"
);
}
#[test]
#[cfg(all(unix, feature = "gguf"))]
fn advise_random_returns_true_for_valid_mmap() {
use std::io::Write;
let mut tf = tempfile_alt();
tf.write_all(b"hello alice-llm streaming pool madvise test")
.unwrap();
tf.flush().unwrap();
let mmap = unsafe { memmap2::Mmap::map(&tf) }.unwrap();
assert!(
super::advise_random(&mmap),
"madvise MADV_RANDOM must succeed on a valid mmap"
);
}
#[cfg(all(unix, feature = "gguf"))]
fn tempfile_alt() -> std::fs::File {
let mut path = std::env::temp_dir();
path.push(format!("alice_llm_streaming_test_{}", std::process::id()));
let file = std::fs::OpenOptions::new()
.read(true)
.write(true)
.create(true)
.truncate(true)
.open(&path)
.unwrap();
let _ = std::fs::remove_file(&path);
file
}
#[test]
fn zero_budget_evicts_immediately() {
let pool = make_pool(4, 32, 0);
let _e0 = pool.get_or_load(0, ExpertKind::Gate, 0);
let stats = pool.cache_stats();
assert!(
stats.entries <= 1,
"zero-budget cache must not accumulate entries"
);
}
#[test]
fn kimi_k3_active_bytes_matches_paper_estimate() {
let active = kimi_k3_active_bytes(
92, 16, 3584, 3072, 50, );
assert_eq!(active, 92 * 16 * 3 * 3584 * 3072 / 2);
let gb = active / 1_000_000_000;
assert!(
(20..=30).contains(&gb),
"K3 active bytes {active} ({gb} GB) outside 20-30 GB paper estimate window"
);
}
#[test]
fn recommended_budget_applies_safety_multiplier() {
let active = 20_000_000_000_usize; assert_eq!(recommended_budget_bytes(active, 12), 24_000_000_000);
assert_eq!(recommended_budget_bytes(active, 17), 34_000_000_000);
assert_eq!(recommended_budget_bytes(active, 10), 20_000_000_000);
}
#[test]
fn pool_supports_kimi_k3_896_experts_top16_dispatch() {
let n_experts = 896;
let bytes_per_expert = 1024; let per_layer_bytes = 3 * n_experts * bytes_per_expert; let budget = 16 * 3 * bytes_per_expert * 4;
let pool = make_pool(n_experts, bytes_per_expert, budget);
assert_eq!(pool.bytes_per_expert(0, ExpertKind::Gate), bytes_per_expert);
let top16: [usize; 16] = [
7, 42, 100, 200, 300, 400, 500, 600, 700, 800, 850, 890, 895, 3, 17, 128,
];
for &e in &top16 {
let g = pool.get_or_load(0, ExpertKind::Gate, e);
let u = pool.get_or_load(0, ExpertKind::Up, e);
let d = pool.get_or_load(0, ExpertKind::Down, e);
assert_eq!(g.len(), bytes_per_expert);
assert_eq!(u.len(), bytes_per_expert);
assert_eq!(d.len(), bytes_per_expert);
}
let stats_before = pool.cache_stats();
for &e in &top16 {
let _g = pool.get_or_load(0, ExpertKind::Gate, e);
let _u = pool.get_or_load(0, ExpertKind::Up, e);
let _d = pool.get_or_load(0, ExpertKind::Down, e);
}
let stats_after = pool.cache_stats();
assert_eq!(
stats_after.hits - stats_before.hits,
48,
"top-16 × 3-slab re-fetch must be all hits inside the 48-slab budget"
);
assert_eq!(
stats_after.misses, stats_before.misses,
"no fresh misses on the re-fetch"
);
let last = pool.get_or_load(0, ExpertKind::Down, 895);
assert_eq!(last.len(), bytes_per_expert);
let _ = per_layer_bytes;
}
#[test]
fn pool_rejects_out_of_range_expert_index_at_896() {
let pool = make_pool(896, 128, 4096);
let ok = std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| {
let _ = pool.get_or_load(0, ExpertKind::Gate, 896);
}));
assert!(
ok.is_err(),
"expert_idx = n_experts must panic (out of range)"
);
}
#[test]
fn persistence_heuristic_scales_to_896_experts_top16() {
use super::{NextLayerPredictor, PersistenceHeuristic};
let mut logits = vec![0.0_f32; 896];
for (i, l) in logits.iter_mut().enumerate() {
*l = i as f32;
}
let mut picks = PersistenceHeuristic.predict(&logits, 16);
assert_eq!(picks.len(), 16, "top-16 selection returned wrong count");
picks.sort_unstable();
let expected: Vec<usize> = (880..896).collect();
assert_eq!(picks, expected, "top-16 must be the last 16 indices");
}
}