use crate::pipeline::{MoeFfn, MoeRoute};
use crate::pool::Pool;
#[cfg(feature = "gpu")]
use std::sync::{Arc, Mutex, Once, OnceLock};
#[derive(Default)]
pub(crate) struct MiniCpmTailCache;
pub(crate) type MiniCpmTail = MiniCpmTailCache;
#[cfg(feature = "gpu")]
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
struct PoolKey {
model_uid: u64,
hidden: usize,
inter: usize,
experts: usize,
layers: usize,
}
#[cfg(feature = "gpu")]
const MINICPM_35B_TOTAL_LAYERS: usize = 40;
#[cfg(feature = "gpu")]
const MINICPM_35B_ACTIVE_TAIL_LAYERS: usize = 13;
#[cfg(feature = "gpu")]
fn active_tail_layers(total_layers: usize) -> Option<usize> {
(total_layers == MINICPM_35B_TOTAL_LAYERS).then_some(MINICPM_35B_ACTIVE_TAIL_LAYERS)
}
#[cfg(feature = "gpu")]
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
struct TailBankKey {
model_uid: u64,
device: usize,
}
#[cfg(feature = "gpu")]
#[derive(Clone, Debug, Eq, PartialEq)]
struct LayerLayout {
expert_ids: Arc<[(usize, usize, usize)]>,
shared_ids: (usize, usize, usize),
}
#[cfg(feature = "gpu")]
struct SharedTailBank {
gpu_pool: crate::qwen4_exp::QwenGpuPool,
pool_key: PoolKey,
layouts: Vec<Option<LayerLayout>>,
}
#[cfg(feature = "gpu")]
impl SharedTailBank {
fn accepts_layout_or_resets(
&mut self,
model: &Arc<cortiq_core::CmfModel>,
key: PoolKey,
li: usize,
candidate: LayerLayout,
) -> bool {
if self.pool_key != key {
return false;
}
let Some(slot) = self.layouts.get_mut(li) else {
return false;
};
let same = match slot {
Some(existing) => existing == &candidate,
None => {
*slot = Some(candidate);
return true;
}
};
if same {
return true;
}
let Some(gpu_pool) = create_tail_gpu_pool(model, key) else {
return false;
};
let mut layouts: Vec<Option<LayerLayout>> = (0..key.layers).map(|_| None).collect();
layouts[li] = Some(candidate);
self.gpu_pool = gpu_pool;
self.layouts = layouts;
true
}
}
#[cfg(feature = "gpu")]
fn create_tail_gpu_pool(
model: &Arc<cortiq_core::CmfModel>,
key: PoolKey,
) -> Option<crate::qwen4_exp::QwenGpuPool> {
let mut gpu_pool = crate::qwen4_exp::QwenGpuPool::create_explicit(
model,
key.inter,
key.hidden,
key.layers,
key.experts,
false,
bounded_slots(),
0,
)?;
if let Some(active_layers) = active_tail_layers(key.layers) {
gpu_pool.configure_minicpm_tail(active_layers);
}
Some(gpu_pool)
}
#[cfg(feature = "gpu")]
type SharedTailBankRef = Arc<Mutex<SharedTailBank>>;
#[cfg(feature = "gpu")]
fn tail_banks() -> &'static Mutex<Vec<(TailBankKey, SharedTailBankRef)>> {
static BANKS: OnceLock<Mutex<Vec<(TailBankKey, SharedTailBankRef)>>> = OnceLock::new();
BANKS.get_or_init(|| Mutex::new(Vec::new()))
}
#[cfg(feature = "gpu")]
pub(crate) fn shutdown_banks() {
let Ok(mut registry) = tail_banks().lock() else {
return;
};
let banks = std::mem::take(&mut *registry);
drop(registry);
drop(banks);
}
#[cfg(feature = "gpu")]
pub(crate) fn release_model_bank(model_uid: u64) {
let Ok(mut registry) = tail_banks().lock() else {
return;
};
let mut retired = Vec::new();
registry.retain(|(key, bank)| {
if key.model_uid == model_uid {
retired.push(bank.clone());
false
} else {
true
}
});
drop(registry);
drop(retired);
}
#[cfg(feature = "gpu")]
fn acquire_tail_bank(
model: &Arc<cortiq_core::CmfModel>,
pool_key: PoolKey,
) -> Option<SharedTailBankRef> {
let registry_key = TailBankKey {
model_uid: pool_key.model_uid,
device: crate::gpu::current_device(),
};
let mut registry = tail_banks().lock().ok()?;
if let Some((_, bank)) = registry.iter().find(|(key, _)| *key == registry_key) {
return Some(bank.clone());
}
let gpu_pool = create_tail_gpu_pool(model, pool_key)?;
let bank = Arc::new(Mutex::new(SharedTailBank {
gpu_pool,
pool_key,
layouts: (0..pool_key.layers).map(|_| None).collect(),
}));
registry.push((registry_key, bank.clone()));
Some(bank)
}
#[cfg(feature = "gpu")]
fn dynamic_tail_precision_safe() -> bool {
if std::env::var("CMF_MINICPM_DYNAMIC_TAIL").as_deref() != Ok("1") {
return false;
}
if !crate::qtensor::a8w8_enabled() {
return true;
}
static WARN: Once = Once::new();
WARN.call_once(|| {
tracing::warn!(
"MiniCPM dynamic tail disabled: its float-activation DSV4 frame cannot replace the \
normal A8W8 CPU q4tp path without changing precision; use CMF_SDOT=0 together with \
CMF_MINICPM_DYNAMIC_TAIL=1 to opt into the parity-checked exact mode"
);
});
false
}
#[cfg(feature = "gpu")]
fn prepared_frame_on() -> bool {
std::env::var("CMF_MINICPM_PREPARED_FRAME").as_deref() != Ok("0")
&& std::env::var_os("CMF_DSV4_MOE_CHECK").is_none()
}
impl MiniCpmTailCache {
#[cfg(feature = "gpu")]
pub(crate) fn is_requested_target(m: &MoeFfn) -> bool {
m.experts
.first()
.and_then(|expert| expert.gate_proj.model_arc())
.is_some_and(|model| model.arch().arch_name == "minicpmv4_7")
&& dynamic_tail_precision_safe()
}
#[cfg(not(feature = "gpu"))]
pub(crate) fn is_requested_target(_m: &MoeFfn) -> bool {
false
}
#[cfg(feature = "gpu")]
pub(crate) fn forward(
&mut self,
li: usize,
m: &MoeFfn,
x: &[f32],
route: &MoeRoute,
pool: Option<&Pool>,
total_layers: usize,
) -> Option<Vec<f32>> {
if !Self::is_requested_target(m)
|| !crate::gpu_wgpu::dsv4_global_moe_supported()
|| li >= total_layers
|| x.is_empty()
|| m.top_k != 8
|| route.idx.len() != 8
|| m.router_sigmoid
|| !m.norm_topk_prob
|| m.expert_bias.is_some()
|| (m.routed_scaling - 1.0).abs() > 1e-9
|| m.route_tau.is_some()
|| m.mask.is_some()
|| m.per_expert_scale.is_some()
|| m.router_input_norm
|| m.resonance.is_some()
|| !m.grown.is_empty()
{
return None;
}
let first = m.experts.first()?;
let model = first.gate_proj.model_arc()?;
if model.arch().arch_name != "minicpmv4_7" {
return None;
}
let hidden = x.len();
let inter = first.gate_proj.rows();
if hidden % 32 != 0 || inter == 0 || inter % 32 != 0 {
return None;
}
let expert_ids: Vec<_> = m
.experts
.iter()
.map(|expert| q4tp_triple(expert, &model, hidden, inter))
.collect::<Option<_>>()?;
let (shared, shared_gate) = m.shared.as_ref()?;
let shared_ids = q4tp_triple(shared, &model, hidden, inter)?;
if let Some(gate) = shared_gate
&& (gate.rows() != 1 || gate.cols() != hidden)
{
return None;
}
let candidate = LayerLayout {
expert_ids: expert_ids.into(),
shared_ids,
};
if m.router.rows() != candidate.expert_ids.len()
|| m.router.cols() != hidden
|| route.logits.len() != candidate.expert_ids.len()
|| route.p.len() != candidate.expert_ids.len()
|| !route.wsum.is_finite()
|| route.wsum <= 0.0
|| route
.idx
.iter()
.any(|&expert| expert >= candidate.expert_ids.len())
|| route
.idx
.iter()
.enumerate()
.any(|(i, &expert)| route.idx[..i].contains(&expert))
{
return None;
}
let mut mix_weights = vec![0.0f32; candidate.expert_ids.len()];
for &expert in &route.idx {
let weight = route.p[expert] / route.wsum;
if !weight.is_finite() {
return None;
}
mix_weights[expert] = weight;
}
let shared_weight = shared_gate.as_ref().map_or(1.0, |gate| {
let mut y = [0.0f32; 1];
crate::gpu::cpu_scope(|| gate.matvec(x, &mut y, pool));
1.0 / (1.0 + (-y[0]).exp())
});
if !shared_weight.is_finite() {
return None;
}
let key = PoolKey {
model_uid: model.uid(),
hidden,
inter,
experts: candidate.expert_ids.len(),
layers: total_layers,
};
let shared = acquire_tail_bank(&model, key)?;
let mut bank = shared.lock().ok()?;
if !bank.accepts_layout_or_resets(&model, key, li, candidate) {
return None;
}
let (expert_ids, shared_ids) = {
let layout = bank.layouts.get(li)?.as_ref()?;
(layout.expert_ids.clone(), layout.shared_ids)
};
let Some(pending) = bank.gpu_pool.begin_ensure(
&model,
li,
&route.idx,
expert_ids.as_ref(),
Some(shared_ids),
) else {
bank.gpu_pool.flush_uploads();
return None;
};
let Some(planned_cold_ids) = pending.cold_ids() else {
drop(pending);
bank.gpu_pool.flush_uploads();
return None;
};
let planned_cold_jobs: Vec<_> = planned_cold_ids
.iter()
.copied()
.map(|expert| (&m.experts[expert], mix_weights[expert]))
.collect();
let geom = crate::gpu_wgpu::Dsv4MoeGeom {
hidden,
inter,
top_k: 8,
route_scale: 1.0,
swiglu_limit: 0.0,
gu_q2: false,
bf16: false,
};
let mut out = vec![0.0f32; hidden];
let prepared = prepared_frame_on();
let (frame_ok, route_ok, cpu_ok, mut cold_cpu) = std::thread::scope(|scope| {
let cpu = (!planned_cold_jobs.is_empty()).then(|| {
scope.spawn(|| {
crate::qtensor::float_activations_scope(|| {
crate::gpu::cpu_scope(|| {
crate::pipeline::moe_cold_experts_cpu(&planned_cold_jobs, x, pool)
})
})
})
});
let ensured = pending.finish_and_flush();
let Some((remap, shared_slot, segment_slots)) = ensured else {
if let Some(job) = cpu
&& let Ok(mut discarded) = job.join()
{
crate::attention::recycle_buf(&mut discarded);
}
return (
false,
false,
false,
crate::attention::take_buf(hidden),
);
};
if remap.len() != expert_ids.len() {
if let Some(job) = cpu
&& let Ok(mut discarded) = job.join()
{
crate::attention::recycle_buf(&mut discarded);
}
return (
false,
false,
false,
crate::attention::take_buf(hidden),
);
}
let cold_ids: Vec<_> = route
.idx
.iter()
.copied()
.filter(|&expert| remap[expert] == u32::MAX)
.collect();
if cold_ids != planned_cold_ids {
if let Some(job) = cpu
&& let Ok(mut discarded) = job.join()
{
crate::attention::recycle_buf(&mut discarded);
}
return (
false,
false,
false,
crate::attention::take_buf(hidden),
);
}
let mut resident_slots = Vec::with_capacity(route.idx.len() + 1);
let mut resident_weights = Vec::with_capacity(route.idx.len() + 1);
for &expert in &route.idx {
let slot = remap[expert];
if slot != u32::MAX {
resident_slots.push(slot);
resident_weights.push(mix_weights[expert]);
}
}
resident_slots.push(shared_slot);
resident_weights.push(shared_weight);
let global = crate::gpu_wgpu::Dsv4GlobalMoe {
pool_uid: model.uid(),
shared_slot,
segment_slots: segment_slots as u32,
};
let (frame_ok, route_ok) = if prepared {
(
crate::gpu_wgpu::minicpm_tail_prepared_frame(
&model,
global,
geom,
x,
&resident_slots,
&resident_weights,
&mut out,
),
true,
)
} else {
let mut cold_from_gpu = Vec::new();
let mut cold_x = Vec::new();
let weights = crate::gpu_wgpu::Dsv4MoeW {
router: &[],
experts: expert_ids.as_ref(),
logits: &route.logits,
bias: Some(&mix_weights),
mask: None,
forced: Some(&route.idx),
remap: Some(&remap),
global: Some(global),
has_shared: true,
shared_weight,
preweighted: true,
qwen_softmax: true,
};
let frame_ok = crate::gpu_wgpu::dsv4_moe_frame(
&model,
&weights,
geom,
x,
&mut cold_from_gpu,
&mut cold_x,
None,
None,
&mut out,
);
let route_ok = cold_from_gpu.len() == cold_ids.len()
&& cold_from_gpu
.iter()
.map(|&(expert, _)| expert)
.eq(cold_ids.iter().copied());
(frame_ok, route_ok)
};
let cpu_out = cpu.and_then(|job| job.join().ok());
let cpu_ok = planned_cold_jobs.is_empty() || cpu_out.is_some();
let cold_cpu = cpu_out.unwrap_or_else(|| crate::attention::take_buf(hidden));
(frame_ok, route_ok, cpu_ok, cold_cpu)
});
drop(bank);
if !frame_ok || !route_ok || !cpu_ok {
crate::attention::recycle_buf(&mut cold_cpu);
return None;
}
for (dst, src) in out.iter_mut().zip(&cold_cpu) {
*dst += src;
}
crate::attention::recycle_buf(&mut cold_cpu);
Some(out)
}
#[cfg(not(feature = "gpu"))]
pub(crate) fn forward(
&mut self,
_li: usize,
_m: &MoeFfn,
_x: &[f32],
_route: &MoeRoute,
_pool: Option<&Pool>,
_total_layers: usize,
) -> Option<Vec<f32>> {
None
}
}
#[cfg(feature = "gpu")]
fn q4tp_triple(
expert: &crate::pipeline::DenseFfn,
model: &std::sync::Arc<cortiq_core::CmfModel>,
hidden: usize,
inter: usize,
) -> Option<(usize, usize, usize)> {
use cortiq_core::TensorDtype;
if expert.act != crate::pipeline::Act::Silu || !expert.segs.is_empty() {
return None;
}
let mapped_q4tp = |tensor: &crate::qtensor::QTensor| {
tensor.model_dtype() == Some(TensorDtype::Q4TiledP)
&& tensor.model_arc().is_some_and(|owner| owner.uid() == model.uid())
};
if !mapped_q4tp(&expert.gate_proj)
|| !mapped_q4tp(&expert.up_proj)
|| !mapped_q4tp(&expert.down_proj)
|| expert.gate_proj.rows() != inter
|| expert.gate_proj.cols() != hidden
|| expert.up_proj.rows() != inter
|| expert.up_proj.cols() != hidden
|| expert.down_proj.rows() != hidden
|| expert.down_proj.cols() != inter
{
return None;
}
Some((
expert.gate_proj.model_idx()?,
expert.up_proj.model_idx()?,
expert.down_proj.model_idx()?,
))
}
#[cfg(feature = "gpu")]
const LARGE_TAIL_BUDGET: u64 = 20 * 1024 * 1024 * 1024;
#[cfg(feature = "gpu")]
fn default_tail_slots(vram_budget: u64) -> usize {
if vram_budget >= LARGE_TAIL_BUDGET {
512
} else {
224
}
}
#[cfg(feature = "gpu")]
fn bounded_slots() -> usize {
std::env::var("CMF_MINICPM_TAIL_SLOTS")
.ok()
.and_then(|raw| raw.parse::<usize>().ok())
.unwrap_or_else(|| default_tail_slots(crate::gpu_wgpu::device_vram_budget()))
.clamp(16, 512)
}
#[cfg(all(test, feature = "gpu"))]
mod tests {
use super::{
LARGE_TAIL_BUDGET, MINICPM_35B_ACTIVE_TAIL_LAYERS,
MINICPM_35B_TOTAL_LAYERS, active_tail_layers, default_tail_slots,
};
#[test]
fn tail_slot_policy_is_bounded_before_allocator_rounding() {
assert!((16..=512).contains(&default_tail_slots(0)));
}
#[test]
fn high_budget_default_expands_without_exceeding_tail_cap() {
assert_eq!(default_tail_slots(LARGE_TAIL_BUDGET - 1), 224);
assert_eq!(default_tail_slots(LARGE_TAIL_BUDGET), 512);
assert_eq!(default_tail_slots(u64::MAX), 512);
}
#[test]
fn active_floor_is_only_enabled_for_the_known_35b_tail_topology() {
assert_eq!(
active_tail_layers(MINICPM_35B_TOTAL_LAYERS),
Some(MINICPM_35B_ACTIVE_TAIL_LAYERS)
);
assert_eq!(active_tail_layers(24), None);
}
}