use std::collections::{BTreeMap, BTreeSet};
use std::path::PathBuf;
use std::sync::OnceLock;
use serde::{Deserialize, Serialize};
use super::attention::generation_attn_mode;
use super::dsa::{SparseGrid, bs_slice, lookup_2d};
use super::gemm::quant_tc_flops;
use super::perf_interp::{self, LeafValue, Node, OpInterpConfig};
use super::{SourceResolver, kernel_source_ok};
use crate::common::enums::{FmhaQuantMode, GemmQuantMode, KvCacheQuantMode};
use crate::common::error::AicError;
use crate::common::system_spec::SystemSpec;
use crate::config::{PerfDbSources, PerfSource};
use crate::perf_database::parquet_loader::PerfReader;
#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
pub enum AttnKind {
Csa,
Hca,
}
impl AttnKind {
pub(crate) fn compress_ratio(self) -> i64 {
match self {
AttnKind::Csa => 4,
AttnKind::Hca => 128,
}
}
}
type ByBatch = BTreeMap<u32, LeafValue>;
type ByIsl = BTreeMap<u32, ByBatch>;
type ByStep = BTreeMap<u32, ByIsl>;
type ByLocal = BTreeMap<u32, ByStep>;
type ByNative = BTreeMap<u32, ByLocal>;
pub struct Dsv4Table {
csa_context_sources: Vec<PerfSource>,
hca_context_sources: Vec<PerfSource>,
csa_generation_sources: Vec<PerfSource>,
hca_generation_sources: Vec<PerfSource>,
topk_calib_sources: Vec<PerfSource>,
paged_mqa_sources: Vec<PerfSource>,
csa_context: OnceLock<Result<ModuleNodes, AicError>>,
hca_context: OnceLock<Result<ModuleNodes, AicError>>,
csa_generation: OnceLock<Result<ModuleNodes, AicError>>,
hca_generation: OnceLock<Result<ModuleNodes, AicError>>,
topk_calib: OnceLock<Result<Option<TopkCalib>, AicError>>,
paged_mqa: OnceLock<Result<Option<SparseKernelNodes>, AicError>>,
}
struct ModuleGrids {
by_keys: BTreeMap<ModuleKey, ByNative>,
}
struct ModuleNodes {
by_keys: BTreeMap<ModuleKey, BTreeMap<u32, BTreeMap<u32, Node>>>,
}
#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord)]
struct ModuleKey {
fmha_quant: String,
kv_quant: String,
gemm_quant: String,
}
pub(crate) struct Dsv4Dims {
pub(crate) hidden_size: i64,
pub(crate) q_lora_rank: i64,
pub(crate) o_lora_rank: i64,
pub(crate) head_dim: i64,
pub(crate) rope_head_dim: i64,
pub(crate) index_n_heads: i64,
pub(crate) index_head_dim: i64,
pub(crate) index_topk: i64,
pub(crate) window_size: i64,
pub(crate) o_groups: i64,
pub(crate) native_heads: i64,
}
const DSV4_PRO_DIMS: Dsv4Dims = Dsv4Dims {
hidden_size: 7168,
q_lora_rank: 1536,
o_lora_rank: 1024,
head_dim: 512,
rope_head_dim: 64,
index_n_heads: 64,
index_head_dim: 128,
index_topk: 1024,
window_size: 128,
o_groups: 16,
native_heads: 128,
};
pub(crate) fn dsv4_dims(_architecture: &str) -> &'static Dsv4Dims {
&DSV4_PRO_DIMS
}
#[derive(Clone, Copy, Debug)]
pub struct Dsv4SolDims {
pub(crate) hidden_size: i64,
pub(crate) q_lora_rank: i64,
pub(crate) o_lora_rank: i64,
pub(crate) head_dim: i64,
pub(crate) rope_head_dim: i64,
pub(crate) index_n_heads: i64,
pub(crate) index_head_dim: i64,
pub(crate) index_topk: i64,
pub(crate) window_size: i64,
pub(crate) local_o_groups: i64,
}
impl Dsv4SolDims {
pub(crate) fn from_pinned(dims: &Dsv4Dims, local_heads: i64) -> Self {
let tp = (dims.native_heads / local_heads.max(1)).max(1);
Dsv4SolDims {
hidden_size: dims.hidden_size,
q_lora_rank: dims.q_lora_rank,
o_lora_rank: dims.o_lora_rank,
head_dim: dims.head_dim,
rope_head_dim: dims.rope_head_dim,
index_n_heads: dims.index_n_heads,
index_head_dim: dims.index_head_dim,
index_topk: dims.index_topk,
window_size: dims.window_size,
local_o_groups: (dims.o_groups / tp).max(1),
}
}
}
impl Dsv4Table {
pub fn new(data_root: PathBuf) -> Self {
Self::with_sources(data_root, &SourceResolver::fixed(PerfDbSources::default()))
.expect("fixed-map resolution is infallible")
}
pub fn with_sources(data_root: PathBuf, resolver: &SourceResolver) -> Result<Self, AicError> {
let csa_context_sources =
resolver.sources_for("dsv4_csa_context_module_perf.parquet", &data_root)?;
let hca_context_sources =
resolver.sources_for("dsv4_hca_context_module_perf.parquet", &data_root)?;
let csa_generation_sources =
resolver.sources_for("dsv4_csa_generation_module_perf.parquet", &data_root)?;
let hca_generation_sources =
resolver.sources_for("dsv4_hca_generation_module_perf.parquet", &data_root)?;
let topk_calib_sources =
resolver.sources_for("dsv4_csa_topk_calib_perf.parquet", &data_root)?;
let paged_mqa_sources =
resolver.sources_for("dsv4_paged_mqa_logits_module_perf.parquet", &data_root)?;
Ok(Self {
csa_context_sources,
hca_context_sources,
csa_generation_sources,
hca_generation_sources,
topk_calib_sources,
paged_mqa_sources,
csa_context: OnceLock::new(),
hca_context: OnceLock::new(),
csa_generation: OnceLock::new(),
hca_generation: OnceLock::new(),
topk_calib: OnceLock::new(),
paged_mqa: OnceLock::new(),
})
}
#[allow(clippy::too_many_arguments)]
pub fn query_context(
&self,
spec: &SystemSpec,
attn_kind: AttnKind,
b: u32,
isl: u32,
local_heads: u32,
native_heads: u32,
kv_quant: KvCacheQuantMode,
fmha_quant: FmhaQuantMode,
gemm_quant: GemmQuantMode,
architecture: &str,
prefix: u32,
sol_dims: Option<Dsv4SolDims>,
) -> Result<LeafValue, AicError> {
let flops = dsv4_sol_flops(spec, gemm_quant, fmha_quant)?;
let grids = match attn_kind {
AttnKind::Csa => self.load_csa_context()?,
AttnKind::Hca => self.load_hca_context()?,
};
let node = select_resolved(
grids,
Some(fmha_quant),
kv_quant,
gemm_quant,
native_heads,
local_heads,
)?;
let dims = sol_dims.unwrap_or_else(|| {
Dsv4SolDims::from_pinned(dsv4_dims(architecture), local_heads as i64)
});
let cr = attn_kind.compress_ratio();
let heads = local_heads as i64;
let sol = move |c: &[f64]| {
dsv4_attention_sol_ms(
spec,
&dims,
cr,
true,
kv_quant,
fmha_quant,
gemm_quant,
c[2] as i64, c[1] as i64, c[0] as i64, heads,
flops,
)
};
let cfg = OpInterpConfig::grid(&["prefix", "seq_len", "batch"], &sol);
let value = perf_interp::query_value(&cfg, node, &[prefix as f64, isl as f64, b as f64])?;
if attn_kind == AttnKind::Csa {
return self.topk_corrected(value, TopkPhase::Context, native_heads, prefix, isl, b);
}
Ok(value)
}
#[allow(clippy::too_many_arguments)]
pub fn query_generation(
&self,
spec: &SystemSpec,
attn_kind: AttnKind,
b: u32,
sequence_tokens: u32,
local_heads: u32,
native_heads: u32,
kv_quant: KvCacheQuantMode,
gemm_quant: GemmQuantMode,
architecture: &str,
sol_dims: Option<Dsv4SolDims>,
) -> Result<LeafValue, AicError> {
let fmha_quant = generation_attn_mode(spec, kv_quant);
let flops = dsv4_sol_flops(spec, gemm_quant, fmha_quant)?;
let grids = match attn_kind {
AttnKind::Csa => self.load_csa_generation()?,
AttnKind::Hca => self.load_hca_generation()?,
};
let node = select_resolved(grids, None, kv_quant, gemm_quant, native_heads, local_heads)?;
let dims = sol_dims.unwrap_or_else(|| {
Dsv4SolDims::from_pinned(dsv4_dims(architecture), local_heads as i64)
});
let cr = attn_kind.compress_ratio();
let heads = local_heads as i64;
let sol = move |c: &[f64]| {
dsv4_attention_sol_ms(
spec,
&dims,
cr,
false,
kv_quant,
fmha_quant,
gemm_quant,
c[0] as i64, c[1] as i64, 0,
heads,
flops,
)
};
let cfg = OpInterpConfig::grid(&["batch", "seq_len"], &sol);
let value = perf_interp::query_value(&cfg, node, &[b as f64, sequence_tokens as f64])?;
if attn_kind == AttnKind::Csa {
return self.topk_corrected(
value,
TopkPhase::Generation,
native_heads,
sequence_tokens.saturating_sub(1),
1,
b,
);
}
Ok(value)
}
fn load_csa_context(&self) -> Result<&ModuleNodes, AicError> {
let cell = self.csa_context.get_or_init(|| {
load_module_parquet(&self.csa_context_sources, true).map(context_nodes)
});
cell.as_ref().map_err(clone_err)
}
fn load_hca_context(&self) -> Result<&ModuleNodes, AicError> {
let cell = self.hca_context.get_or_init(|| {
load_module_parquet(&self.hca_context_sources, true).map(context_nodes)
});
cell.as_ref().map_err(clone_err)
}
fn load_csa_generation(&self) -> Result<&ModuleNodes, AicError> {
let cell = self.csa_generation.get_or_init(|| {
load_module_parquet(&self.csa_generation_sources, false).map(generation_nodes)
});
cell.as_ref().map_err(clone_err)
}
fn load_hca_generation(&self) -> Result<&ModuleNodes, AicError> {
let cell = self.hca_generation.get_or_init(|| {
load_module_parquet(&self.hca_generation_sources, false).map(generation_nodes)
});
cell.as_ref().map_err(clone_err)
}
fn topk_corrected(
&self,
value: LeafValue,
phase: TopkPhase,
native_heads: u32,
prefix: u32,
isl: u32,
bs: u32,
) -> Result<LeafValue, AicError> {
if !topk_correction_enabled() {
return Ok(value);
}
let exact = self.load_topk_calib()?.and_then(|calib| {
match phase {
TopkPhase::Context => &calib.exact_v1,
TopkPhase::Generation => &calib.exact_v2,
}
.get(&native_heads)
});
let corrected = apply_topk_delta(value.latency, exact, prefix, isl, bs);
let energy = if value.latency > 0.0 && value.energy != 0.0 {
value.energy * (corrected / value.latency)
} else {
value.energy
};
Ok(LeafValue {
latency: corrected,
power: value.power,
energy,
})
}
fn load_topk_calib(&self) -> Result<Option<&TopkCalib>, AicError> {
let cell = self
.topk_calib
.get_or_init(|| load_topk_calib_parquet(&self.topk_calib_sources));
match cell {
Ok(calib) => Ok(calib.as_ref()),
Err(err) => Err(clone_err(err)),
}
}
fn load_paged_mqa(&self) -> Result<Option<&SparseKernelNodes>, AicError> {
let cell = self
.paged_mqa
.get_or_init(|| load_sparse_kernel_parquet(&self.paged_mqa_sources));
match cell {
Ok(nodes) => Ok(nodes.as_ref()),
Err(err) => Err(clone_err(err)),
}
}
pub fn query_paged_mqa_logits(
&self,
b: u32,
isl: u32,
past_kv: u32,
tp_size: u32,
native_heads: u32,
) -> Result<Option<f64>, AicError> {
let Some(nodes) = self.load_paged_mqa()? else {
return Ok(None);
};
let Some(per_tp) = nodes.by_heads.get(&native_heads) else {
return Ok(None);
};
let Some(node) = per_tp.get(&tp_size).or_else(|| per_tp.get(&1)) else {
return Ok(None);
};
let sol = |c: &[f64]| c[2] * (c[0] * c[1] + c[1] * c[1] / 2.0);
let cfg = OpInterpConfig::grid(&["past_kv", "seq_len", "batch"], &sol);
match perf_interp::query(&cfg, node, &[past_kv as f64, isl as f64, b as f64]) {
Ok(latency) if latency.is_finite() => Ok(Some(latency)),
Ok(_) | Err(_) => Ok(None),
}
}
pub fn context_points(
&self,
attn_kind: AttnKind,
local_heads: u32,
native_heads: u32,
kv_quant: KvCacheQuantMode,
fmha_quant: FmhaQuantMode,
gemm_quant: GemmQuantMode,
) -> Result<Vec<(Vec<f64>, f64)>, AicError> {
let grids = match attn_kind {
AttnKind::Csa => self.load_csa_context()?,
AttnKind::Hca => self.load_hca_context()?,
};
let node = select_resolved(
grids,
Some(fmha_quant),
kv_quant,
gemm_quant,
native_heads,
local_heads,
)?;
let points = perf_interp::node_points(node);
if points.is_empty() {
return Err(AicError::PerfDatabase(format!(
"DSV4 context module data empty for local_heads={local_heads}, \
attn_kind={attn_kind:?}"
)));
}
Ok(points)
}
pub fn generation_points(
&self,
attn_kind: AttnKind,
local_heads: u32,
native_heads: u32,
kv_quant: KvCacheQuantMode,
gemm_quant: GemmQuantMode,
) -> Result<Vec<(Vec<f64>, f64)>, AicError> {
let grids = match attn_kind {
AttnKind::Csa => self.load_csa_generation()?,
AttnKind::Hca => self.load_hca_generation()?,
};
let node = select_resolved(grids, None, kv_quant, gemm_quant, native_heads, local_heads)?;
let points = perf_interp::node_points(node);
if points.is_empty() {
return Err(AicError::PerfDatabase(format!(
"DSV4 generation module data empty for local_heads={local_heads}, \
attn_kind={attn_kind:?}"
)));
}
Ok(points)
}
pub fn csa_topk_top_last(
&self,
isl: u32,
step: u32,
native_heads: u32,
b: u32,
) -> Result<Option<f64>, AicError> {
let Some(calib) = self.load_topk_calib()? else {
return Ok(None);
};
let Some(grid) = calib.top_last.get(&native_heads) else {
return Ok(None);
};
let Some(bs_grid) = bs_slice(grid, b) else {
return Ok(None);
};
lookup_2d(bs_grid, isl, step)
}
}
fn context_nodes(grids: ModuleGrids) -> ModuleNodes {
let mut by_keys: BTreeMap<ModuleKey, BTreeMap<u32, BTreeMap<u32, Node>>> = BTreeMap::new();
for (key, by_native) in grids.by_keys {
let per_native = by_keys.entry(key).or_default();
for (native, by_local) in by_native {
let per_head = per_native.entry(native).or_default();
for (head, by_step) in by_local {
let node = per_head.entry(head).or_insert_with(Node::branch);
for (step, by_isl) in by_step {
for (isl, by_batch) in by_isl {
for (bb, leaf) in by_batch {
node.insert_value(&[step, isl, bb], leaf);
}
}
}
}
}
}
ModuleNodes { by_keys }
}
fn generation_nodes(grids: ModuleGrids) -> ModuleNodes {
let mut by_keys: BTreeMap<ModuleKey, BTreeMap<u32, BTreeMap<u32, Node>>> = BTreeMap::new();
for (key, by_native) in grids.by_keys {
let per_native = by_keys.entry(key).or_default();
for (native, by_local) in by_native {
let per_head = per_native.entry(native).or_default();
for (head, by_step) in by_local {
let node = per_head.entry(head).or_insert_with(Node::branch);
for (step, by_isl) in by_step {
for (isl, by_batch) in by_isl {
let s_total = isl + step;
for (bb, leaf) in by_batch {
node.insert_value(&[bb, s_total], leaf);
}
}
}
}
}
}
ModuleNodes { by_keys }
}
fn topk_correction_enabled() -> bool {
static ENABLED: OnceLock<bool> = OnceLock::new();
*ENABLED.get_or_init(|| {
parse_topk_correction_env(std::env::var("AIC_DSV4_TOPK_CORRECTION").ok().as_deref())
})
}
fn parse_topk_correction_env(value: Option<&str>) -> bool {
value != Some("0")
}
#[derive(Clone, Copy)]
enum TopkPhase {
Context,
Generation,
}
struct TopkCalib {
exact_v1: BTreeMap<u32, BTreeMap<(u32, u32, u32), f64>>,
exact_v2: BTreeMap<u32, BTreeMap<(u32, u32, u32), f64>>,
top_last: BTreeMap<u32, SparseGrid>,
}
fn apply_topk_delta(
latency: f64,
exact: Option<&BTreeMap<(u32, u32, u32), f64>>,
prefix: u32,
isl: u32,
bs: u32,
) -> f64 {
match exact {
Some(exact) => (latency - topk_delta_ms(exact, prefix, isl, bs)).max(0.0),
None => latency,
}
}
fn load_topk_calib_parquet(sources: &[PerfSource]) -> Result<Option<TopkCalib>, AicError> {
let mut by_mode: BTreeMap<u32, BTreeMap<(u32, u32, u32), BTreeMap<String, f64>>> =
BTreeMap::new();
let mut top_last: BTreeMap<u32, SparseGrid> = BTreeMap::new();
let mut any_source = false;
for source in sources {
let path = source.path();
if !path.exists() {
continue;
}
any_source = true;
let reader = PerfReader::open(path)?;
let step_col = reader.col("step")?;
let isl_col = reader.col("isl")?;
let batch_size_col = reader.col("batch_size")?;
let score_mode_col = reader.col("score_mode")?;
let latency_col = reader.col("latency")?;
let num_heads_col = reader.col_optional("num_heads");
let ks_col = reader.col_optional("kernel_source");
for row in reader.rows()? {
let row = row?;
if !kernel_source_ok(source.kernel_sources(), ks_col, &row)? {
continue;
}
let (step, isl, bs) = (
row.u32(step_col)?,
row.u32(isl_col)?,
row.u32(batch_size_col)?,
);
let mode = row.str_owned(score_mode_col)?;
let latency = row.f64(latency_col)?;
let Some(native) = row.u32_optional(num_heads_col)? else {
continue;
};
if mode == "v1_top_last" {
top_last
.entry(native)
.or_default()
.entry(bs)
.or_default()
.entry((isl, step))
.or_insert(latency);
}
by_mode
.entry(native)
.or_default()
.entry((step, isl, bs))
.or_default()
.entry(mode)
.or_insert(latency);
}
}
if !any_source {
return Ok(None);
}
let mut exact_v1: BTreeMap<u32, BTreeMap<(u32, u32, u32), f64>> = BTreeMap::new();
let mut exact_v2: BTreeMap<u32, BTreeMap<(u32, u32, u32), f64>> = BTreeMap::new();
for (native, shapes) in by_mode {
for (key, modes) in shapes {
if let (Some(flat), Some(tl)) = (modes.get("v1_flat"), modes.get("v1_top_last")) {
exact_v1
.entry(native)
.or_default()
.insert(key, (flat - tl).max(0.0));
}
if let (Some(flat), Some(tl)) = (modes.get("v2_flat"), modes.get("v2_top_last")) {
exact_v2
.entry(native)
.or_default()
.insert(key, (flat - tl).max(0.0));
}
}
}
if exact_v1.is_empty() && exact_v2.is_empty() && top_last.is_empty() {
return Ok(None);
}
Ok(Some(TopkCalib {
exact_v1,
exact_v2,
top_last,
}))
}
struct SparseKernelNodes {
by_heads: BTreeMap<u32, BTreeMap<u32, Node>>,
}
fn load_sparse_kernel_parquet(
sources: &[PerfSource],
) -> Result<Option<SparseKernelNodes>, AicError> {
let mut by_heads: BTreeMap<u32, BTreeMap<u32, Node>> = BTreeMap::new();
let mut any_source = false;
for source in sources {
let path = source.path();
if !path.exists() {
continue;
}
any_source = true;
let reader = PerfReader::open(path)?;
let num_heads_col = reader.col("num_heads")?;
let tp_size_col = reader.col("tp_size")?;
let step_col = reader.col("step")?;
let isl_col = reader.col("isl")?;
let batch_size_col = reader.col("batch_size")?;
let latency_col = reader.col("latency")?;
let ks_col = reader.col_optional("kernel_source");
for row in reader.rows()? {
let row = row?;
if !kernel_source_ok(source.kernel_sources(), ks_col, &row)? {
continue;
}
by_heads
.entry(row.u32(num_heads_col)?)
.or_default()
.entry(row.u32(tp_size_col)?)
.or_insert_with(Node::branch)
.insert_first_wins(
&[
row.u32(step_col)?,
row.u32(isl_col)?,
row.u32(batch_size_col)?,
],
row.f64(latency_col)?,
);
}
}
if !any_source {
return Ok(None);
}
Ok(Some(SparseKernelNodes { by_heads }))
}
fn topk_interp_1d(points: &[(u32, f64)], x: u32) -> Option<f64> {
if points.is_empty() {
return None;
}
let mut merged: BTreeMap<u32, (f64, u32)> = BTreeMap::new();
for &(coord, value) in points {
let entry = merged.entry(coord).or_insert((0.0, 0));
entry.0 += value;
entry.1 += 1;
}
let vals: BTreeMap<u32, f64> = merged
.into_iter()
.map(|(k, (sum, n))| (k, sum / n as f64))
.collect();
if let Some(&v) = vals.get(&x) {
return Some(v);
}
let (&first_x, &first_v) = vals.iter().next().unwrap();
let (&last_x, &last_v) = vals.iter().next_back().unwrap();
if vals.len() == 1 || x <= first_x {
return Some(first_v);
}
if x >= last_x {
return Some(last_v);
}
let (&left_x, &left_v) = vals.range(..x).next_back().unwrap();
let (&right_x, &right_v) = vals.range(x + 1..).next().unwrap();
let t = (x as f64 - left_x as f64) / (right_x as f64 - left_x as f64);
Some(left_v * (1.0 - t) + right_v * t)
}
fn topk_delta_ms(exact: &BTreeMap<(u32, u32, u32), f64>, prefix: u32, isl: u32, bs: u32) -> f64 {
if let Some(&direct) = exact.get(&(prefix, isl, bs)) {
return direct.max(0.0);
}
let prefix_interp = |query_prefix: u32, anchor_isl: u32, anchor_bs: u32| -> Option<f64> {
let points: Vec<(u32, f64)> = exact
.iter()
.filter(|&(&(_, i, b), _)| i == anchor_isl && b == anchor_bs)
.map(|(&(p, _, _), &d)| (p, d))
.collect();
topk_interp_1d(&points, query_prefix)
};
let isl_interp = |query_prefix: u32, query_isl: u32, anchor_bs: u32| -> Option<f64> {
let isl_values: BTreeSet<u32> = exact
.keys()
.filter(|(_, _, b)| *b == anchor_bs)
.map(|(_, i, _)| *i)
.collect();
let points: Vec<(u32, f64)> = isl_values
.into_iter()
.filter_map(|i| prefix_interp(query_prefix, i, anchor_bs).map(|v| (i, v)))
.collect();
topk_interp_1d(&points, query_isl)
};
let bs_values: BTreeSet<u32> = exact.keys().map(|(_, _, b)| *b).collect();
let points: Vec<(u32, f64)> = bs_values
.into_iter()
.filter_map(|b| isl_interp(prefix, isl, b).map(|v| (b, v)))
.collect();
match topk_interp_1d(&points, bs) {
Some(interpolated) => interpolated.max(0.0),
None => 0.0,
}
}
fn select_resolved<'a>(
grids: &'a ModuleNodes,
fmha: Option<FmhaQuantMode>,
kv: KvCacheQuantMode,
gemm: GemmQuantMode,
native_heads: u32,
local_heads: u32,
) -> Result<&'a Node, AicError> {
let key = ModuleKey {
fmha_quant: fmha.map(|f| f.name().to_string()).unwrap_or_default(),
kv_quant: kv.name().to_string(),
gemm_quant: gemm.name().to_string(),
};
let by_native = grids
.by_keys
.get(&key)
.ok_or_else(|| AicError::PerfDatabase(format!("DSV4 module data missing for {key:?}")))?;
let native = resolve_head_key(by_native, native_heads).ok_or_else(|| {
AicError::PerfDatabase(format!(
"DSV4 module data missing for native_heads={native_heads}, {key:?} (loaded native keys: {:?})",
by_native.keys().collect::<Vec<_>>()
))
})?;
let by_local = &by_native[&native];
let head = resolve_head_key(by_local, local_heads).ok_or_else(|| {
AicError::PerfDatabase(format!(
"DSV4 module data missing for local_heads={local_heads} under native_heads={native}, {key:?} \
(loaded local keys: {:?})",
by_local.keys().collect::<Vec<_>>()
))
})?;
Ok(&by_local[&head])
}
fn resolve_head_key<T>(by_native: &BTreeMap<u32, T>, local_heads: u32) -> Option<u32> {
if by_native.is_empty() {
return None;
}
if by_native.contains_key(&local_heads) {
return Some(local_heads);
}
if by_native.len() == 1 {
return by_native.keys().next().copied();
}
by_native
.range(..=local_heads)
.next_back()
.map(|(&k, _)| k)
.or_else(|| by_native.keys().next().copied())
}
#[derive(Clone, Copy)]
pub(crate) struct Dsv4SolFlops {
pub gemm: f64,
pub bf16: f64,
pub fp8: f64,
pub attn: f64,
}
pub(crate) fn dsv4_sol_flops(
spec: &SystemSpec,
gemm_quant: GemmQuantMode,
fmha_quant: FmhaQuantMode,
) -> Result<Dsv4SolFlops, AicError> {
Ok(Dsv4SolFlops {
gemm: quant_tc_flops(spec, gemm_quant.mapping())?,
bf16: quant_tc_flops(spec, GemmQuantMode::Bfloat16.mapping())?,
fp8: quant_tc_flops(spec, GemmQuantMode::Fp8.mapping())?,
attn: quant_tc_flops(spec, fmha_quant.mapping())?,
})
}
fn causal_limited_pairs(batch: i128, query_len: i128, prefix: i128, limit: i128) -> i128 {
if limit <= 0 || query_len <= 0 {
return 0;
}
let full_s = prefix + query_len;
if prefix >= limit {
return batch * query_len * limit;
}
if full_s <= limit {
return batch * (full_s * (full_s + 1) - prefix * (prefix + 1)) / 2;
}
let ramp = batch * (limit * (limit + 1) - prefix * (prefix + 1)) / 2;
let saturated = batch * (full_s - limit) * limit;
ramp + saturated
}
fn sum_floor_upto(n: i128, divisor: i128) -> i128 {
if n < 0 {
return 0;
}
let q = n / divisor;
let r = n % divisor;
divisor * q * (q - 1) / 2 + q * (r + 1)
}
fn compressed_context_pairs(
batch: i128,
query_len: i128,
prefix: i128,
ratio: i128,
limit: i128,
) -> i128 {
if ratio <= 0 || query_len <= 0 || limit <= 0 {
return 0;
}
let start = prefix + 1;
let end = prefix + query_len;
let saturation_start = limit * ratio;
let total = if end < saturation_start {
sum_floor_upto(end, ratio) - sum_floor_upto(start - 1, ratio)
} else if start >= saturation_start {
query_len * limit
} else {
let ramp = sum_floor_upto(saturation_start - 1, ratio) - sum_floor_upto(start - 1, ratio);
ramp + (end - saturation_start + 1) * limit
};
batch * total
}
#[allow(clippy::too_many_arguments)]
pub(crate) fn dsv4_attention_sol_ms(
spec: &SystemSpec,
dims: &Dsv4SolDims,
compress_ratio: i64,
is_context: bool,
kv_quant: KvCacheQuantMode,
fmha_quant: FmhaQuantMode,
gemm_quant: GemmQuantMode,
b: i64,
s: i64,
prefix: i64,
local_heads: i64,
flops: Dsv4SolFlops,
) -> f64 {
let local_o_groups = dims.local_o_groups.max(1);
let (b, s, prefix) = (b as i128, s as i128, prefix as i128);
let nh = local_heads as i128;
let h = dims.hidden_size as i128;
let qlr = dims.q_lora_rank as i128;
let olr = dims.o_lora_rank as i128;
let hd = dims.head_dim as i128;
let rope_hd = dims.rope_head_dim as i128;
let inh = dims.index_n_heads as i128;
let ihd = dims.index_head_dim as i128;
let topk = dims.index_topk as i128;
let window = dims.window_size as i128;
let cr = compress_ratio as i128;
let lg = local_o_groups as i128;
let tokens = if is_context { b * s } else { b };
let kv_len = if is_context {
prefix + s
} else {
(s - 1).max(0)
};
let gemm_projection_ops = 2 * tokens * h * qlr
+ 2 * tokens * qlr * nh * hd
+ 2 * tokens * h * hd
+ 2 * tokens * lg * olr * h;
let output_absorption_ops = 2 * tokens * nh * hd * olr;
let compressor_mult: i128 = if cr == 4 { 2 } else { 1 };
let mut compressor_ops: i128 = 0;
if cr != 0 {
compressor_ops = 4 * tokens * h * compressor_mult * hd + 2 * tokens * compressor_mult * hd;
if cr == 4 {
let indexer_compressor_mult: i128 = 2;
compressor_ops += 4 * tokens * h * indexer_compressor_mult * ihd;
compressor_ops += 2 * tokens * indexer_compressor_mult * ihd;
}
}
let (window_pairs, compressed_pairs) = if is_context {
let wp = causal_limited_pairs(b, s, prefix, window);
let cp = if cr != 0 {
let limit = if cr == 4 { topk } else { (kv_len / cr).max(0) };
compressed_context_pairs(b, s, prefix, cr, limit)
} else {
0
};
(wp, cp)
} else {
let wp = b * kv_len.min(window);
let cp = if cr != 0 {
let limit = if cr == 4 { topk } else { (kv_len / cr).max(0) };
b * (kv_len / cr).min(limit)
} else {
0
};
(wp, cp)
};
let attention_pairs = window_pairs + compressed_pairs;
let attention_ops = 4 * nh * hd * attention_pairs;
let mut indexer_ops: i128 = 0;
let mut indexer_bfloat16_ops: i128 = 0;
let mut indexer_cache_bytes: f64 = 0.0;
if cr == 4 {
let compressed_len = kv_len / cr;
let indexer_query_tokens = if is_context { b * s } else { b };
let indexer_pairs = indexer_query_tokens * compressed_len;
indexer_ops = 2 * indexer_query_tokens * qlr * inh * ihd + 2 * indexer_pairs * inh * ihd;
indexer_bfloat16_ops = 2 * indexer_query_tokens * h * inh;
indexer_cache_bytes = (b * compressed_len) as f64 * (dims.index_head_dim as f64 * 0.5);
}
let gemm_mem = gemm_quant.mapping().memory;
let bf16_mem = GemmQuantMode::Bfloat16.mapping().memory;
let mut gemm_weight_bytes = (h * qlr + qlr * nh * hd + h * hd + lg * olr * h) as f64 * gemm_mem;
let mut bfloat16_weight_bytes = (nh * hd * olr) as f64 * bf16_mem;
if cr != 0 {
gemm_weight_bytes += (2 * h * compressor_mult * hd) as f64 * gemm_mem;
}
if cr == 4 {
gemm_weight_bytes += (qlr * inh * ihd) as f64 * gemm_mem;
bfloat16_weight_bytes += (h * inh) as f64 * bf16_mem;
}
let activation_bytes = (tokens * (h + qlr + nh * hd + hd + lg * olr)) as f64 * gemm_mem;
let kv_cache_bytes = (attention_pairs * hd) as f64 * kv_quant.mapping().memory;
let rope_bytes = (tokens * nh * rope_hd) as f64 * fmha_quant.mapping().memory;
let sol_math = ((gemm_projection_ops + compressor_ops) as f64 / flops.gemm
+ (output_absorption_ops + indexer_bfloat16_ops) as f64 / flops.bf16
+ indexer_ops as f64 / flops.fp8
+ attention_ops as f64 / flops.attn)
* 1000.0;
let sol_mem = (gemm_weight_bytes
+ bfloat16_weight_bytes
+ activation_bytes
+ kv_cache_bytes
+ indexer_cache_bytes
+ rope_bytes)
/ spec.gpu.mem_bw
* 1000.0;
sol_math.max(sol_mem)
}
pub(crate) fn normalize_dsv4_dtype(name: &str) -> String {
match name {
"fp8_e4m3" => "fp8".to_string(),
other => other.to_string(),
}
}
pub(crate) fn validate_dsv4_local_head_semantics(
observed: &BTreeMap<(String, String), BTreeSet<(u32, u32)>>,
) -> Result<(), AicError> {
for ((model, version), pairs) in observed {
let tps: BTreeSet<u32> = pairs.iter().map(|&(_, tp)| tp).collect();
let heads_constant = pairs.iter().map(|&(h, _)| h).collect::<BTreeSet<_>>().len() == 1;
let product_constant = pairs
.iter()
.map(|&(h, tp)| h * tp)
.collect::<BTreeSet<_>>()
.len()
== 1;
if tps.len() > 1 && heads_constant && !product_constant {
return Err(AicError::PerfDatabase(format!(
"DSV4 module rows for model={model:?} version={version:?} keep num_heads \
constant across tp_size values {tps:?}: that is the retired pre-#1131 NATIVE \
semantics (#1429). Migrate the file to rank-local heads \
(num_heads //= tp_size) before loading."
)));
}
}
Ok(())
}
fn load_module_parquet(sources: &[PerfSource], key_on_fmha: bool) -> Result<ModuleGrids, AicError> {
struct RawRow {
key: ModuleKey,
heads: u32,
tp: u32,
step: u32,
isl: u32,
batch: u32,
value: LeafValue,
}
let mut raw_rows: Vec<RawRow> = Vec::new();
let mut observed: BTreeMap<(String, String), BTreeSet<(u32, u32)>> = BTreeMap::new();
let mut any_source = false;
for source in sources {
let path = source.path();
if !path.exists() {
continue;
}
any_source = true;
let reader = PerfReader::open(path)?;
let mla_dtype_col = if key_on_fmha {
Some(reader.col("mla_dtype")?)
} else {
None
};
let kv_cache_dtype_col = reader.col("kv_cache_dtype")?;
let gemm_type_col = reader.col("gemm_type")?;
let model_col = reader.col_optional("model");
let version_col = reader.col_optional("version");
let num_heads_col = reader.col("num_heads")?;
let tp_size_col = reader.col("tp_size")?;
let batch_size_col = reader.col("batch_size")?;
let isl_col = reader.col("isl")?;
let step_col = reader.col("step")?;
let latency_col = reader.col("latency")?;
let power_col = reader.col_optional("power");
let ks_col = reader.col_optional("kernel_source");
for row in reader.rows()? {
let row = row?;
if !kernel_source_ok(source.kernel_sources(), ks_col, &row)? {
continue;
}
let key = ModuleKey {
fmha_quant: match mla_dtype_col {
Some(col) => normalize_dsv4_dtype(&row.str_owned(col)?),
None => String::new(),
},
kv_quant: normalize_dsv4_dtype(&row.str_owned(kv_cache_dtype_col)?),
gemm_quant: row.str_owned(gemm_type_col)?,
};
let model = match model_col {
Some(col) => row.str_owned(col)?,
None => String::new(),
};
let version = match version_col {
Some(col) => row.str_owned(col)?,
None => String::new(),
};
let heads = row.u32(num_heads_col)?;
let tp = row.u32(tp_size_col)?.max(1);
observed
.entry((model, version))
.or_default()
.insert((heads, tp));
let latency = row.f64(latency_col)?;
let power = row.f64_optional(power_col)?.unwrap_or(0.0);
raw_rows.push(RawRow {
key,
heads,
tp,
step: row.u32(step_col)?,
isl: row.u32(isl_col)?,
batch: row.u32(batch_size_col)?,
value: LeafValue::with_power(latency, power),
});
}
}
if !any_source || raw_rows.is_empty() {
return Err(AicError::PerfDatabase(format!(
"no DSV4 module rows loaded from {} source(s) (first: {})",
sources.len(),
sources
.first()
.map(|s| s.path().display().to_string())
.unwrap_or_default()
)));
}
validate_dsv4_local_head_semantics(&observed)?;
let mut by_keys: BTreeMap<ModuleKey, ByNative> = BTreeMap::new();
for row in raw_rows {
let (native_heads, local_heads) = (row.heads * row.tp, row.heads);
by_keys
.entry(row.key)
.or_default()
.entry(native_heads)
.or_default()
.entry(local_heads)
.or_default()
.entry(row.step)
.or_default()
.entry(row.isl)
.or_default()
.entry(row.batch)
.or_insert(row.value);
}
Ok(ModuleGrids { by_keys })
}
fn clone_err(err: &AicError) -> AicError {
AicError::PerfDatabase(err.to_string())
}
#[cfg(test)]
mod tests {
use super::*;
fn b200_sxm_spec() -> SystemSpec {
let systems_yaml = PathBuf::from(env!("CARGO_MANIFEST_DIR"))
.join("../..")
.join("python/aisimulate/src/aiconfigurator_core/systems/b200_sxm.yaml");
SystemSpec::load(&systems_yaml).expect("b200_sxm.yaml must parse")
}
fn b200_sglang_root() -> PathBuf {
PathBuf::from(env!("CARGO_MANIFEST_DIR")).join(
"../../python/aisimulate/src/aiconfigurator_core/systems/data/b200_sxm/sglang/0.5.10",
)
}
#[test]
fn dsv4_data_absent_errors_cleanly() {
let root = PathBuf::from(env!("CARGO_MANIFEST_DIR")).join(
"../../python/aisimulate/src/aiconfigurator_core/systems/data/b200_sxm/vllm/0.19.0",
);
let table = Dsv4Table::new(root);
let spec = b200_sxm_spec();
let err = table
.query_context(
&spec,
AttnKind::Csa,
1,
1024,
128, 128, KvCacheQuantMode::Bfloat16,
FmhaQuantMode::Bfloat16,
GemmQuantMode::Bfloat16,
"DeepseekV4ForCausalLM",
0,
None,
)
.unwrap_err();
match err {
AicError::Io { .. } | AicError::PerfDatabase(_) => {}
other => panic!("unexpected error: {other:?}"),
}
}
#[test]
fn dsv4_stale_native_semantics_guard() {
let observed = |pairs: &[(u32, u32)]| {
let mut m: BTreeMap<(String, String), BTreeSet<(u32, u32)>> = BTreeMap::new();
m.insert(
(
"deepseek-ai/DeepSeek-V4-Pro".to_string(),
"0.5.10".to_string(),
),
pairs.iter().copied().collect(),
);
m
};
let err = validate_dsv4_local_head_semantics(&observed(&[
(128, 1),
(128, 2),
(128, 4),
(128, 8),
]))
.unwrap_err();
assert!(
err.to_string().contains("pre-#1131 NATIVE semantics"),
"{err}"
);
validate_dsv4_local_head_semantics(&observed(&[(128, 1), (64, 2), (32, 4), (16, 8)]))
.unwrap();
validate_dsv4_local_head_semantics(&observed(&[(64, 1)])).unwrap();
validate_dsv4_local_head_semantics(&observed(&[(16, 8)])).unwrap();
}
#[test]
fn dsv4_query_matches_python_v2_engine() {
let root = b200_sglang_root();
let table = Dsv4Table::new(root);
let spec = b200_sxm_spec();
let q_ctx = |kind, b, isl, prefix| {
table
.query_context(
&spec,
kind,
b,
isl,
16,
128,
KvCacheQuantMode::Fp8,
FmhaQuantMode::Bfloat16,
GemmQuantMode::Fp8Block,
"DeepseekV4ForCausalLM",
prefix,
None,
)
.unwrap()
.latency
};
let q_gen = |kind, b, s| {
table
.query_generation(
&spec,
kind,
b,
s,
16,
128,
KvCacheQuantMode::Fp8,
GemmQuantMode::Fp8Block,
"DeepseekV4ForCausalLM",
None,
)
.unwrap()
.latency
};
let approx = |got: f64, want: f64| {
assert!(
((got - want) / want).abs() < 1e-9,
"rust {got} vs python {want}"
);
};
approx(q_ctx(AttnKind::Csa, 8, 512, 0), 2.420168121549068);
approx(q_ctx(AttnKind::Csa, 8, 768, 0), 3.6953556020458156);
approx(q_ctx(AttnKind::Csa, 12, 512, 0), 3.630231679486889);
approx(q_ctx(AttnKind::Csa, 8, 8192, 0), 55.89053506569064);
approx(q_ctx(AttnKind::Csa, 8, 512, 1024), 2.693620664767868);
approx(q_ctx(AttnKind::Hca, 1, 128, 0), 0.1104);
approx(q_ctx(AttnKind::Hca, 8, 8192, 0), 24.400787721796174);
approx(q_gen(AttnKind::Csa, 16, 385), 0.14096129656201914);
approx(q_gen(AttnKind::Csa, 16, 200), 0.14026034674381602);
approx(q_gen(AttnKind::Csa, 16, 100000), 0.19331215178685213);
approx(q_gen(AttnKind::Csa, 15, 385), 0.1407382148927009);
approx(q_gen(AttnKind::Hca, 16, 385), 0.08631992189686928);
}
#[test]
fn dsv4_pro_head_resolution_and_ragged_generation() {
let root = b200_sglang_root();
let table = Dsv4Table::new(root);
let spec = b200_sxm_spec();
let q_gen = |kind, b, s| {
table
.query_generation(
&spec,
kind,
b,
s,
16,
128,
KvCacheQuantMode::Fp8,
GemmQuantMode::Fp8Block,
"DeepseekV4ForCausalLM",
None,
)
.unwrap()
.latency
};
let q_ctx = |kind, b, isl| {
table
.query_context(
&spec,
kind,
b,
isl,
16,
128,
KvCacheQuantMode::Fp8,
FmhaQuantMode::Bfloat16,
GemmQuantMode::Fp8Block,
"DeepseekV4ForCausalLM",
0,
None,
)
.unwrap()
.latency
};
let approx = |got: f64, want: f64| {
assert!(
((got - want) / want).abs() < 1e-9,
"rust {got} vs python {want}"
);
};
approx(q_gen(AttnKind::Csa, 16, 385), 0.14096129656201914);
approx(q_gen(AttnKind::Hca, 16, 385), 0.08631992189686928);
approx(q_gen(AttnKind::Csa, 15, 385), 0.1407382148927009);
approx(q_ctx(AttnKind::Csa, 1, 128), 0.1659);
approx(q_ctx(AttnKind::Hca, 1, 128), 0.1104);
}
#[test]
fn normalize_dsv4_dtype_aliases_fp8_e4m3() {
assert_eq!(normalize_dsv4_dtype("fp8_e4m3"), "fp8");
assert_eq!(normalize_dsv4_dtype("bfloat16"), "bfloat16");
assert_eq!(normalize_dsv4_dtype("fp8_block"), "fp8_block");
assert_eq!(normalize_dsv4_dtype("fp8"), "fp8");
}
#[test]
fn dsv4_context_resolves_fp8_e4m3_kv_quant() {
let root = b200_sglang_root();
let table = Dsv4Table::new(root);
let spec = b200_sxm_spec();
let latency = table
.query_context(
&spec,
AttnKind::Csa,
8, 512, 64, 64, KvCacheQuantMode::Fp8,
FmhaQuantMode::Bfloat16,
GemmQuantMode::Fp8Block,
"DeepseekV4ForCausalLM",
0, None,
)
.expect("DSV4 context lookup must resolve fp8_e4m3 kv_cache_dtype as fp8")
.latency;
assert!(
latency.is_finite() && latency > 0.0,
"unexpected latency: {latency}"
);
}
use parquet::data_type::{ByteArray, ByteArrayType, DoubleType, Int64Type};
use parquet::file::properties::WriterProperties;
use parquet::file::writer::{SerializedFileWriter, SerializedRowGroupWriter};
use parquet::schema::parser::parse_message_type;
use std::fs::File;
use std::path::Path;
use std::sync::Arc;
fn write_column<T: parquet::data_type::DataType>(
rg: &mut SerializedRowGroupWriter<'_, File>,
values: &[T::T],
) {
let mut col = rg.next_column().unwrap().unwrap();
col.typed::<T>().write_batch(values, None, None).unwrap();
col.close().unwrap();
}
fn write_calib_parquet(path: &Path, rows: &[(&str, i64, i64, i64, f64)]) {
let schema = Arc::new(
parse_message_type(
"message calib {
REQUIRED BYTE_ARRAY score_mode (UTF8);
REQUIRED INT64 step;
REQUIRED INT64 isl;
REQUIRED INT64 batch_size;
REQUIRED DOUBLE latency;
}",
)
.unwrap(),
);
let file = File::create(path).unwrap();
let mut writer =
SerializedFileWriter::new(file, schema, Arc::new(WriterProperties::builder().build()))
.unwrap();
let mut rg = writer.next_row_group().unwrap();
let modes: Vec<ByteArray> = rows.iter().map(|r| ByteArray::from(r.0)).collect();
write_column::<ByteArrayType>(&mut rg, &modes);
write_column::<Int64Type>(&mut rg, &rows.iter().map(|r| r.1).collect::<Vec<_>>());
write_column::<Int64Type>(&mut rg, &rows.iter().map(|r| r.2).collect::<Vec<_>>());
write_column::<Int64Type>(&mut rg, &rows.iter().map(|r| r.3).collect::<Vec<_>>());
write_column::<DoubleType>(&mut rg, &rows.iter().map(|r| r.4).collect::<Vec<_>>());
rg.close().unwrap();
writer.close().unwrap();
}
fn write_module_parquet(path: &Path, rows: &[(i64, i64, i64, i64, f64)]) {
let schema = Arc::new(
parse_message_type(
"message module {
REQUIRED BYTE_ARRAY architecture (UTF8);
REQUIRED BYTE_ARRAY mla_dtype (UTF8);
REQUIRED BYTE_ARRAY kv_cache_dtype (UTF8);
REQUIRED BYTE_ARRAY gemm_type (UTF8);
REQUIRED INT64 num_heads;
REQUIRED INT64 tp_size;
REQUIRED INT64 batch_size;
REQUIRED INT64 isl;
REQUIRED INT64 step;
REQUIRED DOUBLE latency;
}",
)
.unwrap(),
);
let file = File::create(path).unwrap();
let mut writer =
SerializedFileWriter::new(file, schema, Arc::new(WriterProperties::builder().build()))
.unwrap();
let mut rg = writer.next_row_group().unwrap();
let n = rows.len();
write_column::<ByteArrayType>(&mut rg, &vec![ByteArray::from("DeepseekV4ForCausalLM"); n]);
write_column::<ByteArrayType>(&mut rg, &vec![ByteArray::from("bfloat16"); n]);
write_column::<ByteArrayType>(&mut rg, &vec![ByteArray::from("fp8"); n]);
write_column::<ByteArrayType>(&mut rg, &vec![ByteArray::from("fp8_block"); n]);
write_column::<Int64Type>(&mut rg, &rows.iter().map(|r| r.0).collect::<Vec<_>>());
write_column::<Int64Type>(&mut rg, &vec![1i64; n]); write_column::<Int64Type>(&mut rg, &rows.iter().map(|r| r.1).collect::<Vec<_>>());
write_column::<Int64Type>(&mut rg, &rows.iter().map(|r| r.2).collect::<Vec<_>>());
write_column::<Int64Type>(&mut rg, &rows.iter().map(|r| r.3).collect::<Vec<_>>());
write_column::<DoubleType>(&mut rg, &rows.iter().map(|r| r.4).collect::<Vec<_>>());
rg.close().unwrap();
writer.close().unwrap();
}
#[test]
fn dsv4_flash_op_spec_dims_change_beyond_grid_hold() {
use crate::operators::dsv4::Dsv4ModuleOp;
let dir = tempfile::tempdir().unwrap();
write_module_parquet(
&dir.path().join("dsv4_hca_context_module_perf.parquet"),
&[(64, 1, 1024, 0, 1.0), (64, 1, 2048, 0, 2.0)],
);
let table = Dsv4Table::new(dir.path().to_path_buf());
let spec = b200_sxm_spec();
let flash: Dsv4ModuleOp = serde_json::from_value(serde_json::json!({
"name": "context_attention",
"scale_factor": 1.0,
"attn_kind": "Hca",
"num_heads": 64,
"native_heads": 64,
"tp_size": 1,
"kv_cache_dtype": "fp8",
"fmha_quant_mode": "bfloat16",
"gemm_quant_mode": "fp8_block",
"architecture": "DeepseekV4ForCausalLM",
"window_size": 128,
"hidden_size": 4096,
"q_lora_rank": 1024,
"o_lora_rank": 1024,
"head_dim": 512,
"rope_head_dim": 64,
"index_n_heads": 64,
"index_head_dim": 128,
"index_topk": 512,
"o_groups": 8,
}))
.expect("Flash op spec must deserialize");
let q = |sol_dims, isl: u32| {
table
.query_context(
&spec,
AttnKind::Hca,
1,
isl,
64,
64,
KvCacheQuantMode::Fp8,
FmhaQuantMode::Bfloat16,
GemmQuantMode::Fp8Block,
"DeepseekV4ForCausalLM",
0,
sol_dims,
)
.unwrap()
.latency
};
let approx = |got: f64, want: f64| {
assert!(
((got - want) / want).abs() < 1e-9,
"rust {got} vs python {want}"
);
};
let flash_hold = q(Some(flash.sol_dims()), 8192);
let pro_hold = q(None, 8192); approx(flash_hold, 8.190924981120823);
approx(pro_hold, 8.143458149525658);
assert!(
(flash_hold - pro_hold).abs() > 1e-3,
"Flash dims must change the hold ({flash_hold} vs {pro_hold})"
);
approx(q(Some(flash.sol_dims()), 1536), 1.5);
approx(q(None, 1536), 1.5);
}
#[test]
fn dsv4_op_spec_dims_default_to_pinned_pro() {
use crate::operators::dsv4::Dsv4ModuleOp;
let old_spec: Dsv4ModuleOp = serde_json::from_value(serde_json::json!({
"name": "context_attention",
"scale_factor": 1.0,
"attn_kind": "Csa",
"num_heads": 16,
"native_heads": 128,
"tp_size": 8,
"kv_cache_dtype": "fp8",
"fmha_quant_mode": "bfloat16",
"gemm_quant_mode": "fp8_block",
"architecture": "DeepseekV4ForCausalLM",
}))
.expect("old op spec must deserialize");
let got = old_spec.sol_dims();
let want = Dsv4SolDims::from_pinned(dsv4_dims("DeepseekV4ForCausalLM"), 16);
assert_eq!(got.hidden_size, want.hidden_size);
assert_eq!(got.q_lora_rank, want.q_lora_rank);
assert_eq!(got.o_lora_rank, want.o_lora_rank);
assert_eq!(got.head_dim, want.head_dim);
assert_eq!(got.rope_head_dim, want.rope_head_dim);
assert_eq!(got.index_n_heads, want.index_n_heads);
assert_eq!(got.index_head_dim, want.index_head_dim);
assert_eq!(got.index_topk, want.index_topk);
assert_eq!(got.window_size, want.window_size);
assert_eq!(got.local_o_groups, want.local_o_groups); assert_eq!(got.local_o_groups, 2);
}
#[test]
fn topk_calib_loader_and_delta_match_python_oracle() {
let dir = tempfile::tempdir().unwrap();
let path = dir.path().join("dsv4_csa_topk_calib_perf.parquet");
write_calib_parquet_with_heads(
&path,
&[
("v1_flat", 0, 512, 1, 64, 1.0),
("v1_top_last", 0, 512, 1, 64, 0.4),
("v1_flat", 0, 512, 4, 64, 2.0),
("v1_top_last", 0, 512, 4, 64, 0.9),
("v1_flat", 0, 2048, 1, 64, 3.0),
("v1_top_last", 0, 2048, 1, 64, 1.0),
("v1_flat", 0, 2048, 4, 64, 5.0),
("v1_top_last", 0, 2048, 4, 64, 2.2),
("v1_flat", 1024, 512, 1, 64, 1.5),
("v1_top_last", 1024, 512, 1, 64, 0.7),
("v1_flat", 1024, 512, 4, 64, 2.5),
("v1_top_last", 1024, 512, 4, 64, 1.0),
("v1_flat", 4096, 512, 1, 64, 0.5),
("v1_top_last", 4096, 512, 1, 64, 0.9), ("v1_flat", 8192, 512, 1, 64, 9.9), ],
);
let calib = load_topk_calib_parquet(&[PerfSource(path.clone(), None)])
.unwrap()
.expect("calib must load");
let expected_exact = [
((0u32, 512u32, 1u32), 0.6),
((0, 512, 4), 1.1),
((0, 2048, 1), 2.0),
((0, 2048, 4), 2.8),
((1024, 512, 1), 0.8),
((1024, 512, 4), 1.5),
((4096, 512, 1), 0.0),
];
let v1_native = &calib.exact_v1[&64];
assert_eq!(v1_native.len(), expected_exact.len());
assert!(calib.exact_v2.is_empty());
assert!(calib.exact_v1.get(&128).is_none());
for (key, want) in expected_exact {
let got = v1_native[&key];
assert!(
(got - want).abs() < 1e-12,
"exact[{key:?}] = {got} vs {want}"
);
}
let oracle = [
((0u32, 512u32, 1u32), 0.6), ((512, 512, 1), 0.7), ((2048, 512, 1), 0.5333333333333334), ((99999, 512, 4), 1.5), ((8192, 512, 1), 0.0), ((0, 1024, 1), 1.0666666666666667), ((0, 512, 2), 0.7666666666666667), ((512, 1024, 2), 1.3555555555555556), ((384, 1, 16), 1.25), ((0, 512, 4), 1.1), ];
for ((prefix, isl, bs), want) in oracle {
let got = topk_delta_ms(v1_native, prefix, isl, bs);
let tol = if want == 0.0 { 1e-12 } else { want * 1e-9 };
assert!(
(got - want).abs() <= tol,
"delta({prefix},{isl},{bs}) = {got} vs python {want}"
);
}
}
#[test]
fn topk_calib_absent_file_is_noop() {
let dir = tempfile::tempdir().unwrap();
let missing = dir.path().join("dsv4_csa_topk_calib_perf.parquet");
let calib = load_topk_calib_parquet(&[PerfSource(missing.clone(), None)]).unwrap();
assert!(calib.is_none(), "absent file must load as None");
assert_eq!(apply_topk_delta(1.25, None, 0, 512, 8), 1.25);
}
#[test]
fn topk_correction_env_gate_matches_python() {
assert!(parse_topk_correction_env(None));
assert!(parse_topk_correction_env(Some("1")));
assert!(parse_topk_correction_env(Some("")));
assert!(parse_topk_correction_env(Some("false")));
assert!(!parse_topk_correction_env(Some("0")));
}
#[test]
fn topk_delta_clamps_corrected_latency_at_zero() {
let mut exact = BTreeMap::new();
exact.insert((0u32, 512u32, 8u32), 0.12);
assert_eq!(apply_topk_delta(0.05, Some(&exact), 0, 512, 8), 0.0);
assert!((apply_topk_delta(1.0, Some(&exact), 0, 512, 8) - 0.88).abs() < 1e-12);
}
#[test]
fn dsv4_query_applies_topk_delta_end_to_end() {
if !topk_correction_enabled() {
return; }
let spec = b200_sxm_spec();
let csa_ctx_rows = [(64, 8, 512, 0, 1.0)];
let hca_ctx_rows = [(64, 8, 512, 0, 0.7)];
let csa_gen_rows = [(64, 16, 1, 384, 0.5)]; let calib_rows = [
("v1_flat", 0, 512, 8, 64, 0.30),
("v1_top_last", 0, 512, 8, 64, 0.18), ("v2_flat", 384, 1, 16, 64, 0.05),
("v2_top_last", 384, 1, 16, 64, 0.02), ];
let make_root = |with_calib: bool| {
let dir = tempfile::tempdir().unwrap();
write_module_parquet(
&dir.path().join("dsv4_csa_context_module_perf.parquet"),
&csa_ctx_rows,
);
write_module_parquet(
&dir.path().join("dsv4_hca_context_module_perf.parquet"),
&hca_ctx_rows,
);
write_module_parquet(
&dir.path().join("dsv4_csa_generation_module_perf.parquet"),
&csa_gen_rows,
);
if with_calib {
write_calib_parquet_with_heads(
&dir.path().join("dsv4_csa_topk_calib_perf.parquet"),
&calib_rows,
);
}
dir
};
let q_ctx = |table: &Dsv4Table, kind| {
table
.query_context(
&spec,
kind,
8,
512,
64,
64,
KvCacheQuantMode::Fp8,
FmhaQuantMode::Bfloat16,
GemmQuantMode::Fp8Block,
"DeepseekV4ForCausalLM",
0,
None,
)
.unwrap()
.latency
};
let q_gen = |table: &Dsv4Table, kind| {
table
.query_generation(
&spec,
kind,
16,
385,
64,
64,
KvCacheQuantMode::Fp8,
GemmQuantMode::Fp8Block,
"DeepseekV4ForCausalLM",
None,
)
.unwrap()
.latency
};
let root = make_root(true);
let table = Dsv4Table::new(root.path().to_path_buf());
assert!((q_ctx(&table, AttnKind::Csa) - 0.88).abs() < 1e-12); assert!((q_gen(&table, AttnKind::Csa) - 0.47).abs() < 1e-12); assert!((q_ctx(&table, AttnKind::Hca) - 0.7).abs() < 1e-12); let uncorrected = table
.query_context(
&spec,
AttnKind::Csa,
8,
512,
64,
128,
KvCacheQuantMode::Fp8,
FmhaQuantMode::Bfloat16,
GemmQuantMode::Fp8Block,
"DeepseekV4ForCausalLM",
0,
None,
)
.unwrap()
.latency;
assert!((uncorrected - 1.0).abs() < 1e-12);
let bare_root = make_root(false);
let table = Dsv4Table::new(bare_root.path().to_path_buf());
assert!((q_ctx(&table, AttnKind::Csa) - 1.0).abs() < 1e-12);
assert!((q_gen(&table, AttnKind::Csa) - 0.5).abs() < 1e-12);
}
fn write_sparse_kernel_parquet(path: &Path, rows: &[(i64, i64, i64, i64, i64, f64)]) {
let schema = Arc::new(
parse_message_type(
"message sparse {
REQUIRED INT64 num_heads;
REQUIRED INT64 batch_size;
REQUIRED INT64 isl;
REQUIRED INT64 tp_size;
REQUIRED INT64 step;
REQUIRED DOUBLE latency;
}",
)
.unwrap(),
);
let file = File::create(path).unwrap();
let mut writer =
SerializedFileWriter::new(file, schema, Arc::new(WriterProperties::builder().build()))
.unwrap();
let mut rg = writer.next_row_group().unwrap();
write_column::<Int64Type>(&mut rg, &rows.iter().map(|r| r.0).collect::<Vec<_>>());
write_column::<Int64Type>(&mut rg, &rows.iter().map(|r| r.1).collect::<Vec<_>>());
write_column::<Int64Type>(&mut rg, &rows.iter().map(|r| r.2).collect::<Vec<_>>());
write_column::<Int64Type>(&mut rg, &rows.iter().map(|r| r.3).collect::<Vec<_>>());
write_column::<Int64Type>(&mut rg, &rows.iter().map(|r| r.4).collect::<Vec<_>>());
write_column::<DoubleType>(&mut rg, &rows.iter().map(|r| r.5).collect::<Vec<_>>());
rg.close().unwrap();
writer.close().unwrap();
}
fn write_calib_parquet_with_heads(path: &Path, rows: &[(&str, i64, i64, i64, i64, f64)]) {
let schema = Arc::new(
parse_message_type(
"message calib {
REQUIRED BYTE_ARRAY score_mode (UTF8);
REQUIRED INT64 step;
REQUIRED INT64 isl;
REQUIRED INT64 batch_size;
REQUIRED INT64 num_heads;
REQUIRED DOUBLE latency;
}",
)
.unwrap(),
);
let file = File::create(path).unwrap();
let mut writer =
SerializedFileWriter::new(file, schema, Arc::new(WriterProperties::builder().build()))
.unwrap();
let mut rg = writer.next_row_group().unwrap();
let modes: Vec<ByteArray> = rows.iter().map(|r| ByteArray::from(r.0)).collect();
write_column::<ByteArrayType>(&mut rg, &modes);
write_column::<Int64Type>(&mut rg, &rows.iter().map(|r| r.1).collect::<Vec<_>>());
write_column::<Int64Type>(&mut rg, &rows.iter().map(|r| r.2).collect::<Vec<_>>());
write_column::<Int64Type>(&mut rg, &rows.iter().map(|r| r.3).collect::<Vec<_>>());
write_column::<Int64Type>(&mut rg, &rows.iter().map(|r| r.4).collect::<Vec<_>>());
write_column::<DoubleType>(&mut rg, &rows.iter().map(|r| r.5).collect::<Vec<_>>());
rg.close().unwrap();
writer.close().unwrap();
}
#[test]
fn paged_mqa_lookup_exact_tp_fallback_and_missing() {
let dir = tempfile::tempdir().unwrap();
write_sparse_kernel_parquet(
&dir.path().join("dsv4_paged_mqa_logits_module_perf.parquet"),
&[
(64, 1, 8192, 1, 0, 0.2),
(64, 1, 8192, 1, 8192, 0.3),
(64, 1, 2048, 1, 0, 0.05),
],
);
let table = Dsv4Table::new(dir.path().to_path_buf());
assert_eq!(
table.query_paged_mqa_logits(1, 8192, 0, 1, 64).unwrap(),
Some(0.2)
);
assert_eq!(
table.query_paged_mqa_logits(1, 8192, 8192, 1, 64).unwrap(),
Some(0.3)
);
assert_eq!(
table.query_paged_mqa_logits(1, 8192, 0, 8, 64).unwrap(),
Some(0.2)
);
assert_eq!(
table.query_paged_mqa_logits(1, 8192, 0, 1, 32).unwrap(),
None
);
let empty = tempfile::tempdir().unwrap();
let bare = Dsv4Table::new(empty.path().to_path_buf());
assert_eq!(
bare.query_paged_mqa_logits(1, 8192, 0, 1, 64).unwrap(),
None
);
}
#[test]
fn csa_topk_top_last_raw_rows_lookup() {
let dir = tempfile::tempdir().unwrap();
let path = dir.path().join("dsv4_csa_topk_calib_perf.parquet");
write_calib_parquet_with_heads(
&path,
&[
("v1_top_last", 0, 16384, 1, 64, 800.0),
("v1_top_last", 0, 2048, 1, 64, 100.0),
("v1_flat", 0, 2048, 1, 64, 130.0),
],
);
let table = Dsv4Table::new(dir.path().to_path_buf());
assert_eq!(
table.csa_topk_top_last(16384, 0, 64, 1).unwrap(),
Some(800.0)
);
assert_eq!(
table.csa_topk_top_last(2048, 0, 64, 1).unwrap(),
Some(100.0)
);
let err = table.csa_topk_top_last(32768, 0, 64, 1).unwrap_err();
assert!(
err.to_string().contains("exceeds the collected"),
"unexpected: {err}"
);
let empty = tempfile::tempdir().unwrap();
let bare = Dsv4Table::new(empty.path().to_path_buf());
assert_eq!(bare.csa_topk_top_last(2048, 0, 64, 1).unwrap(), None);
}
#[test]
fn csa_topk_calib_without_num_heads_column_loads_nothing() {
let dir = tempfile::tempdir().unwrap();
write_calib_parquet(
&dir.path().join("dsv4_csa_topk_calib_perf.parquet"),
&[
("v1_top_last", 0, 2048, 1, 100.0),
("v1_flat", 0, 2048, 1, 130.0),
],
);
let table = Dsv4Table::new(dir.path().to_path_buf());
assert!(table.load_topk_calib().unwrap().is_none());
assert_eq!(table.csa_topk_top_last(2048, 0, 64, 1).unwrap(), None);
}
#[test]
fn csa_topk_top_last_loads_from_donor_sources() {
let dir = tempfile::tempdir().unwrap();
let primary = dir.path().join("primary/dsv4_csa_topk_calib_perf.parquet");
let donor = dir.path().join("donor/dsv4_csa_topk_calib_perf.parquet");
std::fs::create_dir_all(primary.parent().unwrap()).unwrap();
std::fs::create_dir_all(donor.parent().unwrap()).unwrap();
write_calib_parquet_with_heads(
&donor,
&[
("v1_top_last", 0, 2048, 1, 64, 100.0),
("v1_top_last", 0, 4096, 1, 64, 200.0),
],
);
let sources = vec![
PerfSource(primary.clone(), None),
PerfSource(donor.clone(), None),
];
let calib = load_topk_calib_parquet(&sources)
.unwrap()
.expect("donor rows must load");
let grid = calib.top_last.get(&64).expect("native bucket");
assert_eq!(grid.get(&1).and_then(|g| g.get(&(2048, 0))), Some(&100.0));
assert_eq!(grid.get(&1).and_then(|g| g.get(&(4096, 0))), Some(&200.0));
write_calib_parquet_with_heads(&primary, &[("v1_top_last", 0, 2048, 1, 64, 42.0)]);
let calib = load_topk_calib_parquet(&sources)
.unwrap()
.expect("calib must load");
let grid = calib.top_last.get(&64).expect("native bucket");
assert_eq!(grid.get(&1).and_then(|g| g.get(&(2048, 0))), Some(&42.0));
assert_eq!(grid.get(&1).and_then(|g| g.get(&(4096, 0))), Some(&200.0));
}
#[test]
fn csa_topk_top_last_filters_num_heads_when_present() {
let dir = tempfile::tempdir().unwrap();
write_calib_parquet_with_heads(
&dir.path().join("dsv4_csa_topk_calib_perf.parquet"),
&[
("v1_top_last", 0, 2048, 1, 64, 42.0),
("v1_top_last", 0, 2048, 1, 128, 77.0),
],
);
let table = Dsv4Table::new(dir.path().to_path_buf());
assert_eq!(table.csa_topk_top_last(2048, 0, 64, 1).unwrap(), Some(42.0));
assert_eq!(
table.csa_topk_top_last(2048, 0, 128, 1).unwrap(),
Some(77.0)
);
assert_eq!(table.csa_topk_top_last(2048, 0, 32, 1).unwrap(), None);
}
#[test]
fn dsv4_csa_topk_energy_rescale_matches_python_oracle() {
use crate::perf_database::energy_test_fixtures::{Col, energy_test_spec, write_parquet};
let tmp = tempfile::tempdir().expect("tmpdir");
write_parquet(
&tmp.path().join("dsv4_csa_context_module_perf.parquet"),
&[
Col::Str("architecture", vec!["DeepseekV4ForCausalLM"]),
Col::Str("mla_dtype", vec!["bfloat16"]),
Col::Str("kv_cache_dtype", vec!["fp8"]),
Col::Str("gemm_type", vec!["fp8_block"]),
Col::Str("model", vec!["m"]),
Col::Str("version", vec!["v"]),
Col::I64("num_heads", vec![64]),
Col::I64("tp_size", vec![1]),
Col::I64("batch_size", vec![8]),
Col::I64("isl", vec![512]),
Col::I64("step", vec![0]),
Col::F64("latency", vec![1.0]),
Col::F64("power", vec![100.0]),
],
);
write_calib_parquet_with_heads(
&tmp.path().join("dsv4_csa_topk_calib_perf.parquet"),
&[
("v1_flat", 0, 512, 8, 64, 0.30),
("v1_top_last", 0, 512, 8, 64, 0.18), ],
);
let table = Dsv4Table::new(tmp.path().to_path_buf());
let spec = energy_test_spec();
let v = table
.query_context(
&spec,
AttnKind::Csa,
8,
512,
64,
64,
KvCacheQuantMode::Fp8,
FmhaQuantMode::Bfloat16,
GemmQuantMode::Fp8Block,
"DeepseekV4ForCausalLM",
0,
None,
)
.unwrap();
assert!((v.latency - 0.88).abs() < 1e-12, "latency {}", v.latency);
assert!((v.energy - 88.0).abs() < 1e-9 * 88.0, "energy {}", v.energy);
}
}