use crate::common::enums::{DatabaseMode, FmhaQuantMode, KvCacheQuantMode, TransferKind};
use crate::common::error::AicError;
use crate::common::system_spec::SystemSpec;
use crate::operators::base::{PerformanceResult, SolComponents, Source};
use crate::operators::util_empirical::{self, UtilGrid};
use crate::perf_database::PerfDatabase;
use crate::perf_database::attention::{
context_attention_sol_ms, context_attention_sol_with_prefix,
context_attention_sol_with_prefix_ms, encoder_attention_sol, encoder_attention_sol_ms,
generation_attention_sol, generation_attention_sol_ms, generation_attn_flops,
};
use crate::perf_database::gemm::quant_tc_flops;
use serde::{Deserialize, Serialize};
pub(crate) fn mem_op_latency_ms(spec: &SystemSpec, mem_bytes: f64) -> f64 {
let mem_bw = spec.gpu.mem_bw.max(1.0);
let scaling = spec.gpu.mem_bw_empirical_scaling_factor.max(1e-9);
let constant = spec.gpu.mem_empirical_constant_latency;
(mem_bytes / (mem_bw * scaling) + constant) * 1000.0
}
pub(crate) fn query_mem_op(db: &PerfDatabase, mem_bytes: f64) -> PerformanceResult {
match db.database_mode {
DatabaseMode::Sol | DatabaseMode::SolFull => PerformanceResult::sol(SolComponents::new(
0.0,
mem_bytes / db.system_spec.gpu.mem_bw.max(1.0) * 1000.0,
)),
_ => PerformanceResult::new(
mem_op_latency_ms(&db.system_spec, mem_bytes),
Source::Empirical,
),
}
}
fn prefix_correction(full_s: u32, prefix: u32) -> f64 {
if full_s == 0 {
return 0.0;
}
let f = full_s as f64;
let p = prefix as f64;
(f * f - p * p) / (f * f)
}
const ATTN_PREFILL_HS_RATIO_TRTLLM: &[(u32, f64)] = &[
(64, 0.58),
(128, 1.00),
(192, 1.10),
(256, 1.17),
(512, 1.20),
];
const ATTN_PREFILL_HS_RATIO_SGLANG: &[(u32, f64)] = &[
(64, 0.60),
(128, 1.00),
(192, 1.18),
(256, 1.32),
(512, 1.38),
];
const ATTN_PREFILL_HS_RATIO_VLLM: &[(u32, f64)] = &[
(64, 0.60),
(128, 1.00),
(192, 1.27),
(256, 1.51),
(512, 1.60),
];
fn attn_prefill_hs_ratio(backend: &str, head_size: u32) -> f64 {
let table = match backend {
"trtllm" => ATTN_PREFILL_HS_RATIO_TRTLLM,
"sglang" => ATTN_PREFILL_HS_RATIO_SGLANG,
"vllm" => ATTN_PREFILL_HS_RATIO_VLLM,
_ => return 1.0,
};
if let Some(&(_, ratio)) = table.iter().find(|&&(h, _)| h == head_size) {
return ratio;
}
let (first, last) = (table[0], table[table.len() - 1]);
if head_size <= first.0 {
return first.1;
}
if head_size >= last.0 {
return last.1;
}
let (lo, lo_ratio) = *table
.iter()
.rev()
.find(|&&(h, _)| h < head_size)
.expect("lower bracket");
let (hi, hi_ratio) = *table
.iter()
.find(|&&(h, _)| h > head_size)
.expect("upper bracket");
let t = ((head_size as f64).log2() - (lo as f64).log2())
/ ((hi as f64).log2() - (lo as f64).log2());
lo_ratio + t * (hi_ratio - lo_ratio)
}
fn ref_head_size(available: &[u32], target: u32) -> Option<u32> {
let avail: Vec<u32> = available.iter().copied().filter(|&h| h != 0).collect();
if avail.is_empty() {
return None;
}
if avail.contains(&128) {
return Some(128);
}
let features: Vec<Vec<f64>> = avail.iter().map(|&h| vec![h as f64]).collect();
let idx = util_empirical::nearest_candidate_index(&[target as f64], &features)?;
Some(avail[idx])
}
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
pub struct ContextAttentionOp {
pub name: String,
pub scale_factor: f64,
pub n: u32,
pub n_kv: u32,
pub head_size: u32,
pub window_size: u32,
pub kv_cache_dtype: KvCacheQuantMode,
pub fmha_quant_mode: FmhaQuantMode,
pub use_qk_norm: bool,
#[serde(default = "crate::operators::gemm::default_seq_split")]
pub cp_size: u32,
#[serde(default = "default_lane_order")]
pub lane_order: Vec<String>,
}
pub(crate) fn default_lane_order() -> Vec<String> {
vec![crate::perf_database::attention::DEFAULT_LANE.to_string()]
}
impl ContextAttentionOp {
pub fn new(
name: impl Into<String>,
n: u32,
n_kv: u32,
head_size: u32,
kv_cache_dtype: KvCacheQuantMode,
fmha_quant_mode: FmhaQuantMode,
) -> Self {
Self {
name: name.into(),
scale_factor: 1.0,
n,
n_kv,
head_size,
window_size: 0,
kv_cache_dtype,
fmha_quant_mode,
use_qk_norm: false,
cp_size: 1,
lane_order: default_lane_order(),
}
}
pub fn query(
&self,
db: &PerfDatabase,
batch_size: u32,
isl: u32,
prefix: u32,
seq_imbalance_correction_scale: f64,
) -> Result<PerformanceResult, AicError> {
let ctx = |s: u32, pfx: u32| -> Result<PerformanceResult, AicError> {
query_context_attention_table(
db,
&self.lane_order,
batch_size,
s,
pfx,
self.n,
self.n_kv,
self.head_size,
self.window_size,
self.kv_cache_dtype,
self.fmha_quant_mode,
)
};
let mut result = if self.cp_size > 1 {
let c = isl.div_ceil(2 * self.cp_size).max(1);
ctx(c, prefix)?.plus(ctx(c, prefix + isl - c)?)
} else {
ctx(isl, prefix)?
};
let q_num = (self.n * self.head_size) as f64;
let k_num = (self.n_kv * self.head_size) as f64;
let v_num = (self.n_kv * self.head_size) as f64;
let mem_op = |bytes: f64| query_mem_op(db, bytes);
let mut extra = PerformanceResult::new(0.0, Source::Empirical);
if self.use_qk_norm {
let qk_norm = mem_op(q_num * 2.0)
.scaled(2.0)
.plus(mem_op(k_num * 2.0).scaled(2.0));
extra = extra.plus(qk_norm.scaled(2.0)); }
let apply_rope = mem_op(q_num * 2.0 + k_num * 2.0).scaled(2.0);
let kv_write = mem_op(k_num * self.fmha_quant_mode.mapping().memory)
.plus(mem_op(v_num * self.fmha_quant_mode.mapping().memory));
extra = extra.plus(apply_rope.plus(kv_write));
result = result.plus(extra.scaled(1.1));
if seq_imbalance_correction_scale != 1.0 {
result = result.scaled(seq_imbalance_correction_scale);
}
Ok(result.clamp_non_negative().scaled(self.scale_factor))
}
}
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
pub struct GenerationAttentionOp {
pub name: String,
pub scale_factor: f64,
pub n: u32,
pub n_kv: u32,
pub head_size: u32,
pub window_size: u32,
pub kv_cache_dtype: KvCacheQuantMode,
#[serde(default = "default_lane_order")]
pub lane_order: Vec<String>,
}
impl GenerationAttentionOp {
pub fn new(
name: impl Into<String>,
n: u32,
n_kv: u32,
head_size: u32,
kv_cache_dtype: KvCacheQuantMode,
) -> Self {
Self {
name: name.into(),
scale_factor: 1.0,
n,
n_kv,
head_size,
window_size: 0,
kv_cache_dtype,
lane_order: default_lane_order(),
}
}
pub fn query(
&self,
db: &PerfDatabase,
batch_size: u32,
kv_seq_tokens: u32,
gen_seq_imbalance_correction_scale: f64,
) -> Result<PerformanceResult, AicError> {
let mut result = query_generation_attention_table(
db,
&self.lane_order,
batch_size,
kv_seq_tokens,
self.n,
self.n_kv,
self.head_size,
self.window_size,
self.kv_cache_dtype,
)?;
if gen_seq_imbalance_correction_scale != 1.0 {
result = result.scaled(gen_seq_imbalance_correction_scale);
}
Ok(result.clamp_non_negative().scaled(self.scale_factor))
}
}
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
pub struct EncoderAttentionOp {
pub name: String,
pub scale_factor: f64,
pub n: u32,
pub head_size: u32,
pub fmha_quant_mode: FmhaQuantMode,
#[serde(default)]
pub partial_rotary_factor: f64,
}
impl EncoderAttentionOp {
pub fn new(
name: impl Into<String>,
n: u32,
head_size: u32,
fmha_quant_mode: FmhaQuantMode,
) -> Self {
Self {
name: name.into(),
scale_factor: 1.0,
n,
head_size,
fmha_quant_mode,
partial_rotary_factor: 0.0,
}
}
pub fn query(
&self,
db: &PerfDatabase,
batch_size: u32,
s: u32,
) -> Result<PerformanceResult, AicError> {
let mut result = query_encoder_attention_table(
db,
batch_size,
s,
self.n,
self.head_size,
self.fmha_quant_mode,
)?;
if self.partial_rotary_factor > 0.0 {
let qk_num = (self.n as u64) * (self.head_size as u64); let qk_bytes = 2 * (qk_num * 2) * (batch_size as u64) * (s as u64);
let apply_rope =
query_mem_op(db, qk_bytes as f64).scaled(self.partial_rotary_factor * 2.0);
result = result.plus(apply_rope.scaled(1.1));
}
Ok(result.clamp_non_negative().scaled(self.scale_factor))
}
}
#[allow(clippy::too_many_arguments)]
fn query_context_attention_table(
db: &PerfDatabase,
lane_order: &[String],
b: u32,
s: u32,
prefix: u32,
n: u32,
n_kv: u32,
head_size: u32,
window_size: u32,
kv_quant: KvCacheQuantMode,
fmha_quant: FmhaQuantMode,
) -> Result<PerformanceResult, AicError> {
let silicon = || -> Result<PerformanceResult, AicError> {
let full_s = s + prefix;
let value = db.attention.query_context(
lane_order,
b,
full_s,
n,
n_kv,
head_size,
window_size,
kv_quant,
fmha_quant,
)?;
let correction = prefix_correction(full_s, prefix);
Ok(PerformanceResult::with_energy(
value.latency * correction,
value.energy * correction,
Source::Silicon,
))
};
match db.database_mode {
DatabaseMode::Sol | DatabaseMode::SolFull => {
let attn_flops = quant_tc_flops(&db.system_spec, fmha_quant.mapping())?;
Ok(PerformanceResult::sol(context_attention_sol_with_prefix(
&db.system_spec,
b as f64,
s as f64,
prefix as f64,
n as f64,
n_kv as f64,
head_size,
window_size,
kv_quant,
attn_flops,
)))
}
DatabaseMode::Empirical => Ok(PerformanceResult::new(
context_attention_empirical(
db,
lane_order,
b,
s,
prefix,
n,
n_kv,
head_size,
window_size,
kv_quant,
fmha_quant,
)?,
Source::Empirical,
)),
DatabaseMode::Hybrid => match silicon() {
Ok(result) => Ok(result),
Err(err) if err.is_missing_perf_data() => Ok(PerformanceResult::new(
context_attention_empirical(
db,
lane_order,
b,
s,
prefix,
n,
n_kv,
head_size,
window_size,
kv_quant,
fmha_quant,
)?,
Source::Empirical,
)),
Err(err) => Err(err),
},
_ => silicon(),
}
}
fn lane_key(lane_order: &[String]) -> String {
lane_order.join(">")
}
#[allow(clippy::too_many_arguments)]
fn context_attention_empirical(
db: &PerfDatabase,
lane_order: &[String],
b: u32,
s: u32,
prefix: u32,
n: u32,
n_kv: u32,
head_size: u32,
window_size: u32,
kv_quant: KvCacheQuantMode,
fmha_quant: FmhaQuantMode,
) -> Result<f64, AicError> {
let spec = &db.system_spec;
let attn_flops = quant_tc_flops(spec, fmha_quant.mapping())?;
let sol_time = context_attention_sol_with_prefix_ms(
spec,
b as f64,
s as f64,
prefix as f64,
n as f64,
n_kv as f64,
head_size,
window_size,
kv_quant,
attn_flops,
);
let n_kv_lookup = if n == n_kv { 0 } else { n_kv };
let query = [n as f64, (s + prefix) as f64, b as f64];
let windows: Vec<u32> = if window_size > 0 {
vec![window_size, 0]
} else {
vec![window_size]
};
for &slice_window in &windows {
let key = format!(
"ctx_attn:{}:{}:{}:{}:{}:{}",
lane_key(lane_order),
fmha_quant.name(),
kv_quant.name(),
n_kv_lookup,
head_size,
slice_window
);
let grid = db.util_grids.get_or_try_build(&key, || {
match db.attention.context_points(
lane_order,
fmha_quant,
kv_quant,
n_kv_lookup,
head_size,
slice_window,
) {
Ok(points) => {
let sol = |c: &[f64]| {
context_attention_sol_ms(
spec,
n_kv_lookup,
head_size,
slice_window,
kv_quant,
c[0],
c[1],
c[2],
attn_flops,
)
};
Ok(Some(UtilGrid::new(util_empirical::build_samples(
points, sol,
))))
}
Err(err) if err.is_missing_perf_data() => Ok(None),
Err(err) => Err(err),
}
})?;
if grid.as_deref().is_some_and(|g| !g.is_empty()) {
let (latency, _) = util_empirical::estimate(sol_time, &query, grid.as_deref(), 1.0)?;
db.note_provenance(util_empirical::ProvenanceTier::Empirical);
return Ok(latency);
}
if db.transfer_policy.contains(TransferKind::XShape) {
if let Some((ref_grid, ref_hs)) = ctx_headsize_ref_grid(
db,
lane_order,
fmha_quant,
kv_quant,
n_kv_lookup,
head_size,
slice_window,
)? {
let scale = attn_prefill_hs_ratio(&db.backend, head_size)
/ attn_prefill_hs_ratio(&db.backend, ref_hs);
let (latency, _) =
util_empirical::estimate(sol_time, &query, Some(&ref_grid), scale)?;
db.note_provenance(util_empirical::ProvenanceTier::XShape);
return Ok(latency);
}
}
}
util_empirical::estimate(sol_time, &query, None, 1.0).map(|(latency, _)| latency)
}
#[allow(clippy::too_many_arguments)]
fn ctx_headsize_ref_grid(
db: &PerfDatabase,
lane_order: &[String],
fmha_quant: FmhaQuantMode,
kv_quant: KvCacheQuantMode,
n_kv_lookup: u32,
target_hs: u32,
window_size: u32,
) -> Result<Option<(std::sync::Arc<UtilGrid>, u32)>, AicError> {
let fallback_lanes = db.attention.context_lanes().unwrap_or_default();
let candidates = lane_order.iter().cloned().chain(
fallback_lanes
.into_iter()
.map(|(lane, _slices, _rows)| lane)
.filter(|lane| !lane_order.contains(lane)),
);
let mut chosen: Option<(String, u32)> = None;
for lane in candidates {
let head_sizes =
match db
.attention
.context_head_sizes(&lane, fmha_quant, kv_quant, n_kv_lookup)
{
Ok(sizes) => sizes,
Err(err) if err.is_missing_perf_data() => continue,
Err(err) => return Err(err),
};
let Some(ref_hs) = ref_head_size(&head_sizes, target_hs) else {
continue;
};
if db.attention.context_has_slice(
&lane,
fmha_quant,
kv_quant,
n_kv_lookup,
ref_hs,
window_size,
)? {
chosen = Some((lane, ref_hs));
break;
}
}
let Some((ref_lane, ref_hs)) = chosen else {
return Ok(None);
};
let spec = &db.system_spec;
let attn_flops = quant_tc_flops(spec, fmha_quant.mapping())?;
let key = format!(
"ctx_attn_xhs:{}:{}:{}:{}:{}:{}:xshape",
ref_lane,
fmha_quant.name(),
kv_quant.name(),
n_kv_lookup,
ref_hs,
window_size
);
let grid = db.util_grids.get_or_try_build(&key, || {
match db.attention.context_points(
std::slice::from_ref(&ref_lane),
fmha_quant,
kv_quant,
n_kv_lookup,
ref_hs,
window_size,
) {
Ok(points) => {
let sol = |c: &[f64]| {
context_attention_sol_ms(
spec,
n_kv_lookup,
ref_hs,
window_size,
kv_quant,
c[0],
c[1],
c[2],
attn_flops,
)
};
let mut grid = UtilGrid::new(util_empirical::build_samples(points, sol));
grid.reference_provenance = Some("xshape");
Ok(Some(grid))
}
Err(err) if err.is_missing_perf_data() => Ok(None),
Err(err) => Err(err),
}
})?;
Ok(grid.filter(|g| !g.is_empty()).map(|g| (g, ref_hs)))
}
#[allow(clippy::too_many_arguments)]
fn query_generation_attention_table(
db: &PerfDatabase,
lane_order: &[String],
b: u32,
s: u32,
n: u32,
n_kv: u32,
head_size: u32,
window_size: u32,
kv_quant: KvCacheQuantMode,
) -> Result<PerformanceResult, AicError> {
let silicon = |v: crate::perf_database::perf_interp::LeafValue| {
PerformanceResult::with_energy(v.latency, v.energy, Source::Silicon)
};
match db.database_mode {
DatabaseMode::Sol | DatabaseMode::SolFull => {
let attn_flops = generation_attn_flops(&db.system_spec, kv_quant)?;
Ok(PerformanceResult::sol(generation_attention_sol(
&db.system_spec,
n_kv,
head_size,
window_size,
kv_quant,
n as f64,
b as f64,
s as f64,
attn_flops,
)))
}
DatabaseMode::Empirical => Ok(PerformanceResult::new(
generation_attention_empirical(
db,
lane_order,
b,
s,
n,
n_kv,
head_size,
window_size,
kv_quant,
)?,
Source::Empirical,
)),
DatabaseMode::Hybrid => {
match db.attention.query_generation(
lane_order,
b,
s,
n,
n_kv,
head_size,
window_size,
kv_quant,
) {
Ok(value) => Ok(silicon(value)),
Err(err) if err.is_missing_perf_data() => Ok(PerformanceResult::new(
generation_attention_empirical(
db,
lane_order,
b,
s,
n,
n_kv,
head_size,
window_size,
kv_quant,
)?,
Source::Empirical,
)),
Err(err) => Err(err),
}
}
_ => Ok(silicon(db.attention.query_generation(
lane_order,
b,
s,
n,
n_kv,
head_size,
window_size,
kv_quant,
)?)),
}
}
#[allow(clippy::too_many_arguments)]
fn generation_attention_empirical(
db: &PerfDatabase,
lane_order: &[String],
b: u32,
s: u32,
n: u32,
n_kv: u32,
head_size: u32,
window_size: u32,
kv_quant: KvCacheQuantMode,
) -> Result<f64, AicError> {
let spec = &db.system_spec;
let n_kv_lookup = if n_kv == n { 0 } else { n_kv };
let attn_flops = generation_attn_flops(spec, kv_quant)?;
let sol_time = generation_attention_sol_ms(
spec,
n_kv_lookup,
head_size,
window_size,
kv_quant,
n as f64,
b as f64,
s as f64,
attn_flops,
);
let query = [n as f64, b as f64, s as f64];
let windows: Vec<u32> = if window_size > 0 {
vec![window_size, 0]
} else {
vec![window_size]
};
for &slice_window in &windows {
let key = format!(
"gen_attn:{}:{}:{}:{}:{}",
lane_key(lane_order),
kv_quant.name(),
n_kv_lookup,
head_size,
slice_window
);
let grid = db.util_grids.get_or_try_build(&key, || {
match db.attention.generation_points(
lane_order,
kv_quant,
n_kv_lookup,
head_size,
slice_window,
) {
Ok(points) => {
let sol = |c: &[f64]| {
generation_attention_sol_ms(
spec,
n_kv_lookup,
head_size,
slice_window,
kv_quant,
c[0],
c[1],
c[2],
attn_flops,
)
};
Ok(Some(UtilGrid::new(util_empirical::build_samples(
points, sol,
))))
}
Err(err) if err.is_missing_perf_data() => Ok(None),
Err(err) => Err(err),
}
})?;
if grid.as_deref().is_some_and(|g| !g.is_empty()) {
let (latency, _) = util_empirical::estimate(sol_time, &query, grid.as_deref(), 1.0)?;
db.note_provenance(util_empirical::ProvenanceTier::Empirical);
return Ok(latency);
}
if db.transfer_policy.contains(TransferKind::XShape) {
if let Some((ref_grid, _ref_hs)) = gen_headsize_ref_grid(
db,
lane_order,
kv_quant,
n_kv_lookup,
head_size,
slice_window,
)? {
let (latency, _) =
util_empirical::estimate(sol_time, &query, Some(&ref_grid), 1.0)?;
db.note_provenance(util_empirical::ProvenanceTier::XShape);
return Ok(latency);
}
}
}
util_empirical::estimate(sol_time, &query, None, 1.0).map(|(latency, _)| latency)
}
fn gen_headsize_ref_grid(
db: &PerfDatabase,
lane_order: &[String],
kv_quant: KvCacheQuantMode,
n_kv_lookup: u32,
target_hs: u32,
window_size: u32,
) -> Result<Option<(std::sync::Arc<UtilGrid>, u32)>, AicError> {
let fallback_lanes = db.attention.generation_lanes().unwrap_or_default();
let candidates = lane_order.iter().cloned().chain(
fallback_lanes
.into_iter()
.map(|(lane, _slices, _rows)| lane)
.filter(|lane| !lane_order.contains(lane)),
);
let mut chosen: Option<(String, u32)> = None;
for lane in candidates {
let head_sizes = match db
.attention
.generation_head_sizes(&lane, kv_quant, n_kv_lookup)
{
Ok(sizes) => sizes,
Err(err) if err.is_missing_perf_data() => continue,
Err(err) => return Err(err),
};
let Some(ref_hs) = ref_head_size(&head_sizes, target_hs) else {
continue;
};
if db
.attention
.generation_has_slice(&lane, kv_quant, n_kv_lookup, ref_hs, window_size)?
{
chosen = Some((lane, ref_hs));
break;
}
}
let Some((ref_lane, ref_hs)) = chosen else {
return Ok(None);
};
let spec = &db.system_spec;
let attn_flops = generation_attn_flops(spec, kv_quant)?;
let key = format!(
"gen_attn_xhs:{}:{}:{}:{}:{}:xshape",
ref_lane,
kv_quant.name(),
n_kv_lookup,
ref_hs,
window_size
);
let grid = db.util_grids.get_or_try_build(&key, || {
match db.attention.generation_points(
std::slice::from_ref(&ref_lane),
kv_quant,
n_kv_lookup,
ref_hs,
window_size,
) {
Ok(points) => {
let sol = |c: &[f64]| {
generation_attention_sol_ms(
spec,
n_kv_lookup,
ref_hs,
window_size,
kv_quant,
c[0],
c[1],
c[2],
attn_flops,
)
};
let mut grid = UtilGrid::new(util_empirical::build_samples(points, sol));
grid.reference_provenance = Some("xshape");
Ok(Some(grid))
}
Err(err) if err.is_missing_perf_data() => Ok(None),
Err(err) => Err(err),
}
})?;
Ok(grid.filter(|g| !g.is_empty()).map(|g| (g, ref_hs)))
}
fn query_encoder_attention_table(
db: &PerfDatabase,
b: u32,
s: u32,
n: u32,
head_size: u32,
fmha_quant: FmhaQuantMode,
) -> Result<PerformanceResult, AicError> {
let silicon = |v: crate::perf_database::perf_interp::LeafValue| {
PerformanceResult::with_energy(v.latency, v.energy, Source::Silicon)
};
match db.database_mode {
DatabaseMode::Sol | DatabaseMode::SolFull => {
let attn_flops = quant_tc_flops(&db.system_spec, fmha_quant.mapping())?;
Ok(PerformanceResult::sol(encoder_attention_sol(
&db.system_spec,
head_size,
n as f64,
s as f64,
b as f64,
attn_flops,
)))
}
DatabaseMode::Empirical => Ok(PerformanceResult::new(
encoder_attention_empirical(db, b, s, n, head_size, fmha_quant)?,
Source::Empirical,
)),
DatabaseMode::Hybrid => match db.attention.query_encoder(b, s, n, head_size, fmha_quant) {
Ok(value) => Ok(silicon(value)),
Err(err) if err.is_missing_perf_data() => Ok(PerformanceResult::new(
encoder_attention_empirical(db, b, s, n, head_size, fmha_quant)?,
Source::Empirical,
)),
Err(err) => Err(err),
},
_ => Ok(silicon(
db.attention.query_encoder(b, s, n, head_size, fmha_quant)?,
)),
}
}
fn encoder_attention_empirical(
db: &PerfDatabase,
b: u32,
s: u32,
n: u32,
head_size: u32,
fmha_quant: FmhaQuantMode,
) -> Result<f64, AicError> {
let spec = &db.system_spec;
let attn_flops = quant_tc_flops(spec, fmha_quant.mapping())?;
let sol = |c: &[f64]| encoder_attention_sol_ms(spec, head_size, c[0], c[1], c[2], attn_flops);
let query = [n as f64, s as f64, b as f64];
let key = format!("encoder_attn:{}:{}", fmha_quant.name(), head_size);
let grid = db.util_grids.get_or_try_build(&key, || {
match db.attention.encoder_points(fmha_quant, head_size) {
Ok(points) => Ok(Some(UtilGrid::new(util_empirical::build_samples(
points, sol,
)))),
Err(err) if err.is_missing_perf_data() => Ok(None),
Err(err) => Err(err),
}
})?;
let (latency, _) = util_empirical::estimate(sol(&query), &query, grid.as_deref(), 1.0)?;
db.note_provenance(util_empirical::ProvenanceTier::Empirical);
Ok(latency)
}
#[cfg(test)]
pub(crate) fn b200_vllm_context_lane_order() -> Vec<String> {
[
"vllm_flashinfer_trtllmprefill",
"vllm_flashinfer_trtllmdecode",
"vllm_triton_attn",
"fa3",
"fla",
"flashinfer",
"triton",
"trtllm_mha",
"default",
"vllm_flashinfer",
]
.iter()
.map(|lane| lane.to_string())
.collect()
}
#[cfg(test)]
pub(crate) fn b200_vllm_generation_lane_order() -> Vec<String> {
[
"vllm_flashinfer_trtllmdecode",
"vllm_triton_attn",
"fa3",
"fla",
"flashinfer",
"triton",
"trtllm_mha",
"default",
"vllm_flashinfer",
]
.iter()
.map(|lane| lane.to_string())
.collect()
}
#[cfg(test)]
mod tests {
use super::*;
use std::path::PathBuf;
const REPO_ROOT_HINT: &str = env!("CARGO_MANIFEST_DIR");
fn b200_vllm_db() -> PerfDatabase {
let systems_root = PathBuf::from(REPO_ROOT_HINT)
.join("../..")
.join("python/aisimulate/src/aiconfigurator_core/systems");
PerfDatabase::load(&systems_root, "b200_sxm", "vllm", "0.24.0").expect("db must load")
}
fn vllm_context_lanes() -> Vec<String> {
b200_vllm_context_lane_order()
}
fn vllm_generation_lanes() -> Vec<String> {
b200_vllm_generation_lane_order()
}
fn with_vllm_lanes_ctx(mut op: ContextAttentionOp) -> ContextAttentionOp {
op.lane_order = vllm_context_lanes();
op
}
fn with_vllm_lanes_gen(mut op: GenerationAttentionOp) -> GenerationAttentionOp {
op.lane_order = vllm_generation_lanes();
op
}
#[test]
fn context_attention_smoke() {
let db = b200_vllm_db();
let op = with_vllm_lanes_ctx(ContextAttentionOp::new(
"ctx",
64,
1,
128,
KvCacheQuantMode::Fp8,
FmhaQuantMode::Bfloat16,
));
let result = op
.query(&db, 8, 16384, 0, 1.0)
.expect("context attention query must succeed");
assert!(result.latency_ms > 19.0 && result.latency_ms < 30.0);
assert_eq!(result.source, Source::Mixed);
}
#[test]
fn context_attention_prefix_correction_shrinks_latency() {
let db = b200_vllm_db();
let op = with_vllm_lanes_ctx(ContextAttentionOp::new(
"ctx",
64,
1,
128,
KvCacheQuantMode::Fp8,
FmhaQuantMode::Bfloat16,
));
let with_prefix = op
.query(&db, 8, 8192, 8192, 1.0)
.expect("query must succeed")
.latency_ms;
let no_prefix = op
.query(&db, 8, 16384, 0, 1.0)
.expect("query must succeed")
.latency_ms;
assert!(
with_prefix < no_prefix,
"prefix correction must shrink latency: {with_prefix} vs {no_prefix}"
);
}
#[test]
fn generation_attention_smoke() {
let db = b200_vllm_db();
let op = with_vllm_lanes_gen(GenerationAttentionOp::new(
"gen",
64,
4,
128,
KvCacheQuantMode::Fp8,
));
let result = op
.query(&db, 32, 2, 1.0)
.expect("gen attention query must succeed");
assert!(
result.latency_ms.is_finite() && result.latency_ms > 0.0,
"expected positive 5-sample-averaged gen latency, got {}",
result.latency_ms
);
}
#[test]
fn mem_op_latency_uses_empirical_formula() {
let db = b200_vllm_db();
let spec = &db.system_spec;
let latency = mem_op_latency_ms(spec, 1_000_000.0);
let expected = (1_000_000.0_f64
/ (spec.gpu.mem_bw * spec.gpu.mem_bw_empirical_scaling_factor)
+ spec.gpu.mem_empirical_constant_latency)
* 1000.0;
assert!((latency - expected).abs() < 1e-12);
}
#[test]
fn context_attention_empirical_regime_routing() {
let mut db = b200_vllm_db();
db.database_mode = crate::common::enums::DatabaseMode::Empirical;
type Tier = crate::operators::util_empirical::ProvenanceTier;
let cases: &[(u32, u32, u32, u32, u32, u32, u32, KvCacheQuantMode, Tier)] = &[
(
7,
3000,
0,
64,
1,
128,
0,
KvCacheQuantMode::Fp8,
Tier::Empirical,
),
(
8,
16384,
0,
64,
1,
128,
0,
KvCacheQuantMode::Fp8,
Tier::Empirical,
),
(
4,
8192,
8192,
64,
1,
128,
0,
KvCacheQuantMode::Fp8,
Tier::Empirical,
),
(
4,
4096,
0,
48,
8,
192,
0,
KvCacheQuantMode::Fp8,
Tier::XShape,
),
(
2,
10000,
0,
32,
1,
128,
8192,
KvCacheQuantMode::Bfloat16,
Tier::Empirical,
),
(
2,
10000,
0,
32,
1,
128,
4096,
KvCacheQuantMode::Bfloat16,
Tier::Empirical,
),
];
for &(b, s, p, n, nk, hs, w, kv, tier) in cases {
db.reset_provenance();
let result = query_context_attention_table(
&db,
&vllm_context_lanes(),
b,
s,
p,
n,
nk,
hs,
w,
kv,
FmhaQuantMode::Bfloat16,
)
.expect("empirical query");
assert!(result.latency_ms.is_finite() && result.latency_ms > 0.0);
assert_eq!(
result.source,
Source::Empirical,
"(b={b}, s={s}, hs={hs}, w={w})"
);
assert_eq!(
db.worst_provenance(),
tier,
"(b={b}, s={s}, hs={hs}, w={w})"
);
}
}
#[test]
fn generation_attention_empirical_regime_routing() {
let mut db = b200_vllm_db();
db.database_mode = crate::common::enums::DatabaseMode::Empirical;
type Tier = crate::operators::util_empirical::ProvenanceTier;
let cases: &[(u32, u32, u32, u32, u32, u32, KvCacheQuantMode, Tier)] = &[
(
48,
7777,
64,
8,
128,
0,
KvCacheQuantMode::Fp8,
Tier::Empirical,
),
(32, 2, 64, 4, 128, 0, KvCacheQuantMode::Fp8, Tier::Empirical),
(16, 4096, 48, 8, 192, 0, KvCacheQuantMode::Fp8, Tier::XShape),
(
8,
12000,
32,
1,
128,
8192,
KvCacheQuantMode::Bfloat16,
Tier::Empirical,
),
(
8,
12000,
32,
1,
128,
2048,
KvCacheQuantMode::Bfloat16,
Tier::Empirical,
),
];
for &(b, s, n, nk, hs, w, kv, tier) in cases {
db.reset_provenance();
let result = query_generation_attention_table(
&db,
&vllm_generation_lanes(),
b,
s,
n,
nk,
hs,
w,
kv,
)
.expect("empirical query");
assert!(result.latency_ms.is_finite() && result.latency_ms > 0.0);
assert_eq!(
result.source,
Source::Empirical,
"(b={b}, s={s}, hs={hs}, w={w})"
);
assert_eq!(
db.worst_provenance(),
tier,
"(b={b}, s={s}, hs={hs}, w={w})"
);
}
}
#[test]
fn encoder_attention_empirical_and_hybrid_match_python_oracles() {
let mut db = b200_vllm_db();
db.database_mode = crate::common::enums::DatabaseMode::Empirical;
let empirical = query_encoder_attention_table(&db, 3, 900, 16, 64, FmhaQuantMode::Bfloat16)
.expect("empirical query");
assert_eq!(empirical.source, Source::Empirical);
assert!(empirical.latency_ms.is_finite() && empirical.latency_ms > 0.0);
db.database_mode = crate::common::enums::DatabaseMode::Hybrid;
let hybrid = query_encoder_attention_table(&db, 3, 900, 16, 64, FmhaQuantMode::Bfloat16)
.expect("hybrid query");
assert_eq!(hybrid.source, Source::Silicon);
assert!(
(hybrid.latency_ms - empirical.latency_ms).abs() > 1e-12,
"hybrid must resolve on silicon, not replay the empirical estimate"
);
}
#[test]
fn context_attention_hybrid_dispatch_matches_python() {
let mut emp_db = b200_vllm_db();
emp_db.database_mode = crate::common::enums::DatabaseMode::Empirical;
let empirical_192 = query_context_attention_table(
&emp_db,
&vllm_context_lanes(),
4,
4096,
0,
48,
8,
192,
0,
KvCacheQuantMode::Fp8,
FmhaQuantMode::Bfloat16,
)
.expect("empirical query");
let sil_db = b200_vllm_db();
let silicon_hit = query_context_attention_table(
&sil_db,
&vllm_context_lanes(),
8,
16384,
0,
64,
1,
128,
0,
KvCacheQuantMode::Fp8,
FmhaQuantMode::Bfloat16,
)
.expect("silicon query");
let mut db = b200_vllm_db();
db.database_mode = crate::common::enums::DatabaseMode::Hybrid;
let result = query_context_attention_table(
&db,
&vllm_context_lanes(),
4,
4096,
0,
48,
8,
192,
0,
KvCacheQuantMode::Fp8,
FmhaQuantMode::Bfloat16,
)
.expect("hybrid query");
assert!(
(result.latency_ms - empirical_192.latency_ms).abs() < 1e-12,
"hybrid on the uncollected head size must replay the xshape estimate"
);
assert_eq!(result.source, Source::Empirical);
let result = query_context_attention_table(
&db,
&vllm_context_lanes(),
8,
16384,
0,
64,
1,
128,
0,
KvCacheQuantMode::Fp8,
FmhaQuantMode::Bfloat16,
)
.expect("hybrid query");
assert!(
(result.latency_ms - silicon_hit.latency_ms).abs() < 1e-12,
"hybrid on a collected slice must resolve on silicon"
);
assert_eq!(result.source, Source::Silicon);
}
#[test]
fn attention_xshape_disabled_raises_empirical_not_implemented() {
let mut db = b200_vllm_db();
db.database_mode = crate::common::enums::DatabaseMode::Empirical;
db.transfer_policy = crate::common::enums::TransferPolicy::OFF;
let ctx = query_context_attention_table(
&db,
&vllm_context_lanes(),
4,
4096,
0,
48,
8,
192,
0,
KvCacheQuantMode::Fp8,
FmhaQuantMode::Bfloat16,
);
assert!(
matches!(ctx, Err(AicError::EmpiricalNotImplemented(_))),
"got {ctx:?}"
);
let generation = query_generation_attention_table(
&db,
&vllm_generation_lanes(),
16,
4096,
48,
8,
192,
0,
KvCacheQuantMode::Fp8,
);
assert!(
matches!(generation, Err(AicError::EmpiricalNotImplemented(_))),
"got {generation:?}"
);
}
#[test]
fn context_attention_prefix_correction_scales_energy_matches_python_oracle() {
use crate::perf_database::energy_test_fixtures::{
Col, write_energy_systems_root, write_parquet,
};
let tmp = tempfile::tempdir().expect("tmpdir");
let data = write_energy_systems_root(tmp.path());
write_parquet(
&data.join("context_attention_perf.parquet"),
&[
Col::Str("attn_dtype", vec!["bfloat16", "bfloat16"]),
Col::Str("kv_cache_dtype", vec!["bfloat16", "bfloat16"]),
Col::I64("batch_size", vec![2, 2]),
Col::I64("isl", vec![1024, 2048]),
Col::I64("num_heads", vec![16, 16]),
Col::I64("num_key_value_heads", vec![16, 16]),
Col::I64("head_dim", vec![128, 128]),
Col::I64("step", vec![0, 0]),
Col::F64("latency", vec![1.0, 3.0]),
Col::F64("power", vec![100.0, 200.0]),
],
);
let db = PerfDatabase::load(tmp.path(), "testsys", "vllm", "1.0").expect("db must load");
let r = query_context_attention_table(
&db,
&default_lane_order(),
2,
512,
1024,
16,
16,
128,
0,
KvCacheQuantMode::Bfloat16,
FmhaQuantMode::Bfloat16,
)
.expect("silicon query");
assert!(
((r.latency_ms - 1.0366807798802438) / 1.0366807798802438).abs() < 1e-9,
"latency {}",
r.latency_ms
);
assert!(
((r.energy_wms - 155.50211698203657) / 155.50211698203657).abs() < 1e-9,
"energy {}",
r.energy_wms
);
assert_eq!(r.source, Source::Silicon);
}
#[test]
fn attention_sol_mode_returns_roofline_with_sol_source() {
let mut db = b200_vllm_db();
db.database_mode = DatabaseMode::Sol;
let spec = db.system_spec.clone();
let mem_op = query_mem_op(&db, 1_000_000.0);
assert_eq!(mem_op.latency_ms, 1_000_000.0 / spec.gpu.mem_bw * 1000.0);
assert_eq!(mem_op.source, Source::Sol);
assert!(mem_op.latency_ms < mem_op_latency_ms(&spec, 1_000_000.0));
let ctx = ContextAttentionOp::new(
"ctx",
64,
8,
128,
KvCacheQuantMode::Fp8,
FmhaQuantMode::Bfloat16,
);
let result = ctx.query(&db, 4, 2048, 256, 1.0).expect("ctx sol");
let attn_flops = quant_tc_flops(&spec, FmhaQuantMode::Bfloat16.mapping()).unwrap();
let table = context_attention_sol_with_prefix_ms(
&spec,
4.0,
2048.0,
256.0,
64.0,
8.0,
128,
0,
KvCacheQuantMode::Fp8,
attn_flops,
);
let sol_mem_op = |bytes: f64| bytes / spec.gpu.mem_bw * 1000.0;
let q_num = (64 * 128) as f64;
let k_num = (8 * 128) as f64;
let fmha_mem = FmhaQuantMode::Bfloat16.mapping().memory;
let extras = 2.0 * sol_mem_op(q_num * 2.0 + k_num * 2.0)
+ sol_mem_op(k_num * fmha_mem)
+ sol_mem_op(k_num * fmha_mem);
assert!((result.latency_ms - (table + extras * 1.1)).abs() < 1e-12);
assert_eq!(result.source, Source::Sol);
assert_eq!(result.energy_wms, 0.0);
let generation = GenerationAttentionOp::new("gen", 64, 8, 128, KvCacheQuantMode::Fp8);
let result = generation.query(&db, 8, 4096, 1.0).expect("gen sol");
let gen_flops = generation_attn_flops(&spec, KvCacheQuantMode::Fp8).unwrap();
let expected = generation_attention_sol_ms(
&spec,
8,
128,
0,
KvCacheQuantMode::Fp8,
64.0,
8.0,
4096.0,
gen_flops,
);
assert_eq!(result.latency_ms, expected);
assert_eq!(result.source, Source::Sol);
let enc = EncoderAttentionOp::new("enc", 16, 72, FmhaQuantMode::Bfloat16);
let result = enc.query(&db, 2, 64).expect("enc sol");
let enc_flops = quant_tc_flops(&spec, FmhaQuantMode::Bfloat16.mapping()).unwrap();
let expected = encoder_attention_sol_ms(&spec, 72, 16.0, 64.0, 2.0, enc_flops);
assert_eq!(result.latency_ms, expected);
assert_eq!(result.source, Source::Sol);
}
#[test]
fn context_attention_silicon_merges_extras_provenance_into_mixed() {
let db = b200_vllm_db();
let op = with_vllm_lanes_ctx(ContextAttentionOp::new(
"ctx",
64,
8,
128,
KvCacheQuantMode::Fp8,
FmhaQuantMode::Bfloat16,
));
let result = op.query(&db, 4, 2048, 256, 1.0).expect("ctx silicon");
let table = query_context_attention_table(
&db,
&vllm_context_lanes(),
4,
2048,
256,
64,
8,
128,
0,
KvCacheQuantMode::Fp8,
FmhaQuantMode::Bfloat16,
)
.expect("table silicon");
let q_num = (64 * 128) as f64;
let k_num = (8 * 128) as f64;
let fmha_mem = FmhaQuantMode::Bfloat16.mapping().memory;
let mem_op = |bytes: f64| query_mem_op(&db, bytes).latency_ms;
let extras = 2.0 * mem_op(q_num * 2.0 + k_num * 2.0)
+ (mem_op(k_num * fmha_mem) + mem_op(k_num * fmha_mem));
assert_eq!(result.latency_ms, table.latency_ms + extras * 1.1);
assert_eq!(result.energy_wms, table.energy_wms);
assert_eq!(result.source, Source::Mixed);
}
fn b200_sglang_0514_db() -> PerfDatabase {
let systems_root = PathBuf::from(REPO_ROOT_HINT)
.join("../..")
.join("python/aisimulate/src/aiconfigurator_core/systems");
PerfDatabase::load(&systems_root, "b200_sxm", "sglang", "0.5.14").expect("db must load")
}
fn lane_vec(names: &[&str]) -> Vec<String> {
names.iter().map(|n| n.to_string()).collect()
}
fn sglang_default_lanes() -> Vec<String> {
lane_vec(&[
"triton",
"trtllm_mha",
"flashinfer",
"fa3",
"fla",
"default",
])
}
fn sglang_flashinfer_lanes() -> Vec<String> {
lane_vec(&[
"flashinfer",
"triton",
"trtllm_mha",
"fa3",
"fla",
"default",
])
}
#[test]
fn context_attention_empirical_lane_selection_routes_to_the_serving_lane() {
let mut db = b200_sglang_0514_db();
db.database_mode = crate::common::enums::DatabaseMode::Empirical;
type Tier = crate::operators::util_empirical::ProvenanceTier;
let query = |order: &[String], b, s, hs| {
db.reset_provenance();
let result = query_context_attention_table(
&db,
order,
b,
s,
0,
64,
8,
hs,
0,
KvCacheQuantMode::Bfloat16,
FmhaQuantMode::Bfloat16,
)
.expect("empirical query");
(result, db.worst_provenance())
};
for (order, direct, hs, tier) in [
(
sglang_default_lanes(),
lane_vec(&["trtllm_mha"]),
128,
Tier::Empirical,
),
(
sglang_flashinfer_lanes(),
lane_vec(&["flashinfer"]),
128,
Tier::Empirical,
),
] {
let (resolved, resolved_tier) = query(&order, 4, 4096, hs);
let (direct, direct_tier) = query(&direct, 4, 4096, hs);
assert_eq!(resolved.latency_ms, direct.latency_ms);
assert!(resolved.latency_ms.is_finite() && resolved.latency_ms > 0.0);
assert_eq!(resolved.source, Source::Empirical);
assert_eq!(direct.source, Source::Empirical);
assert_eq!(resolved_tier, tier);
assert_eq!(direct_tier, tier);
}
let (default, default_tier) = query(&sglang_default_lanes(), 4, 4096, 80);
let (override_lane, override_tier) = query(&sglang_flashinfer_lanes(), 4, 4096, 80);
assert!(default.latency_ms.is_finite() && default.latency_ms > 0.0);
assert!(override_lane.latency_ms.is_finite() && override_lane.latency_ms > 0.0);
assert_eq!(default.source, Source::Empirical);
assert_eq!(override_lane.source, Source::Empirical);
assert_eq!(default_tier, Tier::XShape);
assert_eq!(override_tier, Tier::XShape);
assert_ne!(default.latency_ms, override_lane.latency_ms);
}
#[test]
fn generation_attention_empirical_lane_selection_routes_to_the_serving_lane() {
let mut db = b200_sglang_0514_db();
db.database_mode = crate::common::enums::DatabaseMode::Empirical;
type Tier = crate::operators::util_empirical::ProvenanceTier;
let query = |order: &[String], hs| {
db.reset_provenance();
let result = query_generation_attention_table(
&db,
order,
8,
4096,
64,
8,
hs,
0,
KvCacheQuantMode::Bfloat16,
)
.expect("empirical query");
(result, db.worst_provenance())
};
for (order, direct, hs, tier) in [
(
sglang_default_lanes(),
lane_vec(&["trtllm_mha"]),
128,
Tier::Empirical,
),
(
sglang_flashinfer_lanes(),
lane_vec(&["flashinfer"]),
128,
Tier::Empirical,
),
(
sglang_default_lanes(),
lane_vec(&["trtllm_mha"]),
80,
Tier::XShape,
),
(
sglang_flashinfer_lanes(),
lane_vec(&["flashinfer"]),
80,
Tier::XShape,
),
] {
let (resolved, resolved_tier) = query(&order, hs);
let (direct, direct_tier) = query(&direct, hs);
assert_eq!(resolved.latency_ms, direct.latency_ms);
assert!(resolved.latency_ms.is_finite() && resolved.latency_ms > 0.0);
assert_eq!(resolved.source, Source::Empirical);
assert_eq!(direct.source, Source::Empirical);
assert_eq!(resolved_tier, tier);
assert_eq!(direct_tier, tier);
}
let (default, _) = query(&sglang_default_lanes(), 80);
let (override_lane, _) = query(&sglang_flashinfer_lanes(), 80);
assert_ne!(default.latency_ms, override_lane.latency_ms);
}
#[test]
fn attention_ops_carry_lane_order_into_the_query() {
let db = b200_sglang_0514_db();
let mut ctx = ContextAttentionOp::new(
"ctx",
64,
8,
128,
KvCacheQuantMode::Bfloat16,
FmhaQuantMode::Bfloat16,
);
ctx.lane_order = sglang_default_lanes();
let default = ctx.query(&db, 4, 4096, 0, 1.0).expect("query");
let default_table = db
.attention
.query_context(
&ctx.lane_order,
4,
4096,
64,
8,
128,
0,
KvCacheQuantMode::Bfloat16,
FmhaQuantMode::Bfloat16,
)
.expect("default table query")
.latency;
ctx.lane_order = sglang_flashinfer_lanes();
let flashinfer = ctx.query(&db, 4, 4096, 0, 1.0).expect("query");
let flashinfer_table = db
.attention
.query_context(
&ctx.lane_order,
4,
4096,
64,
8,
128,
0,
KvCacheQuantMode::Bfloat16,
FmhaQuantMode::Bfloat16,
)
.expect("override table query")
.latency;
assert!(default.latency_ms.is_finite() && default.latency_ms > 0.0);
assert!(flashinfer.latency_ms.is_finite() && flashinfer.latency_ms > 0.0);
assert_eq!(default.source, Source::Mixed);
assert_eq!(flashinfer.source, Source::Mixed);
assert!(
(flashinfer.latency_ms - default.latency_ms - (flashinfer_table - default_table)).abs()
< 1e-12,
"lane order must reach the table: {} vs {}",
default.latency_ms,
flashinfer.latency_ms
);
assert_ne!(default.latency_ms, flashinfer.latency_ms);
let mut generation =
GenerationAttentionOp::new("gen", 64, 8, 128, KvCacheQuantMode::Bfloat16);
generation.lane_order = sglang_default_lanes();
let default = generation.query(&db, 8, 4096, 1.0).expect("query");
let default_table = db
.attention
.query_generation(
&generation.lane_order,
8,
4096,
64,
8,
128,
0,
KvCacheQuantMode::Bfloat16,
)
.expect("default table query")
.latency;
generation.lane_order = sglang_flashinfer_lanes();
let flashinfer = generation.query(&db, 8, 4096, 1.0).expect("query");
let flashinfer_table = db
.attention
.query_generation(
&generation.lane_order,
8,
4096,
64,
8,
128,
0,
KvCacheQuantMode::Bfloat16,
)
.expect("override table query")
.latency;
assert!(default.latency_ms.is_finite() && default.latency_ms > 0.0);
assert!(flashinfer.latency_ms.is_finite() && flashinfer.latency_ms > 0.0);
assert_eq!(default.source, Source::Silicon);
assert_eq!(flashinfer.source, Source::Silicon);
assert!(
(flashinfer.latency_ms - default.latency_ms - (flashinfer_table - default_table)).abs()
< 1e-12,
"lane order must reach the table: {} vs {}",
default.latency_ms,
flashinfer.latency_ms
);
assert_ne!(default.latency_ms, flashinfer.latency_ms);
}
}