use crate::weight_matrix::QuantKind;
use std::collections::{HashMap, HashSet};
use std::panic::Location;
use std::sync::atomic::{AtomicBool, AtomicU64, Ordering};
use std::sync::{OnceLock, RwLock};
#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug, PartialOrd, Ord)]
pub enum Backend {
Cpu,
Metal,
Cuda,
}
impl Backend {
pub fn as_str(self) -> &'static str {
match self {
Backend::Cpu => "cpu",
Backend::Metal => "metal",
Backend::Cuda => "cuda",
}
}
pub fn is_accelerator(self) -> bool {
!matches!(self, Backend::Cpu)
}
}
impl std::fmt::Display for Backend {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.write_str(self.as_str())
}
}
pub mod op {
pub const MATVEC: &str = "matvec";
pub const MATVEC_MULTI: &str = "matvec_multi";
pub const GEMM_PREFILL: &str = "gemm_prefill";
pub const FFN_SWIGLU: &str = "ffn_swiglu";
pub const ENGINE_PREFILL_BATCH: &str = "engine.prefill_batch";
}
#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug)]
pub struct Key {
pub backend: Backend,
pub op: &'static str,
pub role: &'static str,
pub kind: Option<QuantKind>,
pub file: &'static str,
pub line: u32,
}
impl Key {
fn shape(&self) -> (Backend, &'static str, Option<QuantKind>) {
(self.backend, self.op, self.kind)
}
fn kind_name(&self) -> &'static str {
self.kind.map_or("f32", QuantKind::name)
}
}
impl std::fmt::Display for Key {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(
f,
"{} {} {} ({}) at {}:{}",
self.backend,
self.op,
self.kind_name(),
self.role,
self.file,
self.line
)
}
}
#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug)]
pub enum Severity {
ByDesign,
SlowPath,
}
#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug)]
pub enum Outcome {
Hit,
Miss {
fallback: &'static str,
severity: Severity,
},
}
impl Outcome {
pub fn slow_path(fallback: &'static str) -> Self {
Outcome::Miss {
fallback,
severity: Severity::SlowPath,
}
}
pub fn by_design(fallback: &'static str) -> Self {
Outcome::Miss {
fallback,
severity: Severity::ByDesign,
}
}
pub fn is_miss(self) -> bool {
matches!(self, Outcome::Miss { .. })
}
pub fn is_slow_path(self) -> bool {
matches!(
self,
Outcome::Miss {
severity: Severity::SlowPath,
..
}
)
}
pub fn fallback(self) -> Option<&'static str> {
match self {
Outcome::Miss { fallback, .. } => Some(fallback),
Outcome::Hit => None,
}
}
}
#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug)]
pub enum Phase {
Build,
Run,
}
#[derive(Clone, Copy, Debug)]
pub struct Lookup {
pub backend: Backend,
pub op: &'static str,
pub role: &'static str,
pub kind: Option<QuantKind>,
}
impl Lookup {
pub fn new(backend: Backend, op: &'static str, kind: Option<QuantKind>) -> Self {
Lookup {
backend,
op,
role: "(dispatch)",
kind,
}
}
pub fn with_role(mut self, role: &'static str) -> Self {
self.role = role;
self
}
}
#[derive(Clone, Copy, Debug)]
pub struct Entry {
pub key: Key,
pub outcome: Outcome,
pub phase: Phase,
pub count: u64,
}
impl std::fmt::Display for Entry {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self.outcome {
Outcome::Hit => write!(f, "hit {} x{}", self.key, self.count),
Outcome::Miss {
fallback,
severity: Severity::ByDesign,
} => write!(f, "host {} -> {} x{}", self.key, fallback, self.count),
Outcome::Miss {
fallback,
severity: Severity::SlowPath,
} => write!(f, "MISS {} -> {} x{}", self.key, fallback, self.count),
}
}
}
#[derive(Clone, Debug, Default)]
pub struct SealReport {
pub entries: Vec<Entry>,
pub misses: Vec<Entry>,
pub violations: Vec<Entry>,
}
impl SealReport {
pub fn render(&self) -> String {
let mut s = String::new();
for e in &self.entries {
s.push_str("ferrox kernels: ");
s.push_str(&e.to_string());
s.push('\n');
}
s
}
pub fn render_violations(&self) -> String {
let mut s = String::new();
for e in &self.violations {
let unit = if e.key.op.starts_with("engine.") {
String::new()
} else {
format!(", {} weights", e.count)
};
let kind = match e.key.kind {
Some(k) => format!(" {}", k.name()),
None => String::new(),
};
s.push_str(&format!(
"ferrox: NO KERNEL for {} {}{} ({}) -> falls back to {} [{}:{}{}]\n",
e.key.backend,
e.key.op,
kind,
e.key.role,
e.outcome.fallback().unwrap_or("(hit)"),
e.key.file,
e.key.line,
unit,
));
}
s
}
}
#[derive(Clone, Debug)]
pub struct StrictKernelError {
pub report: SealReport,
}
impl std::fmt::Display for StrictKernelError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(
f,
"FERROX_STRICT_KERNELS=1 and {} kernel lookup(s) missed:\n{}",
self.report.violations.len(),
self.report.render_violations()
)
}
}
impl std::error::Error for StrictKernelError {}
struct Row {
outcome: Outcome,
phase: Phase,
count: AtomicU64,
}
#[derive(Default)]
struct State {
rows: HashMap<Key, Row>,
known: HashSet<(Backend, &'static str, Option<QuantKind>)>,
surprises: Vec<Entry>,
}
impl State {
fn snapshot(&self, build_only: bool) -> Vec<Entry> {
let mut entries: Vec<Entry> = self
.rows
.iter()
.filter(|(_, row)| !build_only || row.phase == Phase::Build)
.map(|(key, row)| Entry {
key: *key,
outcome: row.outcome,
phase: row.phase,
count: row.count.load(Ordering::Relaxed),
})
.collect();
entries.sort_by_key(|e| {
(
e.key.backend,
e.key.op,
e.key.kind.map(|k| k.name()).unwrap_or("f32"),
e.key.role,
e.key.line,
)
});
entries
}
}
pub struct Registry {
inner: RwLock<State>,
sealed: AtomicBool,
}
impl Default for Registry {
fn default() -> Self {
Self::new()
}
}
impl Registry {
pub fn new() -> Self {
Registry {
inner: RwLock::new(State::default()),
sealed: AtomicBool::new(false),
}
}
fn read(&self) -> std::sync::RwLockReadGuard<'_, State> {
self.inner.read().unwrap_or_else(|e| e.into_inner())
}
fn write(&self) -> std::sync::RwLockWriteGuard<'_, State> {
self.inner.write().unwrap_or_else(|e| e.into_inner())
}
fn bump_existing(&self, key: &Key) -> bool {
match self.read().rows.get(key) {
Some(row) => {
row.count.fetch_add(1, Ordering::Relaxed);
true
}
None => false,
}
}
pub fn is_sealed(&self) -> bool {
self.sealed.load(Ordering::Relaxed)
}
pub fn record_build_at(&self, loc: &'static Location<'static>, l: Lookup, outcome: Outcome) {
let key = Key {
backend: l.backend,
op: l.op,
role: l.role,
kind: l.kind,
file: loc.file(),
line: loc.line(),
};
if self.bump_existing(&key) {
return;
}
let mut st = self.write();
st.known.insert(key.shape());
st.rows
.entry(key)
.or_insert_with(|| Row {
outcome,
phase: Phase::Build,
count: AtomicU64::new(0),
})
.count
.fetch_add(1, Ordering::Relaxed);
}
pub fn record_at(&self, loc: &'static Location<'static>, l: Lookup, outcome: Outcome) {
let key = Key {
backend: l.backend,
op: l.op,
role: l.role,
kind: l.kind,
file: loc.file(),
line: loc.line(),
};
if self.bump_existing(&key) {
return;
}
let sealed = self.is_sealed();
let mut st = self.write();
if let Some(row) = st.rows.get(&key) {
row.count.fetch_add(1, Ordering::Relaxed);
return;
}
let phase = if sealed { Phase::Run } else { Phase::Build };
st.rows.insert(
key,
Row {
outcome,
phase,
count: AtomicU64::new(1),
},
);
if !sealed {
st.known.insert(key.shape());
return;
}
if !outcome.is_slow_path() || st.known.contains(&key.shape()) {
return;
}
st.surprises.push(Entry {
key,
outcome,
phase: Phase::Run,
count: 1,
});
drop(st);
let fallback = outcome
.fallback()
.unwrap_or("(unknown)" );
eprintln!(
"ferrox: SILENT SLOW PATH — kernel lookup missed after the model was sealed.\n\
ferrox: {} {} for {} has no kernel; falling back to {}.\n\
ferrox: call site {}:{} (role {}).\n\
ferrox: this was not predicted at load time, so no startup diagnostic covered it.\n\
ferrox: set FERROX_STRICT_KERNELS=1 to make this a hard error.",
key.backend,
key.op,
key.kind_name(),
fallback,
key.file,
key.line,
key.role,
);
}
pub fn seal(&self) -> SealReport {
self.sealed.store(true, Ordering::Relaxed);
let entries = self.read().snapshot(true);
let misses: Vec<Entry> = entries
.iter()
.copied()
.filter(|e| e.outcome.is_miss())
.collect();
let violations: Vec<Entry> = misses
.iter()
.copied()
.filter(|e| e.key.backend.is_accelerator() && e.outcome.is_slow_path())
.collect();
SealReport {
entries,
misses,
violations,
}
}
pub fn surprises(&self) -> Vec<Entry> {
self.read().surprises.clone()
}
pub fn entries(&self) -> Vec<Entry> {
self.read().snapshot(false)
}
}
static GLOBAL: OnceLock<Registry> = OnceLock::new();
pub fn global() -> &'static Registry {
GLOBAL.get_or_init(Registry::new)
}
pub fn enabled() -> bool {
static V: OnceLock<bool> = OnceLock::new();
*V.get_or_init(|| {
!matches!(
std::env::var("FERROX_KERNEL_REGISTRY").ok().as_deref(),
Some("0") | Some("false") | Some("off")
)
})
}
pub fn verbose() -> bool {
static V: OnceLock<bool> = OnceLock::new();
*V.get_or_init(|| {
matches!(
std::env::var("FERROX_KERNEL_REGISTRY").ok().as_deref(),
Some("1") | Some("true") | Some("on") | Some("verbose")
)
})
}
pub fn strict() -> bool {
static V: OnceLock<bool> = OnceLock::new();
*V.get_or_init(|| {
matches!(
std::env::var("FERROX_STRICT_KERNELS").ok().as_deref(),
Some("1") | Some("true") | Some("on")
)
})
}
#[track_caller]
pub fn record_build(l: Lookup, outcome: Outcome) {
if !enabled() {
return;
}
global().record_build_at(Location::caller(), l, outcome);
}
#[track_caller]
pub fn hit(l: Lookup) {
if !enabled() {
return;
}
global().record_at(Location::caller(), l, Outcome::Hit);
}
#[track_caller]
pub fn miss(l: Lookup, fallback: &'static str) {
if !enabled() {
return;
}
global().record_at(Location::caller(), l, Outcome::slow_path(fallback));
}
#[track_caller]
pub fn miss_by_design(l: Lookup, fallback: &'static str) {
if !enabled() {
return;
}
global().record_at(Location::caller(), l, Outcome::by_design(fallback));
}
pub fn seal() -> SealReport {
let report = global().seal();
if !enabled() {
return report;
}
if verbose() {
eprint!("{}", report.render());
}
if !report.violations.is_empty() && !strict() {
eprint!("{}", report.render_violations());
eprintln!(
"ferrox: {} kernel lookup(s) above will run on a slower path than the \
selected backend. Set FERROX_STRICT_KERNELS=1 to refuse to run instead.",
report.violations.len()
);
}
report
}
pub fn seal_or_error() -> Result<SealReport, StrictKernelError> {
let report = seal();
if strict() && !report.violations.is_empty() {
return Err(StrictKernelError { report });
}
Ok(report)
}
#[cfg(test)]
mod tests {
use super::*;
fn lookup(backend: Backend, kind: Option<QuantKind>) -> Lookup {
Lookup::new(backend, op::GEMM_PREFILL, kind).with_role("ffn_down")
}
#[test]
fn a_build_hit_is_recorded_once_per_shape_with_a_count() {
let r = Registry::new();
let loc = Location::caller();
for _ in 0..5 {
r.record_build_at(
loc,
lookup(Backend::Metal, Some(QuantKind::Q4K)),
Outcome::Hit,
);
}
let report = r.seal();
assert_eq!(report.entries.len(), 1);
assert_eq!(report.entries[0].count, 5);
assert!(report.misses.is_empty());
assert!(report.violations.is_empty());
}
#[test]
fn a_quantized_weight_with_no_accelerator_kernel_is_a_violation() {
let r = Registry::new();
r.record_build_at(
Location::caller(),
lookup(Backend::Metal, Some(QuantKind::IQ4XS)),
Outcome::slow_path("CPU apply_batch"),
);
let report = r.seal();
assert_eq!(report.violations.len(), 1);
assert_eq!(report.violations[0].key.kind, Some(QuantKind::IQ4XS));
assert!(report.render_violations().contains("IQ4_XS"));
}
#[test]
fn an_f32_miss_is_reported_but_is_not_a_violation() {
let r = Registry::new();
r.record_build_at(
Location::caller(),
lookup(Backend::Metal, None),
Outcome::by_design("host GEMV"),
);
let report = r.seal();
assert_eq!(report.misses.len(), 1);
assert!(report.violations.is_empty());
}
#[test]
fn a_cpu_backend_miss_is_not_a_violation() {
let r = Registry::new();
r.record_build_at(
Location::caller(),
lookup(Backend::Cpu, Some(QuantKind::IQ2XXS)),
Outcome::slow_path("f32 dequant-dot"),
);
assert!(r.seal().violations.is_empty());
}
#[test]
fn a_post_seal_miss_on_a_predicted_shape_is_not_a_surprise() {
let r = Registry::new();
let loc = Location::caller();
r.record_build_at(
loc,
lookup(Backend::Metal, Some(QuantKind::IQ4XS)),
Outcome::slow_path("CPU apply_batch"),
);
r.seal();
r.record_at(
loc,
lookup(Backend::Metal, Some(QuantKind::IQ4XS)),
Outcome::slow_path("CPU apply_batch"),
);
assert!(r.surprises().is_empty(), "seal already reported this shape");
}
#[test]
fn a_post_seal_miss_on_an_unpredicted_shape_is_a_surprise_reported_once() {
let r = Registry::new();
let loc = Location::caller();
r.record_build_at(
loc,
lookup(Backend::Metal, Some(QuantKind::Q4K)),
Outcome::Hit,
);
r.seal();
for _ in 0..3 {
r.record_at(
loc,
lookup(Backend::Metal, Some(QuantKind::Q2K)),
Outcome::slow_path("CPU apply_batch"),
);
}
let surprises = r.surprises();
assert_eq!(surprises.len(), 1, "warned once, not once per dispatch");
assert_eq!(surprises[0].key.kind, Some(QuantKind::Q2K));
assert_eq!(surprises[0].phase, Phase::Run);
}
#[test]
fn a_post_seal_hit_is_never_a_surprise() {
let r = Registry::new();
r.seal();
r.record_at(
Location::caller(),
lookup(Backend::Metal, Some(QuantKind::Q4K)),
Outcome::Hit,
);
assert!(r.surprises().is_empty());
}
#[test]
fn build_records_after_seal_extend_the_predicted_set() {
let r = Registry::new();
let loc = Location::caller();
r.seal();
r.record_build_at(
loc,
lookup(Backend::Metal, Some(QuantKind::Q6K)),
Outcome::slow_path("CPU apply_batch"),
);
r.record_at(
loc,
lookup(Backend::Metal, Some(QuantKind::Q6K)),
Outcome::slow_path("CPU apply_batch"),
);
assert!(r.surprises().is_empty());
}
#[test]
fn concurrent_dispatch_misses_warn_once_and_count_all() {
let r = std::sync::Arc::new(Registry::new());
let loc = Location::caller();
r.seal();
std::thread::scope(|s| {
for _ in 0..8 {
let r = std::sync::Arc::clone(&r);
s.spawn(move || {
for _ in 0..250 {
r.record_at(
loc,
lookup(Backend::Metal, Some(QuantKind::IQ1S)),
Outcome::slow_path("CPU apply_batch"),
);
}
});
}
});
assert_eq!(r.surprises().len(), 1, "warned once across 8 threads");
let counted: u64 = r
.entries()
.iter()
.filter(|e| e.key.kind == Some(QuantKind::IQ1S))
.map(|e| e.count)
.sum();
assert_eq!(counted, 2000, "every lookup counted exactly once");
}
#[test]
fn the_report_names_the_call_site_and_the_quant_kind() {
let r = Registry::new();
r.record_build_at(
Location::caller(),
lookup(Backend::Metal, Some(QuantKind::Q5K)),
Outcome::slow_path("CPU apply_batch"),
);
let rendered = r.seal().render();
assert!(rendered.contains("Q5_K"), "{rendered}");
assert!(rendered.contains("kernel_registry.rs"), "{rendered}");
assert!(rendered.contains("ffn_down"), "{rendered}");
assert!(rendered.contains("CPU apply_batch"), "{rendered}");
}
}