use crate::backend::cpu_features::{CoreTopology, env_disabled, env_usize, physical_core_count};
use std::sync::OnceLock;
const DECODE_MAX_AUTO: usize = 12;
const BPD_THRESHOLD_KB_DEFAULT: usize = 2500;
#[derive(Debug, Clone, Copy)]
pub struct DecodeShape {
pub weight_bytes: u64,
pub dispatches_per_token: usize,
}
impl DecodeShape {
pub fn bytes_per_dispatch(&self) -> u64 {
self.weight_bytes / self.dispatches_per_token.max(1) as u64
}
pub fn from_gguf(gguf: &crate::gguf::GgufFile) -> Option<Self> {
let block_count = gguf
.architecture()
.and_then(|arch| gguf.get_u32(&format!("{arch}.block_count")))
.unwrap_or(0) as usize;
Self::from_tensors(
gguf.tensors.iter().map(|(name, info)| {
(
name.as_str(),
info.shape.get(1).copied().unwrap_or(0),
info.size_bytes as u64,
)
}),
block_count,
super::cpu::gemv_par_threshold(),
)
}
fn from_tensors<'a>(
tensors: impl Iterator<Item = (&'a str, usize, u64)>,
block_count: usize,
par_threshold: usize,
) -> Option<Self> {
let mut weight_bytes: u64 = 0;
let mut dispatches = 0usize;
let mut has_output_head = false;
for (name, rows, bytes) in tensors {
weight_bytes = weight_bytes.saturating_add(bytes);
if name == "token_embd.weight" {
continue;
}
if name == "output.weight" {
has_output_head = true;
}
if rows >= par_threshold {
dispatches += 1;
}
}
if !has_output_head {
dispatches += 1;
}
dispatches = dispatches.saturating_add(block_count);
(weight_bytes > 0 && dispatches > 0).then_some(Self {
weight_bytes,
dispatches_per_token: dispatches,
})
}
}
static DECODE_SHAPE: OnceLock<DecodeShape> = OnceLock::new();
pub fn set_decode_shape(shape: DecodeShape) {
let _ = DECODE_SHAPE.set(shape);
}
fn decode_shape() -> Option<DecodeShape> {
DECODE_SHAPE.get().copied()
}
fn sizing_enabled() -> bool {
!env_disabled("CERA_DECODE_SIZING")
}
pub fn decode_thread_count(topo: &CoreTopology) -> usize {
let max_t = topo.perf_core_count.max(1);
if let Ok(v) = std::env::var("CERA_DECODE_THREADS") {
let v = v.trim();
if !v.eq_ignore_ascii_case("auto")
&& let Ok(n) = v.parse::<usize>()
&& n >= 1
{
if n > max_t {
tracing::warn!(
"cera: CERA_DECODE_THREADS={n} exceeds the {max_t} detected \
performance cores; clamping to {max_t} (set CERA_THREADS to \
raise the detected count, though that is itself clamped to \
the pinnable cores)"
);
}
return n.min(max_t);
}
}
if sizing_enabled()
&& let Some(shape) = decode_shape()
&& let Some(n) = width_for_host(topo, shape, env_overrides(), physical_core_count())
{
tracing::debug!(
"cera: decode width {n} of {max_t} (weights {} MB, {} dispatches/token, \
{} KB/dispatch)",
shape.weight_bytes / 1_000_000,
shape.dispatches_per_token,
shape.bytes_per_dispatch() / 1000,
);
return n;
}
max_t.min(DECODE_MAX_AUTO)
}
pub fn prefill_thread_count(topo: &CoreTopology) -> usize {
let default = topo.perf_core_count.max(1);
let Some(n) = env_usize("CERA_PREFILL_THREADS") else {
return default;
};
if topo.pin_cores.is_empty() || !super::threadpool::pinning_enabled() {
return n;
}
let ceiling = topo.pin_cores.len();
if n > ceiling {
tracing::warn!(
"cera: CERA_PREFILL_THREADS={n} exceeds the {ceiling} pinnable cores; \
clamping to {ceiling}"
);
}
n.min(ceiling)
}
const DECODE_WIDTH_MAX: usize = 24;
fn width_for_host(
topo: &CoreTopology,
shape: DecodeShape,
ov: Overrides,
physical: Option<usize>,
) -> Option<usize> {
let max_t = topo.perf_core_count.max(1);
if ov.narrow.is_none() && ov.wide.is_none() && !topo.pin_cores.is_empty() {
return None;
}
let phys = match physical {
Some(p) => p.clamp(1, max_t),
None if ov.narrow.is_some() && ov.wide.is_some() => max_t,
None => return None,
};
let wide = ov
.wide
.unwrap_or_else(|| phys.saturating_add(phys / 4).min(DECODE_WIDTH_MAX));
let narrow = ov
.narrow
.unwrap_or_else(|| (phys / 2).clamp(1, DECODE_MAX_AUTO))
.min(wide);
let threshold_kb = ov.threshold_kb.unwrap_or(BPD_THRESHOLD_KB_DEFAULT);
Some(width_for_shape(max_t, narrow, wide, threshold_kb, shape))
}
#[derive(Debug, Clone, Copy, Default)]
struct Overrides {
narrow: Option<usize>,
wide: Option<usize>,
threshold_kb: Option<usize>,
}
fn env_overrides() -> Overrides {
Overrides {
narrow: env_usize("CERA_DECODE_NARROW"),
wide: env_usize("CERA_DECODE_WIDE"),
threshold_kb: env_usize("CERA_DECODE_BPD_KB"),
}
}
fn width_for_shape(
max_t: usize,
narrow: usize,
wide: usize,
threshold_kb: usize,
shape: DecodeShape,
) -> usize {
let want = if shape.bytes_per_dispatch() / 1000 < threshold_kb as u64 {
narrow
} else {
wide
};
want.clamp(1, max_t)
}
#[cfg(test)]
mod tests {
use super::*;
const PHYS: usize = 16;
const MAX_T: usize = 32;
const NARROW: usize = PHYS / 2; const WIDE: usize = PHYS + PHYS / 4;
fn shape(mb: u64, dispatches: usize) -> DecodeShape {
DecodeShape {
weight_bytes: mb * 1_000_000,
dispatches_per_token: dispatches,
}
}
fn width(mb: u64, dispatches: usize) -> usize {
width_for_shape(
MAX_T,
NARROW,
WIDE,
BPD_THRESHOLD_KB_DEFAULT,
shape(mb, dispatches),
)
}
#[test]
fn bytes_per_dispatch_is_bytes_over_dispatches() {
assert_eq!(shape(100, 100).bytes_per_dispatch(), 1_000_000);
assert_eq!(shape(100, 0).bytes_per_dispatch(), 100_000_000);
}
#[test]
fn rule_separates_the_controlled_pair() {
let lfm2_379 = width(379, 99); let dense_386 = width(386, 257); assert_eq!(lfm2_379, WIDE);
assert_eq!(dense_386, NARROW);
}
#[test]
fn rule_matches_measured_direction() {
let cases = [
(21, 25, false),
(92, 181, false),
(145, 181, false),
(153, 89, false),
(219, 99, false), (353, 145, false),
(379, 99, true),
(386, 257, false),
(531, 145, true),
(639, 225, true),
(808, 129, true),
(1321, 129, true),
];
for (mb, disp, wants_wide) in cases {
let want = if wants_wide { WIDE } else { NARROW };
assert_eq!(width(mb, disp), want, "{mb} MB / {disp} dispatches");
}
}
#[test]
fn width_never_exceeds_the_pool_ceiling() {
for max_t in [1, 2, 4, 6, 12, 16, 32] {
for s in [shape(4096, 64), shape(1, 4096)] {
let n = width_for_shape(max_t, NARROW, WIDE, BPD_THRESHOLD_KB_DEFAULT, s);
assert!(n >= 1 && n <= max_t, "max_t={max_t} gave {n}");
}
}
}
#[test]
fn arms_are_capped_on_many_core_hosts() {
let epyc = CoreTopology {
perf_core_count: 192,
pin_cores: Vec::new(),
fast_cores: 0,
core_weights: Vec::new(),
};
for phys in [16usize, 32, 64, 96] {
let narrow = width_for_host(&epyc, shape(21, 25), Overrides::default(), Some(phys))
.expect("homogeneous host with known physical count sizes");
let wide = width_for_host(&epyc, shape(1321, 129), Overrides::default(), Some(phys))
.expect("homogeneous host with known physical count sizes");
assert!(
wide <= DECODE_WIDTH_MAX,
"phys={phys}: wide {wide} over cap"
);
assert!(
narrow <= DECODE_MAX_AUTO,
"phys={phys}: narrow {narrow} exceeds the flat default"
);
assert!(narrow < wide, "phys={phys}: arms collapsed at {narrow}");
}
let zen5 = CoreTopology {
perf_core_count: 32,
pin_cores: Vec::new(),
fast_cores: 0,
core_weights: Vec::new(),
};
assert_eq!(
width_for_host(&zen5, shape(1321, 129), Overrides::default(), Some(16)),
Some(20)
);
assert_eq!(
width_for_host(&zen5, shape(21, 25), Overrides::default(), Some(16)),
Some(8)
);
}
#[test]
fn only_a_pinned_width_overrides_a_decline() {
let big_little = CoreTopology {
perf_core_count: 6,
pin_cores: vec![7, 6, 5, 4, 3, 2],
fast_cores: 6,
core_weights: Vec::new(),
};
let unknown_phys = CoreTopology {
perf_core_count: 32,
pin_cores: Vec::new(),
fast_cores: 0,
core_weights: Vec::new(),
};
let s = shape(219, 99);
let threshold_only = Overrides {
threshold_kb: Some(1000),
..Default::default()
};
let one_width = Overrides {
wide: Some(6),
..Default::default()
};
let both_widths = Overrides {
narrow: Some(3),
wide: Some(6),
..Default::default()
};
assert_eq!(
width_for_host(&big_little, s, threshold_only, Some(8)),
None
);
assert_eq!(width_for_host(&unknown_phys, s, threshold_only, None), None);
assert!(width_for_host(&big_little, s, one_width, Some(8)).is_some());
assert_eq!(width_for_host(&unknown_phys, s, one_width, None), None);
assert!(width_for_host(&unknown_phys, s, both_widths, None).is_some());
}
#[test]
fn pinned_arms_are_the_widths_used() {
let host = CoreTopology {
perf_core_count: 32,
pin_cores: Vec::new(),
fast_cores: 0,
core_weights: Vec::new(),
};
let ov = Overrides {
narrow: Some(3),
wide: Some(7),
..Default::default()
};
assert_eq!(width_for_host(&host, shape(21, 25), ov, Some(16)), Some(3));
assert_eq!(
width_for_host(&host, shape(1321, 129), ov, Some(16)),
Some(7)
);
let low_threshold = Overrides {
threshold_kb: Some(100),
..ov
};
assert_eq!(
width_for_host(&host, shape(21, 25), low_threshold, Some(16)),
Some(7),
"a 840 KB/dispatch model should take the wide arm under a 100 KB threshold"
);
}
#[test]
fn narrow_never_exceeds_wide() {
let host = CoreTopology {
perf_core_count: 32,
pin_cores: Vec::new(),
fast_cores: 0,
core_weights: Vec::new(),
};
let low_wide = Overrides {
wide: Some(4),
..Default::default()
};
assert_eq!(
width_for_host(&host, shape(21, 25), low_wide, Some(16)),
Some(4)
);
assert_eq!(
width_for_host(&host, shape(1321, 129), low_wide, Some(16)),
Some(4)
);
}
#[test]
fn from_tensors_counts_dispatches() {
const T: usize = 256; let untied = DecodeShape::from_tensors(
[
("token_embd.weight", 32_000, 1_000),
("blk.0.attn_q.weight", 512, 100),
("blk.0.attn_k.weight", 128, 100), ("blk.0.ffn_up.weight", 1024, 100),
("output.weight", 32_000, 100),
]
.into_iter(),
1, T,
)
.expect("shape");
assert_eq!(untied.dispatches_per_token, 4);
assert_eq!(untied.weight_bytes, 1_400);
let tied = DecodeShape::from_tensors(
[
("token_embd.weight", 32_000, 1_000),
("blk.0.attn_q.weight", 512, 100),
("blk.0.attn_k.weight", 128, 100),
("blk.0.ffn_up.weight", 1024, 100),
]
.into_iter(),
1,
T,
)
.expect("shape");
assert_eq!(tied.dispatches_per_token, 4);
assert!(DecodeShape::from_tensors([].into_iter(), 0, T).is_none());
}
#[test]
fn heterogeneous_topology_declines_sizing() {
let big_little = CoreTopology {
perf_core_count: 6,
pin_cores: vec![7, 6, 5, 4, 3, 2],
fast_cores: 6,
core_weights: Vec::new(),
};
assert_eq!(
width_for_host(&big_little, shape(219, 99), Overrides::default(), Some(8)),
None
);
}
#[test]
fn unknown_physical_count_declines_sizing() {
let windows_box = CoreTopology {
perf_core_count: 32,
pin_cores: Vec::new(),
fast_cores: 0,
core_weights: Vec::new(),
};
assert_eq!(
width_for_host(&windows_box, shape(1321, 129), Overrides::default(), None),
None
);
}
#[test]
fn prefill_width_defaults_to_perf_cores() {
let big_little = CoreTopology {
perf_core_count: 6,
pin_cores: vec![7, 6, 5, 4, 3, 2, 1, 0],
fast_cores: 6,
core_weights: Vec::new(),
};
assert_eq!(prefill_thread_count(&big_little), 6);
let unpinned = CoreTopology {
perf_core_count: 10,
pin_cores: Vec::new(),
fast_cores: 0,
core_weights: Vec::new(),
};
assert_eq!(prefill_thread_count(&unpinned), 10);
}
}