use std::collections::{BTreeMap, BTreeSet};
use crate::common::enums::{DatabaseMode, FmhaQuantMode, GemmQuantMode, KvCacheQuantMode};
use crate::common::error::AicError;
use crate::operators::base::{blend_sol, PerformanceResult, Source};
use crate::operators::communication::NcclOp;
use crate::operators::util_empirical::{self, UtilGrid};
use crate::perf_database::dsa::{
bs_slice, dsa_context_sol, dsa_context_sol_flops, dsa_context_sol_ms, dsa_dims,
dsa_generation_sol, dsa_generation_sol_flops, dsa_generation_sol_ms, dsa_sparse_file_prefix,
lookup_2d, DsaHeadGrid, DsaKey, DsaSparseTables,
};
use crate::perf_database::perf_interp::LeafValue;
use crate::perf_database::PerfDatabase;
use serde::{Deserialize, Serialize};
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
pub struct DsaModuleOp {
pub name: String,
pub scale_factor: f64,
pub num_heads: u32,
pub kv_cache_dtype: KvCacheQuantMode,
pub fmha_quant_mode: FmhaQuantMode,
pub gemm_quant_mode: GemmQuantMode,
pub architecture: String,
pub index_topk: u32,
#[serde(default = "default_cp_size")]
pub cp_size: u32,
#[serde(default = "default_full_frac")]
pub full_frac: f64,
#[serde(default)]
pub attn_projection_quant_modes: Option<DsaProjectionQuants>,
}
#[derive(Clone, Copy, Debug, PartialEq, Serialize, Deserialize)]
pub struct DsaProjectionQuants {
pub q: GemmQuantMode,
pub kv: GemmQuantMode,
pub o: GemmQuantMode,
pub indexer: GemmQuantMode,
}
fn default_cp_size() -> u32 {
1
}
fn default_full_frac() -> f64 {
1.0
}
impl DsaModuleOp {
#[allow(clippy::too_many_arguments)]
pub fn new(
name: impl Into<String>,
num_heads: u32,
kv_cache_dtype: KvCacheQuantMode,
fmha_quant_mode: FmhaQuantMode,
gemm_quant_mode: GemmQuantMode,
architecture: impl Into<String>,
index_topk: u32,
) -> Self {
Self {
name: name.into(),
scale_factor: 1.0,
num_heads,
kv_cache_dtype,
fmha_quant_mode,
gemm_quant_mode,
architecture: architecture.into(),
index_topk,
cp_size: 1,
full_frac: 1.0,
attn_projection_quant_modes: None,
}
}
pub fn weight_bytes(&self) -> f64 {
let dims = crate::perf_database::dsa::dsa_dims(&self.architecture);
let h = dims.hidden_size as f64;
let q_lora = dims.q_lora_rank as f64;
let kv_lora = dims.kv_lora_rank as f64;
let qk = (dims.qk_nope_head_dim + dims.qk_rope_head_dim) as f64;
let v = dims.v_head_dim as f64;
let idx = (dims.index_head_dim * dims.index_n_heads) as f64;
let local_heads = f64::from(self.num_heads);
let quants = self
.attn_projection_quant_modes
.unwrap_or(DsaProjectionQuants {
q: self.gemm_quant_mode,
kv: self.gemm_quant_mode,
o: self.gemm_quant_mode,
indexer: self.gemm_quant_mode,
});
let q_params = h * q_lora + q_lora * local_heads * qk;
let kv_params = h * (kv_lora + dims.qk_rope_head_dim as f64)
+ kv_lora * local_heads * (dims.qk_nope_head_dim as f64 + v);
let o_params = local_heads * v * h;
let indexer_params = q_lora * idx + h * dims.index_n_heads as f64;
let bytes = q_params * quants.q.mapping().memory
+ kv_params * quants.kv.mapping().memory
+ o_params * quants.o.mapping().memory
+ indexer_params * quants.indexer.mapping().memory;
bytes * self.scale_factor
}
fn effective_full_frac(&self, has_skip_rows: bool) -> f64 {
if self.full_frac >= 1.0 || has_skip_rows {
self.full_frac
} else {
1.0
}
}
pub fn query_context(
&self,
db: &PerfDatabase,
batch_size: u32,
isl: u32,
prefix: u32,
) -> Result<PerformanceResult, AicError> {
if self.cp_size > 1 && db.database_mode == DatabaseMode::SolFull {
return Err(AicError::InvalidEngineConfig(format!(
"DSA context SOL_FULL decomposition is not supported for cp_size={} because the CP sparse MQA/top-k deltas are latency-only",
self.cp_size
)));
}
let w = if self.full_frac >= 1.0
|| matches!(db.database_mode, DatabaseMode::Sol | DatabaseMode::SolFull)
{
self.full_frac
} else {
self.effective_full_frac(db.dsa.has_context_skip_rows()?)
};
if self.cp_size > 1 {
let full = self.query_context_cp(db, batch_size, isl, prefix, false)?;
if w >= 1.0 {
return Ok(full);
}
let skip = self.query_context_cp(db, batch_size, isl, prefix, true)?;
let mut result = PerformanceResult::new(
w * full.latency_ms + (1.0 - w) * skip.latency_ms,
full.source,
);
if let Some(components) = blend_sol(w, full.sol, skip.sol) {
result = result.with_sol(components);
}
return Ok(result);
}
let q = |skip_indexer: bool| {
query_context_table(db, self, batch_size, isl, prefix, "trtllm", skip_indexer)
};
let full = q(false)?;
let result = if w >= 1.0 {
full
} else {
let skip = q(true)?;
let mut blended = PerformanceResult::with_energy(
w * full.latency_ms + (1.0 - w) * skip.latency_ms,
w * full.energy_wms + (1.0 - w) * skip.energy_wms,
full.source.combine(skip.source),
);
if let Some(components) = blend_sol(w, full.sol, skip.sol) {
blended = blended.with_sol(components);
}
blended
};
Ok(result.clamp_non_negative().scaled(self.scale_factor))
}
fn query_context_cp(
&self,
db: &PerfDatabase,
batch_size: u32,
isl: u32,
prefix: u32,
skip_indexer: bool,
) -> Result<PerformanceResult, AicError> {
let sparse = db.dsa.load_cp_sparse(&self.architecture, self.num_heads)?;
let mut base = |per_card: u32| {
query_context_table(
db,
self,
batch_size,
per_card,
prefix,
"flashmla_kv",
skip_indexer,
)
.map(|r| r.latency_ms)
};
let mut ag = |elems: u64| {
NcclOp::new(
format!("{}_cp_all_gather", self.name),
1.0,
elems as f64,
self.cp_size,
"all_gather",
)
.query(db, 1)
.map(|r| r.latency_ms)
};
self.query_cp_with(
&sparse,
batch_size,
isl,
prefix,
skip_indexer,
&mut base,
&mut ag,
)
}
fn query_cp_with(
&self,
sparse: &DsaSparseTables,
b: u32,
isl: u32,
prefix: u32,
skip_indexer: bool,
base: &mut dyn FnMut(u32) -> Result<f64, AicError>,
ag: &mut dyn FnMut(u64) -> Result<f64, AicError>,
) -> Result<PerformanceResult, AicError> {
let cp = self.cp_size;
let per_card = isl.div_ceil(cp).max(1); let file_prefix = dsa_sparse_file_prefix(&self.architecture);
let missing: Vec<&str> = if skip_indexer {
Vec::new()
} else {
[
("mqa", &sparse.mqa),
("topk_last", &sparse.topk_last),
("topk_flat", &sparse.topk_flat),
]
.into_iter()
.filter(|(_, grid)| grid.is_empty())
.map(|(name, _)| name)
.collect()
};
if !missing.is_empty() {
return Err(AicError::PerfDatabase(format!(
"DSA CP modeling needs sparse tables ['{}'] for {} (num_heads={}); \
collect {file_prefix}_mqa_logits/{file_prefix}_topk first.",
missing.join("', '"),
self.architecture,
self.num_heads
)));
}
let dsa_base = base(per_card)?;
let mqa_tab = bs_slice(&sparse.mqa, b);
let tl_tab = bs_slice(&sparse.topk_last, b);
let tf_tab = bs_slice(&sparse.topk_flat, b);
let empty = std::collections::BTreeMap::new();
let mqa_tab = mqa_tab.unwrap_or(&empty);
let tl_tab = tl_tab.unwrap_or(&empty);
let tf_tab = tf_tab.unwrap_or(&empty);
let mqa_full = lookup_2d(mqa_tab, isl, prefix)?;
let mqa_perc = lookup_2d(mqa_tab, per_card, prefix)?;
let tl_full = lookup_2d(tl_tab, isl, prefix)?;
let tf_perc = lookup_2d(tf_tab, per_card, prefix)?;
let mut latency = dsa_base;
if !skip_indexer {
if let (Some(mqa_full), Some(mqa_perc), Some(tl_full), Some(tf_perc)) =
(mqa_full, mqa_perc, tl_full, tf_perc)
{
let delta_mqa = mqa_full / f64::from(cp) - mqa_perc;
let delta_topk = tl_full / f64::from(cp) - tf_perc;
latency += delta_mqa + delta_topk;
}
}
let dims = dsa_dims(&self.architecture);
let tokens = u64::from(b) * u64::from(isl);
let ag_kv = if skip_indexer {
0.0
} else {
ag(tokens * dims.index_head_dim as u64)?
};
let ag_lse = ag(tokens * (dims.kv_lora_rank + dims.qk_rope_head_dim) as u64)?;
latency += ag_kv + ag_lse;
Ok(PerformanceResult::new(latency, Source::Estimated).scaled(self.scale_factor))
}
pub fn query_generation(
&self,
db: &PerfDatabase,
batch_size: u32,
s: u32,
) -> Result<PerformanceResult, AicError> {
let w = if self.full_frac >= 1.0
|| matches!(db.database_mode, DatabaseMode::Sol | DatabaseMode::SolFull)
{
self.full_frac
} else {
self.effective_full_frac(db.dsa.has_generation_skip_rows()?)
};
let q = |skip_indexer: bool| {
query_generation_table(db, self, batch_size, s, "trtllm", skip_indexer)
};
let full = q(false)?;
let result = if w >= 1.0 {
full
} else {
let skip = q(true)?;
let mut blended = PerformanceResult::with_energy(
w * full.latency_ms + (1.0 - w) * skip.latency_ms,
w * full.energy_wms + (1.0 - w) * skip.energy_wms,
full.source.combine(skip.source),
);
if let Some(components) = blend_sol(w, full.sol, skip.sol) {
blended = blended.with_sol(components);
}
blended
};
Ok(result.clamp_non_negative().scaled(self.scale_factor))
}
}
fn query_context_table(
db: &PerfDatabase,
op: &DsaModuleOp,
b: u32,
isl: u32,
prefix: u32,
dsa_backend: &str,
skip_indexer: bool,
) -> Result<PerformanceResult, AicError> {
let silicon = || {
db.dsa
.query_context(
&db.system_spec,
b,
isl,
op.num_heads,
op.kv_cache_dtype,
op.fmha_quant_mode,
op.gemm_quant_mode,
&op.architecture,
prefix,
op.index_topk,
dsa_backend,
skip_indexer,
)
.map(|v| PerformanceResult::with_energy(v.latency, v.energy, Source::Silicon))
};
match db.database_mode {
DatabaseMode::Sol | DatabaseMode::SolFull => {
let spec = &db.system_spec;
let dims = dsa_dims(&op.architecture);
let flops = dsa_context_sol_flops(spec, op.gemm_quant_mode, op.fmha_quant_mode)?;
Ok(PerformanceResult::sol(dsa_context_sol(
spec,
dims,
op.index_topk as i64,
op.kv_cache_dtype,
op.fmha_quant_mode,
op.gemm_quant_mode,
b as i64,
isl as i64,
prefix as i64,
op.num_heads as i64,
skip_indexer,
flops,
)))
}
DatabaseMode::Empirical => Ok(PerformanceResult::new(
context_empirical(db, op, b, isl, prefix, dsa_backend, skip_indexer)?,
Source::Empirical,
)),
DatabaseMode::Hybrid => match silicon() {
Ok(result) => Ok(result),
Err(err) if err.is_missing_perf_data() => Ok(PerformanceResult::new(
context_empirical(db, op, b, isl, prefix, dsa_backend, skip_indexer)?,
Source::Empirical,
)),
Err(err) => Err(err),
},
_ => silicon(),
}
}
fn query_generation_table(
db: &PerfDatabase,
op: &DsaModuleOp,
b: u32,
s: u32,
dsa_backend: &str,
skip_indexer: bool,
) -> Result<PerformanceResult, AicError> {
let silicon = || {
db.dsa
.query_generation(
&db.system_spec,
b,
s,
op.num_heads,
op.kv_cache_dtype,
op.fmha_quant_mode,
op.gemm_quant_mode,
&op.architecture,
dsa_backend,
skip_indexer,
)
.map(|v| PerformanceResult::with_energy(v.latency, v.energy, Source::Silicon))
};
match db.database_mode {
DatabaseMode::Sol | DatabaseMode::SolFull => {
let spec = &db.system_spec;
let dims = dsa_dims(&op.architecture);
let flops = dsa_generation_sol_flops(spec, op.gemm_quant_mode)?;
Ok(PerformanceResult::sol(dsa_generation_sol(
spec,
dims,
op.kv_cache_dtype,
op.gemm_quant_mode,
b as i64,
s as i64,
op.num_heads as i64,
flops,
)))
}
DatabaseMode::Empirical => Ok(PerformanceResult::new(
generation_empirical(db, op, b, s, dsa_backend, skip_indexer)?,
Source::Empirical,
)),
DatabaseMode::Hybrid => match silicon() {
Ok(result) => Ok(result),
Err(err) if err.is_missing_perf_data() => Ok(PerformanceResult::new(
generation_empirical(db, op, b, s, dsa_backend, skip_indexer)?,
Source::Empirical,
)),
Err(err) => Err(err),
},
_ => silicon(),
}
}
#[derive(Clone, Copy)]
enum CtxSolShape {
PrefixSeqBatch,
HeadPrefixSeqBatch,
SeqBatchP0,
HeadSeqBatchP0,
}
enum CtxCalibration<'a> {
HeadPrefix(&'a BTreeMap<u32, BTreeMap<u32, BTreeMap<u32, LeafValue>>>),
AllHeads(&'a DsaHeadGrid),
HeadP0(&'a BTreeMap<u32, BTreeMap<u32, LeafValue>>),
AllHeadsP0(&'a DsaHeadGrid),
Missing,
}
fn context_empirical(
db: &PerfDatabase,
op: &DsaModuleOp,
b: u32,
s: u32,
prefix: u32,
dsa_backend: &str,
skip_indexer: bool,
) -> Result<f64, AicError> {
let spec = &db.system_spec;
let dims = dsa_dims(&op.architecture);
let topk = op.index_topk as i64;
let (kv, fmha, gemm) = (op.kv_cache_dtype, op.fmha_quant_mode, op.gemm_quant_mode);
let flops = dsa_context_sol_flops(spec, gemm, fmha)?;
let sol = |b: f64, s: f64, prefix: f64, num_heads: f64| {
dsa_context_sol_ms(
spec,
dims,
topk,
kv,
fmha,
gemm,
b as i64,
s as i64,
prefix as i64,
num_heads as i64,
skip_indexer,
flops,
)
};
let sol_time = sol(b as f64, s as f64, prefix as f64, op.num_heads as f64);
let key = DsaKey {
architecture: op.architecture.clone(),
fmha_quant: fmha.name().to_string(),
kv_quant: kv.name().to_string(),
gemm_quant: gemm.name().to_string(),
};
let slice = match db.dsa.context_raw_slice(&key, dsa_backend, skip_indexer) {
Ok(slice) => Some(slice),
Err(err) if err.is_missing_perf_data() => None,
Err(err) => return Err(err),
};
let has_prefix = match slice {
Some(_) => true,
None => op.architecture == "GlmMoeDsaForCausalLM",
};
let head_grid = slice
.and_then(|slc| slc.get(&op.num_heads))
.filter(|head_grid| !head_grid.is_empty());
let exact_head = head_grid.is_some();
let prefix_keys: Vec<u32> = if let Some(head_grid) = head_grid {
head_grid.keys().copied().collect()
} else if let Some(slc) = slice {
let mut seen: BTreeSet<u32> = BTreeSet::new();
for by_prefix in slc.values() {
seen.extend(by_prefix.keys().copied());
}
seen.into_iter().collect()
} else {
Vec::new()
};
let interp_prefix = prefix_keys.len() >= 2
&& prefix_keys[0] <= prefix
&& prefix <= *prefix_keys.last().expect("non-empty prefix keys");
let (heads_f, prefix_f, s_f, b_f) = (op.num_heads as f64, prefix as f64, s as f64, b as f64);
let (tag, depth, query, shape, calibration, head_scoped): (
&'static str,
usize,
Vec<f64>,
CtxSolShape,
CtxCalibration,
bool,
) = if has_prefix && interp_prefix {
if exact_head {
(
"ctx_dsa_exact_head",
3,
vec![prefix_f, s_f, b_f],
CtxSolShape::PrefixSeqBatch,
CtxCalibration::HeadPrefix(head_grid.expect("exact head slice")),
true,
)
} else {
(
"ctx_dsa",
4,
vec![heads_f, prefix_f, s_f, b_f],
CtxSolShape::HeadPrefixSeqBatch,
CtxCalibration::AllHeads(slice.expect("populated slice")),
false,
)
}
} else if has_prefix && prefix_keys.contains(&0) {
if exact_head {
(
"ctx_dsa_p0anchor_exact_head",
2,
vec![s_f + prefix_f, b_f],
CtxSolShape::SeqBatchP0,
CtxCalibration::HeadP0(&head_grid.expect("exact head slice")[&0]),
true,
)
} else {
(
"ctx_dsa_p0anchor",
3,
vec![heads_f, s_f + prefix_f, b_f],
CtxSolShape::HeadSeqBatchP0,
CtxCalibration::AllHeadsP0(slice.expect("populated slice")),
false,
)
}
} else if has_prefix {
if exact_head {
(
"ctx_dsa_prefix_boundary_exact_head",
3,
vec![prefix_f, s_f, b_f],
CtxSolShape::PrefixSeqBatch,
CtxCalibration::HeadPrefix(head_grid.expect("exact head slice")),
true,
)
} else {
(
"ctx_dsa_prefix_boundary",
4,
vec![heads_f, prefix_f, s_f, b_f],
CtxSolShape::HeadPrefixSeqBatch,
slice.map_or(CtxCalibration::Missing, CtxCalibration::AllHeads),
false,
)
}
} else {
if exact_head {
(
"ctx_dsa_legacy_exact_head",
2,
vec![s_f + prefix_f, b_f],
CtxSolShape::SeqBatchP0,
CtxCalibration::Missing,
true,
)
} else {
(
"ctx_dsa_legacy",
3,
vec![heads_f, s_f + prefix_f, b_f],
CtxSolShape::HeadSeqBatchP0,
CtxCalibration::Missing,
false,
)
}
};
let sample_sol = |c: &[f64]| match shape {
CtxSolShape::PrefixSeqBatch => sol(c[2], c[1], c[0], heads_f),
CtxSolShape::HeadPrefixSeqBatch => sol(c[3], c[2], c[1], c[0]),
CtxSolShape::SeqBatchP0 => sol(c[1], c[0], 0.0, heads_f),
CtxSolShape::HeadSeqBatchP0 => sol(c[2], c[1], 0.0, c[0]),
};
let skip_tag = if skip_indexer { ":skip" } else { "" };
let cache_key = if head_scoped {
format!(
"{tag}:{}:{}:{}:{}:{dsa_backend}:{depth}:h{}{skip_tag}",
fmha.name(),
kv.name(),
gemm.name(),
op.architecture,
op.num_heads
)
} else {
format!(
"{tag}:{}:{}:{}:{}:{dsa_backend}:{depth}{skip_tag}",
fmha.name(),
kv.name(),
gemm.name(),
op.architecture
)
};
let grid = db.util_grids.get_or_try_build(&cache_key, || {
let points = match calibration {
CtxCalibration::HeadPrefix(head_grid) => context_points_head(head_grid),
CtxCalibration::AllHeads(slc) => context_points_all(slc),
CtxCalibration::HeadP0(p0) => points_2d(p0),
CtxCalibration::AllHeadsP0(slc) => context_points_all_p0(slc),
CtxCalibration::Missing => return Ok(None),
};
Ok(Some(UtilGrid::new(util_empirical::build_samples(
points, sample_sol,
))))
})?;
let (latency, _) = util_empirical::estimate(sol_time, &query, grid.as_deref(), 1.0)?;
db.note_provenance(util_empirical::ProvenanceTier::Empirical);
Ok(latency)
}
fn generation_empirical(
db: &PerfDatabase,
op: &DsaModuleOp,
b: u32,
s: u32,
dsa_backend: &str,
skip_indexer: bool,
) -> Result<f64, AicError> {
let spec = &db.system_spec;
let dims = dsa_dims(&op.architecture);
let (kv, gemm) = (op.kv_cache_dtype, op.gemm_quant_mode);
let flops = dsa_generation_sol_flops(spec, gemm)?;
let sol = |b: f64, s: f64, num_heads: f64| {
dsa_generation_sol_ms(
spec,
dims,
kv,
gemm,
b as i64,
s as i64,
num_heads as i64,
flops,
)
};
let heads_f = op.num_heads as f64;
let sol_time = sol(b as f64, s as f64, heads_f);
let key = DsaKey {
architecture: op.architecture.clone(),
fmha_quant: op.fmha_quant_mode.name().to_string(),
kv_quant: kv.name().to_string(),
gemm_quant: gemm.name().to_string(),
};
let slice = match db.dsa.generation_raw_slice(&key, dsa_backend, skip_indexer) {
Ok(slice) => Some(slice),
Err(err) if err.is_missing_perf_data() => None,
Err(err) => return Err(err),
};
if let Some(head_grid) = slice.and_then(|slc| slc.get(&op.num_heads)) {
let skip_tag = if skip_indexer { ":skip" } else { "" };
let cache_key = format!(
"gen_dsa_exact_heads:{}:{}:{}:{}:{dsa_backend}:h{}{skip_tag}",
op.fmha_quant_mode.name(),
kv.name(),
gemm.name(),
op.architecture,
op.num_heads
);
let grid = db.util_grids.get_or_try_build(&cache_key, || {
Ok(Some(UtilGrid::new(util_empirical::build_samples(
generation_points_head(head_grid),
|c: &[f64]| sol(c[0], c[1], heads_f),
))))
})?;
let (latency, _) =
util_empirical::estimate(sol_time, &[b as f64, s as f64], grid.as_deref(), 1.0)?;
db.note_provenance(util_empirical::ProvenanceTier::Empirical);
Ok(latency)
} else if let Some(slc) = slice {
let skip_tag = if skip_indexer { ":skip" } else { "" };
let cache_key = format!(
"gen_dsa:{}:{}:{}:{}:{dsa_backend}{skip_tag}",
op.fmha_quant_mode.name(),
kv.name(),
gemm.name(),
op.architecture
);
let grid = db.util_grids.get_or_try_build(&cache_key, || {
Ok(Some(UtilGrid::new(util_empirical::build_samples(
generation_points_all(slc),
|c: &[f64]| sol(c[1], c[2], c[0]),
))))
})?;
let (latency, _) = util_empirical::estimate(
sol_time,
&[heads_f, b as f64, s as f64],
grid.as_deref(),
1.0,
)?;
db.note_provenance(util_empirical::ProvenanceTier::Empirical);
Ok(latency)
} else {
let (latency, _) =
util_empirical::estimate(sol_time, &[heads_f, b as f64, s as f64], None, 1.0)?;
Ok(latency)
}
}
fn points_2d(grid: &BTreeMap<u32, BTreeMap<u32, LeafValue>>) -> Vec<(Vec<f64>, f64)> {
let mut points = Vec::new();
for (&s, by_b) in grid {
for (&b, leaf) in by_b {
points.push((vec![s as f64, b as f64], leaf.latency));
}
}
points
}
fn context_points_head(
head_grid: &BTreeMap<u32, BTreeMap<u32, BTreeMap<u32, LeafValue>>>,
) -> Vec<(Vec<f64>, f64)> {
let mut points = Vec::new();
for (&prefix, by_s) in head_grid {
for (&s, by_b) in by_s {
for (&b, leaf) in by_b {
points.push((vec![prefix as f64, s as f64, b as f64], leaf.latency));
}
}
}
points
}
fn context_points_all(slice: &DsaHeadGrid) -> Vec<(Vec<f64>, f64)> {
let mut points = Vec::new();
for (&heads, head_grid) in slice {
for (&prefix, by_s) in head_grid {
for (&s, by_b) in by_s {
for (&b, leaf) in by_b {
points.push((
vec![heads as f64, prefix as f64, s as f64, b as f64],
leaf.latency,
));
}
}
}
}
points
}
fn context_points_all_p0(slice: &DsaHeadGrid) -> Vec<(Vec<f64>, f64)> {
let mut points = Vec::new();
for (&heads, head_grid) in slice {
if let Some(by_s) = head_grid.get(&0) {
for (&s, by_b) in by_s {
for (&b, leaf) in by_b {
points.push((vec![heads as f64, s as f64, b as f64], leaf.latency));
}
}
}
}
points
}
fn generation_points_head(
head_grid: &BTreeMap<u32, BTreeMap<u32, BTreeMap<u32, LeafValue>>>,
) -> Vec<(Vec<f64>, f64)> {
let mut points = Vec::new();
for by_seq in head_grid.values() {
for (&seq, by_b) in by_seq {
for (&b, leaf) in by_b {
points.push((vec![b as f64, seq as f64], leaf.latency));
}
}
}
points
}
fn generation_points_all(slice: &DsaHeadGrid) -> Vec<(Vec<f64>, f64)> {
let mut points = Vec::new();
for (&heads, head_grid) in slice {
for by_seq in head_grid.values() {
for (&seq, by_b) in by_seq {
for (&b, leaf) in by_b {
points.push((vec![heads as f64, b as f64, seq as f64], leaf.latency));
}
}
}
}
points
}
#[cfg(test)]
mod tests {
use super::*;
use crate::perf_database::dsa::SparseGrid;
use std::path::PathBuf;
fn glm_cp_op(cp_size: u32) -> DsaModuleOp {
DsaModuleOp {
name: "context_dsa".into(),
scale_factor: 1.0,
num_heads: 64,
kv_cache_dtype: KvCacheQuantMode::Bfloat16,
fmha_quant_mode: FmhaQuantMode::Bfloat16,
gemm_quant_mode: GemmQuantMode::Bfloat16,
architecture: "GlmMoeDsaForCausalLM".into(),
index_topk: 2048,
cp_size,
full_frac: 1.0,
attn_projection_quant_modes: None,
}
}
fn grid(rows: &[(u32, u32, u32, f64)]) -> SparseGrid {
let mut g = SparseGrid::new();
for &(bs, isl, step, lat) in rows {
g.entry(bs).or_default().insert((isl, step), lat);
}
g
}
#[test]
fn cp_composition_matches_python_synthetic() {
let (cp, isl, prefix) = (8u32, 16384u32, 0u32);
let per_card = isl.div_ceil(cp); let sparse = DsaSparseTables {
mqa: grid(&[(1, isl, 0, 1600.0), (1, per_card, 0, 25.0)]),
topk_last: grid(&[(1, isl, 0, 800.0), (1, per_card, 0, 190.0)]),
topk_flat: grid(&[(1, per_card, 0, 100.0)]),
dsa_attn: SparseGrid::new(),
};
let op = glm_cp_op(cp);
let mut base_calls: Vec<u32> = Vec::new();
let mut ag_volumes: Vec<u64> = Vec::new();
let res = op
.query_cp_with(
&sparse,
1,
isl,
prefix,
false,
&mut |per_card| {
base_calls.push(per_card);
Ok(4300.0) },
&mut |elems| {
ag_volumes.push(elems);
Ok(50.0) },
)
.expect("CP composition must succeed");
assert_eq!(res.latency_ms, 4575.0);
assert_eq!(res.source, Source::Estimated);
assert_eq!(base_calls, vec![per_card]); ag_volumes.sort_unstable();
let mut expected = vec![u64::from(isl) * 128, u64::from(isl) * (512 + 64)];
expected.sort_unstable();
assert_eq!(ag_volumes, expected);
}
#[test]
fn cp_propagates_lookup_fail_loud_beyond_grid() {
let sparse = DsaSparseTables {
mqa: grid(&[(1, 16384, 0, 1600.0), (1, 4096, 0, 25.0)]), topk_last: grid(&[(1, 16384, 0, 800.0), (1, 4096, 0, 190.0)]),
topk_flat: grid(&[(1, 4096, 0, 100.0)]),
dsa_attn: SparseGrid::new(),
};
let op = glm_cp_op(8);
let err = op
.query_cp_with(
&sparse,
1,
32768, 0,
false,
&mut |_| Ok(4300.0),
&mut |_| Ok(50.0),
)
.unwrap_err();
assert!(
err.to_string().contains("exceeds the collected"),
"unexpected message: {err}"
);
}
#[test]
fn cp_missing_sparse_tables_fail_loud() {
let systems_root = PathBuf::from(env!("CARGO_MANIFEST_DIR"))
.join("../..")
.join("src/aiconfigurator_core/systems");
let db = PerfDatabase::load(&systems_root, "b200_sxm", "vllm", "0.24.0")
.expect("b200_sxm/vllm/0.24.0 must load");
let op = glm_cp_op(8);
let err = op.query_context(&db, 1, 16384, 0).unwrap_err();
let msg = err.to_string();
assert!(
msg.contains("DSA CP modeling needs sparse tables")
&& msg.contains("['mqa', 'topk_last', 'topk_flat']")
&& msg.contains("GlmMoeDsaForCausalLM")
&& msg.contains("num_heads=64")
&& msg.contains("collect glm5_mqa_logits/glm5_topk first."),
"unexpected message: {msg}"
);
}
#[test]
fn cp_size_defaults_to_one_in_serde() {
let mut v = serde_json::to_value(glm_cp_op(3)).expect("serialize");
v.as_object_mut().expect("object").remove("cp_size");
let de: DsaModuleOp = serde_json::from_value(v).expect("deserialize");
assert_eq!(de.cp_size, 1);
}
use crate::common::enums::DatabaseMode;
use crate::operators::base::Source;
use crate::perf_database::PerfDatabase;
const DSV32: &str = "DeepseekV32ForCausalLM";
const GLM: &str = "GlmMoeDsaForCausalLM";
fn b200_db(backend: &str, version: &str, mode: DatabaseMode) -> PerfDatabase {
let systems_root = PathBuf::from(env!("CARGO_MANIFEST_DIR"))
.join("../..")
.join("src/aiconfigurator_core/systems");
let mut db = PerfDatabase::load(&systems_root, "b200_sxm", backend, version)
.expect("b200_sxm db must load");
db.database_mode = mode;
db
}
fn b200_db_on(system: &str, backend: &str, version: &str, mode: DatabaseMode) -> PerfDatabase {
let root = PathBuf::from(env!("CARGO_MANIFEST_DIR"))
.join("../..")
.join("src/aiconfigurator_core/systems");
let mut db = PerfDatabase::load(&root, system, backend, version).expect("db loads");
db.database_mode = mode;
db
}
fn sglang_db(system: &str, version: &str, mode: DatabaseMode) -> PerfDatabase {
let systems_root = PathBuf::from(env!("CARGO_MANIFEST_DIR"))
.join("../..")
.join("src/aiconfigurator_core/systems");
let mut db = PerfDatabase::load(&systems_root, system, "sglang", version)
.unwrap_or_else(|e| panic!("{system} db must load: {e}"));
db.database_mode = mode;
db
}
const GLM52_FULL_FRAC: f64 = 21.0 / 78.0;
fn glm52_op(kv: KvCacheQuantMode, full_frac: f64) -> DsaModuleOp {
let mut op = dsa_op(GLM, 64, kv);
op.full_frac = full_frac;
op
}
fn dsa_op(architecture: &str, num_heads: u32, kv: KvCacheQuantMode) -> DsaModuleOp {
DsaModuleOp::new(
"dsa_module",
num_heads,
kv,
FmhaQuantMode::Bfloat16,
GemmQuantMode::Bfloat16,
architecture,
2048,
)
}
fn approx_rel_1e9(got: f64, want: f64) {
assert!(
((got - want) / want).abs() < 1e-9,
"rust {got} vs python {want}"
);
}
#[test]
fn missing_skip_indexer_variant_degrades_amortization_to_all_full() {
let db = b200_db_on("h200_sxm", "vllm", "0.24.0", DatabaseMode::Silicon);
assert!(
!db.dsa.has_context_skip_rows().expect("probe"),
"h200/vllm/0.24.0 ships full rows only"
);
assert!(!db.dsa.has_generation_skip_rows().expect("probe"));
let glm52 = glm52_op(KvCacheQuantMode::Bfloat16, GLM52_FULL_FRAC);
let all_full = glm52_op(KvCacheQuantMode::Bfloat16, 1.0);
let degraded = glm52
.query_context(&db, 2, 4096, 512)
.expect("context query must not fail on a full-only table");
approx_rel_1e9(
degraded.latency_ms,
all_full
.query_context(&db, 2, 4096, 512)
.expect("all-full context")
.latency_ms,
);
let degraded_gen = glm52
.query_generation(&db, 2, 4096)
.expect("generation query must not fail on a full-only table");
approx_rel_1e9(
degraded_gen.latency_ms,
all_full
.query_generation(&db, 2, 4096)
.expect("all-full generation")
.latency_ms,
);
}
#[test]
fn present_skip_indexer_variant_keeps_mixed_amortization() {
let db = sglang_db("gb200", "0.5.14", DatabaseMode::Silicon);
assert!(
db.dsa.has_context_skip_rows().expect("probe"),
"gb200 ships both variants"
);
assert!(db.dsa.has_generation_skip_rows().expect("probe"));
let glm52 = glm52_op(KvCacheQuantMode::Bfloat16, GLM52_FULL_FRAC);
let all_full = glm52_op(KvCacheQuantMode::Bfloat16, 1.0);
let mixed = glm52
.query_context(&db, 2, 4096, 512)
.expect("context query")
.latency_ms;
let full_only = all_full
.query_context(&db, 2, 4096, 512)
.expect("all-full context")
.latency_ms;
assert!(
mixed < full_only,
"skip layers must lower the amortized cost: {mixed} vs {full_only}"
);
let skip_only = glm52_op(KvCacheQuantMode::Bfloat16, 0.0)
.query_context(&db, 2, 4096, 512)
.expect("pure-skip context")
.latency_ms;
approx_rel_1e9(
mixed,
GLM52_FULL_FRAC * full_only + (1.0 - GLM52_FULL_FRAC) * skip_only,
);
approx_rel_1e9(mixed, 6.599784615384616);
let mixed_gen = glm52
.query_generation(&db, 2, 4096)
.expect("generation query")
.latency_ms;
let full_only_gen = all_full
.query_generation(&db, 2, 4096)
.expect("all-full generation")
.latency_ms;
assert!(mixed_gen < full_only_gen);
approx_rel_1e9(mixed_gen, 0.10149143817608174);
}
#[test]
fn sol_mode_keeps_configured_amortization_on_full_only_table() {
let db = b200_db_on("h200_sxm", "vllm", "0.24.0", DatabaseMode::Sol);
assert!(
!db.dsa.has_context_skip_rows().expect("probe"),
"anchor must be full-only"
);
let glm52 = glm52_op(KvCacheQuantMode::Bfloat16, GLM52_FULL_FRAC);
let all_full = glm52_op(KvCacheQuantMode::Bfloat16, 1.0);
let mixed_sol = glm52
.query_context(&db, 2, 4096, 512)
.expect("SOL context query must not fail on a full-only table")
.latency_ms;
let full_sol = all_full
.query_context(&db, 2, 4096, 512)
.expect("all-full SOL context")
.latency_ms;
assert!(
mixed_sol < full_sol,
"SOL must keep the configured blend (degradation would collapse it to all-full): {mixed_sol} vs {full_sol}"
);
let skip_sol = glm52_op(KvCacheQuantMode::Bfloat16, 0.0)
.query_context(&db, 2, 4096, 512)
.expect("pure-skip SOL context")
.latency_ms;
approx_rel_1e9(
mixed_sol,
GLM52_FULL_FRAC * full_sol + (1.0 - GLM52_FULL_FRAC) * skip_sol,
);
let mixed_gen_sol = glm52
.query_generation(&db, 2, 4096)
.expect("SOL generation query must not fail on a full-only table")
.latency_ms;
let full_gen_sol = all_full
.query_generation(&db, 2, 4096)
.expect("all-full SOL generation")
.latency_ms;
approx_rel_1e9(mixed_gen_sol, full_gen_sol);
}
#[test]
fn context_empirical_sglang_glm_wiring() {
let db = b200_db("sglang", "0.5.14", DatabaseMode::Empirical);
for heads in [128u32, 64] {
let op = dsa_op(GLM, heads, KvCacheQuantMode::Bfloat16);
let result = op
.query_context(&db, 2, 4096, 512)
.expect("empirical query");
assert!(result.latency_ms.is_finite() && result.latency_ms > 0.0);
assert_eq!(result.source, Source::Empirical, "h={heads}");
}
}
#[test]
fn context_hybrid_prefers_silicon() {
let sil = b200_db("vllm", "0.24.0", DatabaseMode::Silicon);
let db = b200_db("vllm", "0.24.0", DatabaseMode::Hybrid);
let op = dsa_op(DSV32, 128, KvCacheQuantMode::Bfloat16);
for (b, s) in [(4u32, 2048u32), (4, 3000)] {
let want = op.query_context(&sil, b, s, 0).expect("silicon answer");
let got = op.query_context(&db, b, s, 0).expect("hybrid answer");
assert_eq!(got.source, Source::Silicon, "(b={b}, s={s})");
assert!(
(got.latency_ms - want.latency_ms).abs() < 1e-12,
"hybrid must replay the silicon answer at (b={b}, s={s})"
);
}
}
#[test]
fn context_missing_slice_raises_empirical_not_implemented() {
for mode in [DatabaseMode::Hybrid, DatabaseMode::Empirical] {
let db = b200_db("vllm", "0.24.0", mode);
let op = dsa_op(DSV32, 128, KvCacheQuantMode::Int8);
let result = op.query_context(&db, 4, 3000, 0);
assert!(
matches!(result, Err(AicError::EmpiricalNotImplemented(_))),
"{mode:?}: got {result:?}"
);
}
}
#[test]
fn generation_empirical_sglang_glm_wiring() {
let db = b200_db("sglang", "0.5.14", DatabaseMode::Empirical);
let op = dsa_op(GLM, 64, KvCacheQuantMode::Bfloat16);
let result = op
.query_generation(&db, 48, 10000)
.expect("empirical query");
assert!(result.latency_ms.is_finite() && result.latency_ms > 0.0);
assert_eq!(result.source, Source::Empirical);
}
#[test]
fn generation_hybrid_missing_slice_raises_empirical_not_implemented() {
let db = b200_db("vllm", "0.24.0", DatabaseMode::Hybrid);
let op = dsa_op(DSV32, 128, KvCacheQuantMode::Int8);
let result = op.query_generation(&db, 24, 3000);
assert!(
matches!(result, Err(AicError::EmpiricalNotImplemented(_))),
"got {result:?}"
);
}
#[test]
fn dsa_sol_mode_returns_roofline_with_sol_source() {
let systems_root = PathBuf::from(env!("CARGO_MANIFEST_DIR"))
.join("../..")
.join("src/aiconfigurator_core/systems");
let mut db =
PerfDatabase::load(&systems_root, "b200_sxm", "vllm", "0.24.0").expect("db must load");
db.database_mode = DatabaseMode::Sol;
let spec = db.system_spec.clone();
let op = glm_cp_op(1);
let dims = dsa_dims(&op.architecture);
let result = op.query_context(&db, 2, 4096, 512).expect("ctx sol");
let flops = dsa_context_sol_flops(&spec, op.gemm_quant_mode, op.fmha_quant_mode).unwrap();
let expected = dsa_context_sol_ms(
&spec,
dims,
op.index_topk as i64,
op.kv_cache_dtype,
op.fmha_quant_mode,
op.gemm_quant_mode,
2,
4096,
512,
op.num_heads as i64,
false,
flops,
);
assert_eq!(result.latency_ms, expected);
assert_eq!(result.source, Source::Sol);
assert_eq!(result.energy_wms, 0.0);
let result = op.query_generation(&db, 8, 4096).expect("gen sol");
let flops = dsa_generation_sol_flops(&spec, op.gemm_quant_mode).unwrap();
let expected = dsa_generation_sol_ms(
&spec,
dims,
op.kv_cache_dtype,
op.gemm_quant_mode,
8,
4096,
op.num_heads as i64,
flops,
);
assert_eq!(result.latency_ms, expected);
assert_eq!(result.source, Source::Sol);
}
}