pub mod cache;
pub use cache::*;
pub use fuel_dispatch::ranker::profile_oracle::ProfileJudgeOracle;
use fuel_correctness_fixtures::{
CorrectnessDrift, CorrectnessFixture, FIXTURE_FILE_VERSION, FixtureFile,
validate_against_fixture,
};
use fuel_hardware::probe::ProbeReport;
use fuel_ir::probe::{BackendId, DeviceDescriptor};
use fuel_ir::{DType, Result, Shape};
use std::collections::HashMap;
use std::panic::AssertUnwindSafe;
use std::path::Path;
use std::sync::{Arc, RwLock};
use std::time::Instant;
pub use fuel_ir::dispatch::{
OpKind, PROFILE_REPORT_VERSION, ProfileEntry, ProfileReport, SizeClass,
};
pub use fuel_dispatch::judge_report_path::{PROFILE_REPORT_FILENAME, default_report_path};
#[cfg(test)]
pub(crate) fn test_equiv_key(backend: BackendId) -> fuel_ir::probe::EquivalenceKey {
fuel_ir::probe::EquivalenceKey {
backend,
vendor_id: 0,
device_id: 0,
compute_capability: None,
driver_version: String::new(),
}
}
const PROFILED_OPS: &[OpKind] = &[
OpKind::MatMul,
OpKind::AddElementwise,
OpKind::NegElementwise,
OpKind::SqrElementwise,
OpKind::SqrtElementwise,
OpKind::ExpElementwise,
OpKind::LogElementwise,
OpKind::SinElementwise,
OpKind::CosElementwise,
OpKind::TanhElementwise,
OpKind::SigmoidElementwise,
OpKind::SiluElementwise,
OpKind::GeluTanhElementwise,
OpKind::ReluElementwise,
OpKind::StepElementwise,
OpKind::RecipElementwise,
OpKind::AbsElementwise,
OpKind::FloorElementwise,
OpKind::CeilElementwise,
OpKind::RoundElementwise,
OpKind::SignElementwise,
OpKind::ErfElementwise,
OpKind::GeluElementwise,
OpKind::RsqrtElementwise,
OpKind::PowElementwise,
OpKind::RemElementwise,
OpKind::SubElementwise,
OpKind::MulElementwise,
OpKind::DivElementwise,
OpKind::MaximumElementwise,
OpKind::MinimumElementwise,
OpKind::SumReduce,
OpKind::MaxReduce,
OpKind::MinReduce,
OpKind::MeanReduce,
OpKind::ReduceSumTo,
OpKind::ReduceMaxTo,
OpKind::Affine,
OpKind::ClampElementwise,
OpKind::PowIElementwise,
OpKind::FlashAttn,
];
const PROFILED_DTYPES: &[DType] = &[DType::F32, DType::F16, DType::BF16];
const DECODE_HEADS: usize = 32;
const DECODE_KLEN_SMALL: usize = 128;
const DECODE_KLEN_CAP: usize = 4096;
const DECODE_HIDDEN: usize = 2048;
const DECODE_HEAD_DIM: usize = 64;
const DECODE_FFN: usize = 5632;
const DECODE_SCORE_SMALL_ELEMS: usize = DECODE_HEADS * DECODE_KLEN_SMALL;
const DECODE_SCORE_CAP_ELEMS: usize = DECODE_HEADS * DECODE_KLEN_CAP;
const DECODE_KV_HEADS: usize = 4;
pub const DEFAULT_ITERATIONS: u32 = 7;
pub const DEFAULT_WARMUP: u32 = 3;
pub struct Judge {
pub iterations: u32,
pub warmup: u32,
pub size_plan_override: Option<Vec<(OpKind, OpSize)>>,
pub fixtures: Option<HashMap<(OpKind, DType, SizeClass), Vec<CorrectnessFixture>>>,
}
impl Default for Judge {
fn default() -> Self {
Self {
iterations: DEFAULT_ITERATIONS,
warmup: DEFAULT_WARMUP,
size_plan_override: None,
fixtures: None,
}
}
}
impl Judge {
pub fn with_fixtures_from(path: &Path) -> Result<Self> {
let fixtures = load_fixtures_recursive(path)?;
Ok(Self {
fixtures: Some(fixtures),
..Self::default()
})
}
fn lookup_fixture(
&self,
op: OpKind,
dtype: DType,
size_class: SizeClass,
output_elem_count: usize,
input_elem_count: usize,
) -> Option<&CorrectnessFixture> {
let map = self.fixtures.as_ref()?;
let bucket = map.get(&(op, dtype, size_class))?;
let expected_seed = fuel_correctness_fixtures::capture::derive_seed(op, dtype, size_class);
let mut regenerated_hash: Option<u64> = None;
let mut shape_matches = false;
for f in bucket {
if f.output_element_count != output_elem_count {
continue;
}
shape_matches = true;
if f.input_seed != expected_seed {
eprintln!(
"judge: fixture for ({op:?}, {dtype:?}, size_class={}) seed mismatch \
(fixture seed={}, expected={}); falling back to consensus",
size_class.0, f.input_seed, expected_seed,
);
continue;
}
let actual_hash = *regenerated_hash.get_or_insert_with(|| {
let input = fuel_correctness_fixtures::capture::deterministic_f32_input(
op,
input_elem_count,
);
fuel_correctness_fixtures::capture::hash_f32_input(&input)
});
if f.input_hash != actual_hash {
eprintln!(
"judge: fixture input drifted for ({op:?}, {dtype:?}, \
size_class={}, seed={}); fixture hash={}, regenerated hash={}; \
falling back to consensus",
size_class.0, f.input_seed, f.input_hash, actual_hash,
);
continue;
}
return Some(f);
}
if !shape_matches {
eprintln!(
"judge: fixture bucket for ({op:?}, {dtype:?}, size_class={}) has no entry \
matching output_element_count={output_elem_count}; falling back to consensus",
size_class.0,
);
}
None
}
fn size_plan(&self, op: OpKind) -> Vec<OpSize> {
if let Some(over) = &self.size_plan_override {
return over
.iter()
.filter(|(k, _)| *k == op)
.map(|(_, s)| *s)
.collect();
}
match op {
OpKind::MatMul => vec![
OpSize::MatMul {
m: 64,
n: 64,
k: 64,
},
OpSize::MatMul {
m: 256,
n: 256,
k: 256,
},
OpSize::MatMul {
m: 1024,
n: 1024,
k: 1024,
},
OpSize::MatMul {
m: 1,
n: DECODE_HIDDEN,
k: DECODE_HIDDEN,
},
OpSize::MatMul {
m: 1,
n: DECODE_FFN,
k: DECODE_HIDDEN,
},
OpSize::MatMul {
m: 1,
n: DECODE_KLEN_SMALL,
k: DECODE_HEAD_DIM,
},
OpSize::MatMul {
m: 1,
n: DECODE_HEAD_DIM,
k: DECODE_KLEN_SMALL,
},
],
OpKind::AddElementwise
| OpKind::SubElementwise
| OpKind::MulElementwise
| OpKind::DivElementwise
| OpKind::MaximumElementwise
| OpKind::MinimumElementwise
| OpKind::PowElementwise
| OpKind::RemElementwise
| OpKind::NegElementwise
| OpKind::SqrElementwise
| OpKind::SqrtElementwise
| OpKind::ExpElementwise
| OpKind::LogElementwise
| OpKind::SinElementwise
| OpKind::CosElementwise
| OpKind::TanhElementwise
| OpKind::SigmoidElementwise
| OpKind::SiluElementwise
| OpKind::GeluTanhElementwise
| OpKind::ReluElementwise
| OpKind::StepElementwise
| OpKind::RecipElementwise
| OpKind::AbsElementwise
| OpKind::FloorElementwise
| OpKind::CeilElementwise
| OpKind::RoundElementwise
| OpKind::SignElementwise
| OpKind::ErfElementwise
| OpKind::GeluElementwise
| OpKind::RsqrtElementwise
| OpKind::Affine
| OpKind::ClampElementwise
| OpKind::PowIElementwise => {
let mut ladder = vec![
OpSize::Elementwise(1 << 10),
OpSize::Elementwise(1 << 16),
OpSize::Elementwise(1 << 20),
];
if is_decode_softmax_elementwise(op) {
ladder.push(OpSize::Elementwise(DECODE_SCORE_SMALL_ELEMS));
ladder.push(OpSize::Elementwise(DECODE_SCORE_CAP_ELEMS));
}
ladder
}
OpKind::SumReduce | OpKind::MaxReduce | OpKind::MinReduce | OpKind::MeanReduce => {
let mut ladder = vec![
OpSize::Reduce {
rows: 1 << 4,
cols: 64,
},
OpSize::Reduce {
rows: 1 << 10,
cols: 64,
},
OpSize::Reduce {
rows: 1 << 14,
cols: 64,
},
];
if is_decode_softmax_reduction(op) {
ladder.push(OpSize::Reduce {
rows: DECODE_HEADS,
cols: DECODE_KLEN_SMALL,
});
ladder.push(OpSize::Reduce {
rows: DECODE_HEADS,
cols: DECODE_KLEN_CAP,
});
}
ladder
}
OpKind::ReduceSumTo | OpKind::ReduceMaxTo => vec![
OpSize::ReduceTo {
rows: 1 << 4,
cols: 64,
},
OpSize::ReduceTo {
rows: 1 << 10,
cols: 64,
},
OpSize::ReduceTo {
rows: 1 << 14,
cols: 64,
},
],
OpKind::FlashAttn => vec![
OpSize::Attention {
b: 1,
hq: DECODE_HEADS,
hkv: DECODE_KV_HEADS,
d: DECODE_HEAD_DIM,
k_len: DECODE_KLEN_SMALL,
},
OpSize::Attention {
b: 1,
hq: DECODE_HEADS,
hkv: DECODE_KV_HEADS,
d: DECODE_HEAD_DIM,
k_len: DECODE_KLEN_CAP,
},
],
_ => Vec::new(),
}
}
pub fn run(&self, probe: &ProbeReport) -> ProfileReport {
let mut entries = Vec::new();
let classes = probe.equivalence_classes();
let mut class_keys: Vec<_> = classes.keys().collect();
class_keys.sort_by_key(|k| (k.backend as u32, k.device_id, k.vendor_id));
for &op in PROFILED_OPS {
for sz in self.size_plan(op) {
let size_class = match sz {
OpSize::MatMul { m, n, k } => SizeClass::matmul(m, n, k),
OpSize::Attention { hq, k_len, d, .. } => SizeClass::attention(hq, k_len, d),
_ => SizeClass::from_elem_count(sz.total_elements()),
};
for &dtype in PROFILED_DTYPES {
let mut cell_runs: Vec<CellRun> = Vec::with_capacity(class_keys.len());
for key in &class_keys {
let devs = &classes[key];
let rep = devs[0];
if let Some(run) = self.measure_on_device_capturing(op, dtype, &sz, rep) {
let dispatched_source = run.kernel_source.clone();
let dispatched_revision = run.kernel_revision_hash;
cell_runs.push(run);
if let Some(extra_runs) = self.measure_extra_alternatives(
op,
dtype,
&sz,
rep,
&dispatched_source,
dispatched_revision,
) {
for extra in extra_runs {
cell_runs.push(extra);
}
}
}
}
let expected_elem_count =
cell_runs.first().map(|r| r.output.len()).unwrap_or(0);
let input_elem_count = sz.input_elements(op);
let fixture = self.lookup_fixture(
op,
dtype,
size_class,
expected_elem_count,
input_elem_count,
);
let consensus = if fixture.is_none() {
compute_pairwise_consensus(&cell_runs)
} else {
Vec::new() };
for (i, run) in cell_runs.iter().enumerate() {
let rel_err = if let Some(f) = fixture {
max_rel_err_vs_fixture(&run.output, f)
} else {
max_rel_err_vs_consensus(&cell_runs, &consensus, i)
};
let class_devs = classes.iter().find_map(|(k, devs)| {
if k.backend == run.backend && devs[0].device_index == run.device_index
{
Some(devs)
} else {
None
}
});
if let Some(devs) = class_devs {
for d in devs {
entries.push(ProfileEntry {
op,
dtype,
size_class,
backend: run.backend,
device_index: d.device_index,
latency_ns: run.latency_ns,
iterations: run.iterations,
max_rel_error: rel_err,
kernel_source: run.kernel_source.clone(),
kernel_revision_hash: run.kernel_revision_hash,
device: d.equivalence_key(),
});
}
}
}
}
}
}
ProfileReport {
version: PROFILE_REPORT_VERSION,
entries,
}
}
fn measure_extra_alternatives(
&self,
op: OpKind,
dtype: DType,
size: &OpSize,
device: &DeviceDescriptor,
dispatched_kernel_source: &str,
dispatched_kernel_revision: u64,
) -> Option<Vec<CellRun>> {
if device.backend != BackendId::Cpu {
return None;
}
if dtype != DType::F32 {
return None;
}
let alternatives =
direct_call_alternatives(op, dispatched_kernel_source, dispatched_kernel_revision);
if alternatives.is_empty() {
return None;
}
let prepared = prepare_direct_call_inputs(op, size)?;
let mut extra: Vec<CellRun> = Vec::with_capacity(alternatives.len());
for alt in alternatives {
if let Some(run) = self.time_alternative_direct(op, size, device, &prepared, &alt) {
extra.push(run);
}
}
if extra.is_empty() { None } else { Some(extra) }
}
fn time_alternative_direct(
&self,
op: OpKind,
size: &OpSize,
device: &DeviceDescriptor,
prepared: &PreparedDirectCall,
alt: &DirectCallAlternative,
) -> Option<CellRun> {
let kernel = alt.kernel;
for _ in 0..self.warmup {
let r = std::panic::catch_unwind(AssertUnwindSafe(|| {
let mut outs = vec![Arc::clone(&prepared.output)];
kernel(
&prepared.inputs,
&mut outs,
&prepared.layouts,
&prepared.op_params,
)
}));
if r.is_err() {
eprintln!(
"judge: skipping {op}@{size:?} on {}:{} kernel_source={:?} — kernel panicked in warmup",
device.backend, device.device_index, alt.kernel_source,
);
return None;
}
if let Ok(Err(_)) = r {
eprintln!(
"judge: skipping {op}@{size:?} on {}:{} kernel_source={:?} — kernel returned Err in warmup",
device.backend, device.device_index, alt.kernel_source,
);
return None;
}
}
let mut timings_ns = Vec::with_capacity(self.iterations as usize);
let mut captured_output: Option<Vec<f32>> = None;
for _ in 0..self.iterations {
let t0 = Instant::now();
let res = std::panic::catch_unwind(AssertUnwindSafe(|| {
let mut outs = vec![Arc::clone(&prepared.output)];
kernel(
&prepared.inputs,
&mut outs,
&prepared.layouts,
&prepared.op_params,
)
}));
let elapsed_ns = t0.elapsed().as_nanos() as u64;
match res {
Ok(Ok(())) => {}
_ => {
eprintln!(
"judge: skipping {op}@{size:?} on {}:{} kernel_source={:?} — direct call failed mid-run",
device.backend, device.device_index, alt.kernel_source,
);
return None;
}
}
timings_ns.push(elapsed_ns);
if captured_output.is_none() {
captured_output = Some(read_output_f32(&prepared.output));
}
}
timings_ns.sort_unstable();
let median = timings_ns[timings_ns.len() / 2];
let output = captured_output?;
Some(CellRun {
backend: device.backend,
device_index: device.device_index,
output,
latency_ns: median,
iterations: self.iterations,
kernel_source: alt.kernel_source.to_string(),
kernel_revision_hash: alt.kernel_revision_hash,
})
}
fn measure_on_device_capturing(
&self,
op: OpKind,
dtype: DType,
size: &OpSize,
device: &DeviceDescriptor,
) -> Option<CellRun> {
if device.backend != BackendId::Cpu && !has_binding_alternative(op, dtype, device.backend) {
eprintln!(
"judge: skipping {op}@{size:?} ({dtype:?}) on {}:{} — no binding-table \
alternative registered for this backend/dtype",
device.backend, device.device_index,
);
return None;
}
let factory = match crate::factories::factory_for(device.backend) {
Some(f) => f,
None => {
eprintln!(
"judge: skipping {}:{} — backend not compiled in",
device.backend, device.device_index,
);
return None;
}
};
let mut realizer = match factory.try_make_realizer(device.device_index) {
Ok(r) => r,
Err(e) => {
eprintln!(
"judge: skipping {}:{} — factory failed: {e}",
device.backend, device.device_index,
);
return None;
}
};
let cell = self.time_op_capturing(op, dtype, size, device, realizer.as_mut());
drop(realizer);
cell
}
fn time_op_capturing(
&self,
op: OpKind,
dtype: DType,
size: &OpSize,
device: &DeviceDescriptor,
realizer: &mut dyn crate::factories::Realizer,
) -> Option<CellRun> {
let tensor =
match std::panic::catch_unwind(AssertUnwindSafe(|| build_input_graph(op, dtype, size)))
{
Ok(t) => t,
Err(_) => {
eprintln!("judge: skipping {op}@{size:?} — build_input_graph panicked",);
return None;
}
};
for _ in 0..self.warmup {
match std::panic::catch_unwind(AssertUnwindSafe(|| {
realizer.realize_capture_f32(&tensor)
})) {
Ok(Ok(_)) => {}
Ok(Err(e)) => {
eprintln!(
"judge: skipping {op}@{size:?} on {}:{} — backend realize errored: {e}",
device.backend, device.device_index,
);
return None;
}
Err(_) => {
eprintln!(
"judge: skipping {op}@{size:?} on {}:{} — backend realize panicked",
device.backend, device.device_index,
);
return None;
}
}
}
let mut timings_ns = Vec::with_capacity(self.iterations as usize);
let mut captured_output: Option<Vec<f32>> = None;
for _ in 0..self.iterations {
let t0 = Instant::now();
let out = match std::panic::catch_unwind(AssertUnwindSafe(|| {
realizer.realize_capture_f32(&tensor)
})) {
Ok(Ok(v)) => v,
Ok(Err(e)) => {
eprintln!(
"judge: skipping {op}@{size:?} on {}:{} — backend realize errored mid-run: {e}",
device.backend, device.device_index,
);
return None;
}
Err(_) => {
eprintln!(
"judge: skipping {op}@{size:?} on {}:{} — backend realize panicked mid-run",
device.backend, device.device_index,
);
return None;
}
};
let elapsed_ns = t0.elapsed().as_nanos() as u64;
timings_ns.push(elapsed_ns);
if captured_output.is_none() {
captured_output = Some(out);
}
}
timings_ns.sort_unstable();
let median = timings_ns[timings_ns.len() / 2];
let output = captured_output?;
let (kernel_source, kernel_revision_hash) = match (
realizer.last_kernel_source(),
realizer.last_kernel_revision(),
) {
(Some(src), Some(rev)) => (src.to_string(), rev),
_ => return None,
};
Some(CellRun {
backend: device.backend,
device_index: device.device_index,
output,
latency_ns: median,
iterations: self.iterations,
kernel_source,
kernel_revision_hash,
})
}
}
struct DirectCallAlternative {
kernel: fuel_dispatch::kernel::KernelRef,
kernel_source: &'static str,
kernel_revision_hash: u64,
}
struct PreparedDirectCall {
inputs: Vec<Arc<RwLock<fuel_memory::Storage>>>,
output: Arc<RwLock<fuel_memory::Storage>>,
layouts: Vec<fuel_ir::Layout>,
op_params: fuel_dispatch::kernel::OpParams,
}
fn has_binding_alternative(op: OpKind, dtype: DType, backend: BackendId) -> bool {
let dtypes = match canonical_binding_dtypes_for(op, dtype) {
Some(d) => d,
None => return false,
};
let table = fuel_dispatch::dispatch::global_bindings();
!table.lookup_alternatives(op, &dtypes, backend).is_empty()
}
fn is_dispatched_alt(
alt_source: &str,
alt_revision: u64,
dispatched_source: &str,
dispatched_revision: u64,
) -> bool {
alt_source == dispatched_source && alt_revision == dispatched_revision
}
fn direct_call_alternatives(
op: OpKind,
dispatched_kernel_source: &str,
dispatched_kernel_revision: u64,
) -> Vec<DirectCallAlternative> {
let dtypes = match canonical_binding_dtypes_for(op, DType::F32) {
Some(d) => d,
None => return Vec::new(),
};
let table = fuel_dispatch::dispatch::global_bindings();
let alts = table.lookup_alternatives(op, &dtypes, BackendId::Cpu);
if alts.len() < 2 {
return Vec::new();
}
let mut extras = Vec::with_capacity(alts.len().saturating_sub(1));
let mut skipped_dispatched = false;
for alt in alts.iter() {
if !skipped_dispatched
&& is_dispatched_alt(
alt.kernel_source,
alt.kernel_revision_hash,
dispatched_kernel_source,
dispatched_kernel_revision,
)
{
skipped_dispatched = true;
continue;
}
extras.push(DirectCallAlternative {
kernel: alt.kernel,
kernel_source: alt.kernel_source,
kernel_revision_hash: alt.kernel_revision_hash,
});
}
extras
}
fn canonical_binding_dtypes_for(op: OpKind, dtype: DType) -> Option<Vec<DType>> {
let t = dtype;
Some(match op {
OpKind::MatMul => vec![t, t, t],
OpKind::AddElementwise
| OpKind::SubElementwise
| OpKind::MulElementwise
| OpKind::DivElementwise
| OpKind::MaximumElementwise
| OpKind::MinimumElementwise
| OpKind::PowElementwise
| OpKind::RemElementwise => vec![t, t, t],
OpKind::NegElementwise
| OpKind::SqrElementwise
| OpKind::SqrtElementwise
| OpKind::ExpElementwise
| OpKind::LogElementwise
| OpKind::SinElementwise
| OpKind::CosElementwise
| OpKind::TanhElementwise
| OpKind::SigmoidElementwise
| OpKind::SiluElementwise
| OpKind::GeluTanhElementwise
| OpKind::ReluElementwise
| OpKind::StepElementwise
| OpKind::RecipElementwise
| OpKind::AbsElementwise
| OpKind::FloorElementwise
| OpKind::CeilElementwise
| OpKind::RoundElementwise
| OpKind::SignElementwise
| OpKind::ErfElementwise
| OpKind::GeluElementwise
| OpKind::RsqrtElementwise
| OpKind::Affine
| OpKind::ClampElementwise
| OpKind::PowIElementwise => vec![t, t],
OpKind::SumReduce
| OpKind::MaxReduce
| OpKind::MinReduce
| OpKind::MeanReduce
| OpKind::ReduceSumTo
| OpKind::ReduceMaxTo => vec![t, t],
OpKind::FlashAttn => vec![t, t, t, t],
_ => return None,
})
}
fn prepare_direct_call_inputs(op: OpKind, size: &OpSize) -> Option<PreparedDirectCall> {
use fuel_cpu_backend::CpuStorageBytes;
use fuel_dispatch::kernel::OpParams;
use fuel_ir::Layout;
use fuel_memory::{BackendStorage, Storage};
let make_storage = |bytes: CpuStorageBytes| {
Arc::new(RwLock::new(Storage::new(
BackendStorage::Cpu(bytes),
DType::F32,
)))
};
match (op, *size) {
(OpKind::MatMul, OpSize::MatMul { m, n, k }) => {
let a_data: Vec<f32> = (0..(m * k)).map(|i| ((i as f32) * 1.3e-3).sin()).collect();
let b_data: Vec<f32> = (0..(k * n)).map(|i| ((i as f32) * 1.7e-3).cos()).collect();
let lhs = make_storage(CpuStorageBytes::from_slice(&a_data));
let rhs = make_storage(CpuStorageBytes::from_slice(&b_data));
let out = make_storage(CpuStorageBytes::from_zero_bytes(
m * n * std::mem::size_of::<f32>(),
));
let layouts = vec![
Layout::contiguous(Shape::from_dims(&[m, k])),
Layout::contiguous(Shape::from_dims(&[k, n])),
Layout::contiguous(Shape::from_dims(&[m, n])),
];
let op_params = OpParams::Matmul {
lhs_batch_dims: Vec::new(),
rhs_batch_dims: Vec::new(),
m,
n,
k,
m_compute: fuel_dispatch::kernel::MatmulM::All,
};
Some(PreparedDirectCall {
inputs: vec![lhs, rhs],
output: out,
layouts,
op_params,
})
}
(op, OpSize::Elementwise(n)) if is_binary_elementwise(op) => {
let (a_data, b_data) = binary_inputs(op, n);
let lhs = make_storage(CpuStorageBytes::from_slice(&a_data));
let rhs = make_storage(CpuStorageBytes::from_slice(&b_data));
let out = make_storage(CpuStorageBytes::from_zero_bytes(
n * std::mem::size_of::<f32>(),
));
let shape = Shape::from_dims(&[n]);
let layouts = vec![
Layout::contiguous(shape.clone()),
Layout::contiguous(shape.clone()),
Layout::contiguous(shape),
];
Some(PreparedDirectCall {
inputs: vec![lhs, rhs],
output: out,
layouts,
op_params: OpParams::None,
})
}
(op, OpSize::Elementwise(n)) if is_unary_elementwise(op) => {
let raw = unary_input(n);
let needs_nonzero = matches!(
op,
OpKind::SqrtElementwise
| OpKind::LogElementwise
| OpKind::RecipElementwise
| OpKind::RsqrtElementwise,
);
let data: Vec<f32> = if needs_nonzero {
raw.into_iter().map(|x| x + 1.5).collect()
} else {
raw
};
let inp = make_storage(CpuStorageBytes::from_slice(&data));
let out = make_storage(CpuStorageBytes::from_zero_bytes(
n * std::mem::size_of::<f32>(),
));
let shape = Shape::from_dims(&[n]);
let layouts = vec![Layout::contiguous(shape.clone()), Layout::contiguous(shape)];
Some(PreparedDirectCall {
inputs: vec![inp],
output: out,
layouts,
op_params: OpParams::None,
})
}
(op, OpSize::Elementwise(n)) if is_scalar_op(op) => {
let data = unary_input(n);
let inp = make_storage(CpuStorageBytes::from_slice(&data));
let out = make_storage(CpuStorageBytes::from_zero_bytes(
n * std::mem::size_of::<f32>(),
));
let shape = Shape::from_dims(&[n]);
let layouts = vec![Layout::contiguous(shape.clone()), Layout::contiguous(shape)];
let op_params = match op {
OpKind::Affine => OpParams::Affine { mul: 2.0, add: 0.0 },
OpKind::ClampElementwise => OpParams::Clamp {
min: -0.5,
max: 0.5,
},
OpKind::PowIElementwise => OpParams::PowI { exp: 3 },
_ => unreachable!(),
};
Some(PreparedDirectCall {
inputs: vec![inp],
output: out,
layouts,
op_params,
})
}
_ => None,
}
}
fn read_output_f32(out: &Arc<RwLock<fuel_memory::Storage>>) -> Vec<f32> {
use fuel_memory::BackendStorage;
let g = out.read().expect("output storage lock");
match &g.inner {
BackendStorage::Cpu(c) => c
.as_slice::<f32>()
.expect("cpu storage as f32 slice")
.to_vec(),
#[allow(unreachable_patterns)]
_ => Vec::new(),
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum OpSize {
MatMul { m: usize, n: usize, k: usize },
Elementwise(usize),
Reduce { rows: usize, cols: usize },
ReduceTo { rows: usize, cols: usize },
Attention {
b: usize,
hq: usize,
hkv: usize,
d: usize,
k_len: usize,
},
}
impl OpSize {
fn total_elements(&self) -> usize {
match *self {
OpSize::MatMul { m, n, k: _ } => m * n,
OpSize::Elementwise(n) => n,
OpSize::Reduce { rows, cols } => rows * cols,
OpSize::ReduceTo { rows, cols } => rows * cols,
OpSize::Attention { b, hq, d, .. } => b * hq * d,
}
}
fn input_elements(&self, op: OpKind) -> usize {
match *self {
OpSize::MatMul { m, n, k } => m * k + k * n,
OpSize::Elementwise(n) => {
if is_binary_elementwise(op) {
2 * n
} else {
n
}
}
OpSize::Reduce { rows, cols } => rows * cols,
OpSize::ReduceTo { rows, cols } => rows * cols,
OpSize::Attention {
b,
hq,
hkv,
d,
k_len,
} => b * hq * 1 * d + 2 * (b * hkv * k_len * d),
}
}
}
fn make_leaf(dtype: DType, data: Vec<f32>, shape: Shape) -> crate::lazy::Tensor {
use crate::lazy::Tensor;
let dev = crate::Device::cpu();
let leaf = |t: std::result::Result<Tensor, fuel_ir::Error>| {
t.expect(
"make_leaf: data length must match shape element count. Both are caller-supplied, so this function cannot prove it -- the panic is the judge's error channel and is caught by the catch_unwind in Judge::run",
)
};
match dtype {
DType::F32 => leaf(Tensor::from_f32(data, shape, &dev)),
DType::F16 => leaf(Tensor::from_f16(
data.iter()
.map(|&x| half::f16::from_f32(x))
.collect::<Vec<_>>(),
shape,
&dev,
)),
DType::BF16 => leaf(Tensor::from_bf16(
data.iter()
.map(|&x| half::bf16::from_f32(x))
.collect::<Vec<_>>(),
shape,
&dev,
)),
other => panic!("build_input_graph: unsupported profiled dtype {other:?}"),
}
}
fn make_const_like(
a: &crate::lazy::Tensor,
dtype: DType,
data: Vec<f32>,
shape: Shape,
) -> crate::lazy::Tensor {
let cl = |t: std::result::Result<crate::lazy::Tensor, fuel_ir::Error>| {
t.expect(
"make_const_like: data length must match shape element count. Both are caller-supplied, so this function cannot prove it -- the panic is the judge's error channel and is caught by the catch_unwind in Judge::run",
)
};
match dtype {
DType::F32 => cl(a.const_f32_like(data, shape)),
DType::F16 => cl(a.const_f16_like(
data.iter()
.map(|&x| half::f16::from_f32(x))
.collect::<Vec<_>>(),
shape,
)),
DType::BF16 => cl(a.const_bf16_like(
data.iter()
.map(|&x| half::bf16::from_f32(x))
.collect::<Vec<_>>(),
shape,
)),
other => panic!("build_input_graph: unsupported profiled dtype {other:?}"),
}
}
fn build_input_graph(op: OpKind, dtype: DType, size: &OpSize) -> crate::lazy::Tensor {
match (op, *size) {
(OpKind::MatMul, OpSize::MatMul { m, n, k }) => {
let a_data: Vec<f32> = (0..(m * k)).map(|i| ((i as f32) * 1.3e-3).sin()).collect();
let b_data: Vec<f32> = (0..(k * n)).map(|i| ((i as f32) * 1.7e-3).cos()).collect();
let a = make_leaf(dtype, a_data, Shape::from_dims(&[m, k]));
let b = make_const_like(&a, dtype, b_data, Shape::from_dims(&[k, n]));
a.matmul(&b).unwrap()
}
(op, OpSize::Elementwise(n)) if is_binary_elementwise(op) => {
let (a_data, b_data) = binary_inputs(op, n);
let a = make_leaf(dtype, a_data, Shape::from_dims(&[n]));
let b = make_const_like(&a, dtype, b_data, Shape::from_dims(&[n]));
apply_binary(op, &a, &b)
}
(op, OpSize::Reduce { rows, cols }) if is_reduction(op) => {
let n = rows * cols;
let data: Vec<f32> = (0..n).map(|i| ((i as f32) * 1.7e-3).sin()).collect();
let a = make_leaf(dtype, data, Shape::from_dims(&[rows, cols]));
apply_reduction(op, &a)
}
(op, OpSize::ReduceTo { rows, cols }) if is_reduce_to(op) => {
let n = rows * cols;
let data: Vec<f32> = (0..n).map(|i| ((i as f32) * 1.7e-3).sin()).collect();
let a = make_leaf(dtype, data, Shape::from_dims(&[rows, cols]));
let target = Shape::from_dims(&[1, cols]);
match op {
OpKind::ReduceSumTo => a.reduce_sum_to(target),
OpKind::ReduceMaxTo => a.reduce_max_to(target),
_ => unreachable!(),
}
}
(op, OpSize::Elementwise(n)) if is_scalar_op(op) => {
let data: Vec<f32> = unary_input(n);
let a = make_leaf(dtype, data, Shape::from_dims(&[n]));
match op {
OpKind::Affine => a.mul_scalar(2.0),
OpKind::ClampElementwise => a.clamp(-0.5, 0.5),
OpKind::PowIElementwise => a.powi(3),
_ => unreachable!(),
}
}
(op, OpSize::Elementwise(n)) if is_unary_elementwise(op) => {
let raw = unary_input(n);
let needs_nonzero = matches!(
op,
OpKind::SqrtElementwise
| OpKind::LogElementwise
| OpKind::RecipElementwise
| OpKind::RsqrtElementwise,
);
let data: Vec<f32> = if needs_nonzero {
raw.into_iter().map(|x| x + 1.5).collect()
} else {
raw
};
let a = make_leaf(dtype, data, Shape::from_dims(&[n]));
apply_unary(op, &a)
}
(
OpKind::FlashAttn,
OpSize::Attention {
b,
hq,
hkv,
d,
k_len,
},
) => {
let scale = 1.0 / (d as f32).sqrt();
let q_elems = b * hq * 1 * d;
let kv_elems = b * hkv * k_len * d;
let q_data: Vec<f32> = (0..q_elems).map(|i| ((i as f32) * 1.3e-3).sin()).collect();
let k_data: Vec<f32> = (0..kv_elems).map(|i| ((i as f32) * 1.7e-3).cos()).collect();
let v_data: Vec<f32> = (0..kv_elems)
.map(|i| ((i as f32) * 0.9e-3).sin() + 1.0)
.collect();
let q = make_leaf(dtype, q_data, Shape::from_dims(&[b, hq, 1, d]));
let k = make_const_like(&q, dtype, k_data, Shape::from_dims(&[b, hkv, k_len, d]));
let v = make_const_like(&q, dtype, v_data, Shape::from_dims(&[b, hkv, k_len, d]));
q.flash_attn(&k, &v, None, scale, true, None, None, None)
.expect("judge: flash_attn shape/GQA invariant")
}
(op, sz) => panic!("build_input_graph: op {op:?} size {sz:?} not implemented"),
}
}
fn is_scalar_op(op: OpKind) -> bool {
matches!(
op,
OpKind::Affine | OpKind::ClampElementwise | OpKind::PowIElementwise,
)
}
fn is_reduce_to(op: OpKind) -> bool {
matches!(op, OpKind::ReduceSumTo | OpKind::ReduceMaxTo)
}
fn is_decode_softmax_elementwise(op: OpKind) -> bool {
matches!(
op,
OpKind::SubElementwise | OpKind::ExpElementwise | OpKind::DivElementwise,
)
}
fn is_decode_softmax_reduction(op: OpKind) -> bool {
matches!(op, OpKind::MaxReduce | OpKind::SumReduce)
}
fn is_reduction(op: OpKind) -> bool {
matches!(
op,
OpKind::SumReduce | OpKind::MaxReduce | OpKind::MinReduce | OpKind::MeanReduce,
)
}
fn apply_reduction(op: OpKind, a: &crate::lazy::Tensor) -> crate::lazy::Tensor {
let last_dim = a.rank() - 1;
match op {
OpKind::SumReduce => a.sum_dim(last_dim).unwrap(),
OpKind::MaxReduce => a.max_dim(last_dim).unwrap(),
OpKind::MinReduce => a.min_dim(last_dim).unwrap(),
OpKind::MeanReduce => a.mean_dim(last_dim).unwrap(),
_ => unreachable!("apply_reduction called on non-reduction OpKind {op:?}"),
}
}
fn is_binary_elementwise(op: OpKind) -> bool {
matches!(
op,
OpKind::AddElementwise
| OpKind::SubElementwise
| OpKind::MulElementwise
| OpKind::DivElementwise
| OpKind::MaximumElementwise
| OpKind::MinimumElementwise
| OpKind::PowElementwise
| OpKind::RemElementwise,
)
}
fn binary_inputs(op: OpKind, n: usize) -> (Vec<f32>, Vec<f32>) {
let mut a: Vec<f32> = (0..n).map(|i| ((i as f32) * 2.1e-3).sin()).collect();
let mut b: Vec<f32> = (0..n).map(|i| ((i as f32) * 1.9e-3).cos()).collect();
if matches!(op, OpKind::DivElementwise) {
for x in &mut b {
*x += 1.5;
}
}
if matches!(op, OpKind::PowElementwise) {
for x in &mut a {
*x += 1.5;
}
for x in &mut b {
*x += 1.5;
}
}
if matches!(op, OpKind::RemElementwise) {
for x in &mut b {
*x += 1.5;
}
}
(a, b)
}
fn apply_binary(
op: OpKind,
a: &crate::lazy::Tensor,
b: &crate::lazy::Tensor,
) -> crate::lazy::Tensor {
match op {
OpKind::AddElementwise => a.add(b).unwrap(),
OpKind::SubElementwise => a.sub(b).unwrap(),
OpKind::MulElementwise => a.mul(b).unwrap(),
OpKind::DivElementwise => a.div(b).unwrap(),
OpKind::MaximumElementwise => a.maximum(b).unwrap(),
OpKind::MinimumElementwise => a.minimum(b).unwrap(),
OpKind::PowElementwise => a.pow(b).expect("judge: pow shape/dtype invariant"),
OpKind::RemElementwise => a.rem(b).expect("judge: rem shape/dtype invariant"),
_ => unreachable!("apply_binary called on non-binary OpKind {op:?}"),
}
}
fn is_unary_elementwise(op: OpKind) -> bool {
matches!(
op,
OpKind::NegElementwise
| OpKind::SqrElementwise
| OpKind::SqrtElementwise
| OpKind::ExpElementwise
| OpKind::LogElementwise
| OpKind::SinElementwise
| OpKind::CosElementwise
| OpKind::TanhElementwise
| OpKind::SigmoidElementwise
| OpKind::SiluElementwise
| OpKind::GeluTanhElementwise
| OpKind::ReluElementwise
| OpKind::StepElementwise
| OpKind::RecipElementwise
| OpKind::AbsElementwise
| OpKind::FloorElementwise
| OpKind::CeilElementwise
| OpKind::RoundElementwise
| OpKind::SignElementwise
| OpKind::ErfElementwise
| OpKind::GeluElementwise
| OpKind::RsqrtElementwise,
)
}
fn unary_input(n: usize) -> Vec<f32> {
(0..n).map(|i| ((i as f32) * 2.1e-3).sin()).collect()
}
fn apply_unary(op: OpKind, a: &crate::lazy::Tensor) -> crate::lazy::Tensor {
match op {
OpKind::NegElementwise => a.neg(),
OpKind::SqrElementwise => a.sqr(),
OpKind::SqrtElementwise => a.sqrt(),
OpKind::ExpElementwise => a.exp(),
OpKind::LogElementwise => a.log(),
OpKind::TanhElementwise => a.tanh(),
OpKind::SigmoidElementwise => a.sigmoid(),
OpKind::SiluElementwise => a.silu(),
OpKind::GeluTanhElementwise => a.gelu_tanh(),
OpKind::ReluElementwise => a.relu(),
OpKind::SinElementwise => a.sin(),
OpKind::CosElementwise => a.cos(),
OpKind::StepElementwise => a.step(),
OpKind::RecipElementwise => a.recip(),
OpKind::AbsElementwise => a.abs(),
OpKind::FloorElementwise => a.floor(),
OpKind::CeilElementwise => a.ceil(),
OpKind::RoundElementwise => a.round(),
OpKind::SignElementwise => a.sign(),
OpKind::ErfElementwise => a.erf(),
OpKind::GeluElementwise => a.gelu(),
OpKind::RsqrtElementwise => a.rsqrt(),
_ => unreachable!("apply_unary called on non-unary OpKind {op:?}"),
}
}
fn max_rel_err(a: &[f32], b: &[f32]) -> f32 {
if a.len() != b.len() {
return f32::INFINITY;
}
let mut worst = 0.0_f32;
for (&x, &y) in a.iter().zip(b.iter()) {
if !x.is_finite() || !y.is_finite() {
return f32::INFINITY;
}
let denom = x.abs().max(y.abs()).max(f32::MIN_POSITIVE);
let rel = (x - y).abs() / denom;
if rel > worst {
worst = rel;
}
}
worst
}
const CONSENSUS_EPSILON: f32 = 1e-3;
#[derive(Debug)]
struct CellRun {
backend: BackendId,
device_index: u32,
output: Vec<f32>,
latency_ns: u64,
iterations: u32,
kernel_source: String,
kernel_revision_hash: u64,
}
#[allow(clippy::needless_range_loop)]
fn compute_pairwise_consensus(runs: &[CellRun]) -> Vec<usize> {
let n = runs.len();
if n == 0 {
return Vec::new();
}
if n == 1 {
return vec![0];
}
let mut agree = vec![vec![false; n]; n];
for i in 0..n {
agree[i][i] = true;
for j in (i + 1)..n {
let close = max_rel_err(&runs[i].output, &runs[j].output) < CONSENSUS_EPSILON;
agree[i][j] = close;
agree[j][i] = close;
}
}
let mut best: Vec<usize> = vec![0];
for i in 0..n {
let mut cluster = vec![i];
for j in 0..n {
if j == i {
continue;
}
if cluster.iter().all(|&k| agree[j][k]) {
cluster.push(j);
}
}
cluster.sort_unstable();
if cluster.len() > best.len() {
best = cluster;
}
}
best
}
fn max_rel_err_vs_fixture(out: &[f32], fixture: &CorrectnessFixture) -> f32 {
let bytes: Vec<u8> = out.iter().flat_map(|x| x.to_le_bytes()).collect();
match validate_against_fixture(fixture, &bytes) {
Ok(()) => 0.0,
Err(CorrectnessDrift::OutOfTolerance { rel_err, .. }) => rel_err as f32,
Err(CorrectnessDrift::LengthMismatch { .. }) => f32::INFINITY,
Err(CorrectnessDrift::NonFinite { .. }) => f32::INFINITY,
Err(CorrectnessDrift::UnvalidatableDType { .. }) => f32::INFINITY,
}
}
fn load_fixtures_recursive(
root: &Path,
) -> Result<HashMap<(OpKind, DType, SizeClass), Vec<CorrectnessFixture>>> {
let mut map: HashMap<(OpKind, DType, SizeClass), Vec<CorrectnessFixture>> = HashMap::new();
let mut visit = |p: &Path| -> Result<()> {
let raw = std::fs::read_to_string(p).map_err(|e| {
fuel_ir::error::Error::Msg(format!(
"judge: failed to read fixture file {}: {e}",
p.display(),
))
})?;
let file: FixtureFile = serde_json::from_str(&raw).map_err(|e| {
fuel_ir::error::Error::Msg(format!(
"judge: failed to parse fixture file {} as FixtureFile: {e}",
p.display(),
))
})?;
if file.version != FIXTURE_FILE_VERSION {
eprintln!(
"judge: skipping fixture file {} — version {} does not match \
current FIXTURE_FILE_VERSION ({}); regenerate via fuel-capture-fixtures",
p.display(),
file.version,
FIXTURE_FILE_VERSION,
);
return Ok(());
}
for f in file.fixtures {
let key = (f.op, f.dtype, f.size_class);
map.entry(key).or_default().push(f);
}
Ok(())
};
fn walk(dir: &Path, visit: &mut dyn FnMut(&Path) -> Result<()>) -> Result<()> {
let entries = std::fs::read_dir(dir).map_err(|e| {
fuel_ir::error::Error::Msg(format!(
"judge: failed to read fixture dir {}: {e}",
dir.display(),
))
})?;
for entry in entries {
let entry = entry.map_err(|e| {
fuel_ir::error::Error::Msg(format!(
"judge: failed reading dir entry in {}: {e}",
dir.display(),
))
})?;
let p = entry.path();
if p.is_dir() {
walk(&p, visit)?;
} else if p.extension().and_then(|s| s.to_str()) == Some("json") {
visit(&p)?;
}
}
Ok(())
}
if root.is_file() {
visit(root)?;
} else if root.is_dir() {
walk(root, &mut visit)?;
} else {
return Err(fuel_ir::error::Error::Msg(format!(
"judge: fixture path {} does not exist",
root.display(),
)));
}
Ok(map)
}
fn max_rel_err_vs_consensus(runs: &[CellRun], consensus: &[usize], self_idx: usize) -> f32 {
let peers: Vec<usize> = consensus
.iter()
.copied()
.filter(|&i| i != self_idx)
.collect();
if peers.is_empty() {
return 0.0;
}
let self_out = &runs[self_idx].output;
let mut worst = 0.0_f32;
for i in peers {
let e = max_rel_err(self_out, &runs[i].output);
if e > worst {
worst = e;
}
}
worst
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn size_class_matches_log2_floor() {
assert_eq!(SizeClass::from_elem_count(1).0, 0);
assert_eq!(SizeClass::from_elem_count(2).0, 1);
assert_eq!(SizeClass::from_elem_count(3).0, 1);
assert_eq!(SizeClass::from_elem_count(4).0, 2);
assert_eq!(SizeClass::from_elem_count(1 << 16).0, 16);
assert_eq!(SizeClass::from_elem_count(65_537).0, 16);
}
#[test]
fn size_class_zero_input_clamps_to_one() {
assert_eq!(SizeClass::from_elem_count(0).0, 0);
}
#[test]
fn op_kind_display_is_stable() {
assert_eq!(OpKind::MatMul.to_string(), "matmul");
assert_eq!(OpKind::AddElementwise.to_string(), "add");
}
#[test]
fn unvalidatable_dtype_fixture_ranks_as_failure_not_perfect() {
use fuel_correctness_fixtures::ToleranceBand;
let out = vec![0.0f32, 0.0f32];
let fixture = CorrectnessFixture {
op: OpKind::AddElementwise,
dtype: DType::F16,
size_class: SizeClass(2),
input_seed: 1,
input_hash: 0,
expected_output: vec![0u8; 8],
output_element_count: 4,
tolerance: ToleranceBand::F32_DEFAULT,
};
let verdict = max_rel_err_vs_fixture(&out, &fixture);
assert_eq!(
verdict,
f32::INFINITY,
"unvalidatable-dtype fixture must rank as failure (INFINITY), \
not 0.0 — a false 'perfect' certifies an unchecked kernel",
);
}
#[test]
fn cell_run_carries_attribution_or_is_dropped() {
struct StubRealizer {
src: Option<&'static str>,
rev: Option<u64>,
}
impl crate::factories::Realizer for StubRealizer {
fn realize_f32(&mut self, _tensor: &crate::lazy::Tensor) -> Result<Vec<f32>> {
Ok(vec![0.0; 8])
}
fn last_kernel_source(&self) -> Option<&'static str> {
self.src
}
fn last_kernel_revision(&self) -> Option<u64> {
self.rev
}
}
let judge = Judge {
iterations: 1,
warmup: 0,
size_plan_override: None,
fixtures: None,
};
let device = DeviceDescriptor {
backend: BackendId::Cpu,
device_index: 0,
hardware_sku: "test-cpu".into(),
vendor_id: 0,
device_id: 0,
compute_capability: None,
subgroup_width: None,
driver_version: String::new(),
total_memory_bytes: 0,
location: fuel_ir::DeviceLocation::Cpu,
};
let op = OpKind::AddElementwise;
let size = OpSize::Elementwise(8);
let mut reporting = StubRealizer {
src: Some("stub-sibling"),
rev: Some(0x00AB_CDEF),
};
let run = judge
.time_op_capturing(op, DType::F32, &size, &device, &mut reporting)
.expect("attributed dispatch produces a CellRun");
assert_eq!(run.kernel_source, "stub-sibling");
assert_eq!(run.kernel_revision_hash, 0x00AB_CDEF);
let mut silent = StubRealizer {
src: None,
rev: None,
};
assert!(
judge
.time_op_capturing(op, DType::F32, &size, &device, &mut silent)
.is_none(),
"unattributed dispatch must produce NO row, not a guessed/fabricated one",
);
}
#[test]
fn judge_profiles_cpu_on_small_matmul() {
let probe = ProbeReport::probe_all();
let judge = Judge {
iterations: 3,
warmup: 1,
size_plan_override: Some(vec![
(
OpKind::MatMul,
OpSize::MatMul {
m: 32,
n: 32,
k: 32,
},
),
(OpKind::AddElementwise, OpSize::Elementwise(1 << 10)),
]),
fixtures: None,
};
let report = judge.run(&probe);
assert_eq!(report.version, PROFILE_REPORT_VERSION);
assert!(report.entries.iter().any(|e| e.backend == BackendId::Cpu));
for e in report
.entries
.iter()
.filter(|e| e.backend == BackendId::Cpu)
{
assert!(
e.max_rel_error < 1e-3,
"cpu cell rel_err should stay tight ({e:?})"
);
assert!(e.latency_ns > 0);
}
}
#[test]
fn judge_profiles_all_unary_elementwise_ops() {
let probe = ProbeReport::probe_all();
let unary = [
OpKind::NegElementwise,
OpKind::SqrElementwise,
OpKind::SqrtElementwise,
OpKind::ExpElementwise,
OpKind::LogElementwise,
OpKind::SinElementwise,
OpKind::CosElementwise,
OpKind::TanhElementwise,
OpKind::SigmoidElementwise,
OpKind::SiluElementwise,
OpKind::GeluTanhElementwise,
OpKind::ReluElementwise,
OpKind::StepElementwise,
OpKind::RecipElementwise,
OpKind::AbsElementwise,
OpKind::FloorElementwise,
OpKind::CeilElementwise,
OpKind::RoundElementwise,
OpKind::SignElementwise,
OpKind::ErfElementwise,
OpKind::GeluElementwise,
OpKind::RsqrtElementwise,
];
let plan: Vec<_> = unary
.iter()
.map(|&op| (op, OpSize::Elementwise(1 << 8)))
.collect();
let judge = Judge {
iterations: 3,
warmup: 1,
size_plan_override: Some(plan),
fixtures: None,
};
let report = judge.run(&probe);
for &op in &unary {
let cpu_entries: Vec<_> = report
.entries
.iter()
.filter(|e| e.op == op && e.backend == BackendId::Cpu)
.collect();
assert!(
!cpu_entries.is_empty(),
"expected ≥1 cpu entry for {op}, got 0"
);
let e = cpu_entries[0];
assert!(
e.max_rel_error < 1e-3,
"cpu vs reference disagreement on {op}: rel_err={}",
e.max_rel_error
);
assert!(e.latency_ns > 0);
}
}
#[test]
fn judge_profiles_all_binary_elementwise_ops() {
let probe = ProbeReport::probe_all();
let binary = [
OpKind::AddElementwise,
OpKind::SubElementwise,
OpKind::MulElementwise,
OpKind::DivElementwise,
OpKind::MaximumElementwise,
OpKind::MinimumElementwise,
OpKind::PowElementwise,
OpKind::RemElementwise,
];
let plan: Vec<_> = binary
.iter()
.map(|&op| (op, OpSize::Elementwise(1 << 8)))
.collect();
let judge = Judge {
iterations: 3,
warmup: 1,
size_plan_override: Some(plan),
fixtures: None,
};
let report = judge.run(&probe);
for &op in &binary {
let cpu_entries: Vec<_> = report
.entries
.iter()
.filter(|e| e.op == op && e.backend == BackendId::Cpu)
.collect();
assert!(
!cpu_entries.is_empty(),
"expected >=1 cpu entry for {op}, got 0"
);
let e = cpu_entries[0];
assert!(
e.max_rel_error < 1e-3,
"cpu vs reference disagreement on {op}: rel_err={}",
e.max_rel_error
);
assert!(e.latency_ns > 0);
}
}
#[test]
fn judge_profiles_all_reductions() {
let probe = ProbeReport::probe_all();
let reduce = [
OpKind::SumReduce,
OpKind::MaxReduce,
OpKind::MinReduce,
OpKind::MeanReduce,
];
let plan: Vec<_> = reduce
.iter()
.map(|&op| (op, OpSize::Reduce { rows: 16, cols: 16 }))
.collect();
let judge = Judge {
iterations: 3,
warmup: 1,
size_plan_override: Some(plan),
fixtures: None,
};
let report = judge.run(&probe);
for &op in &reduce {
let cpu_entries: Vec<_> = report
.entries
.iter()
.filter(|e| e.op == op && e.backend == BackendId::Cpu)
.collect();
assert!(
!cpu_entries.is_empty(),
"expected >=1 cpu entry for {op}, got 0"
);
let e = cpu_entries[0];
assert!(
e.max_rel_error < 5e-3,
"cpu vs reference disagreement on {op}: rel_err={}",
e.max_rel_error
);
assert!(e.latency_ns > 0);
}
}
#[test]
fn judge_profiles_all_reduce_to() {
let probe = ProbeReport::probe_all();
let reduce_to = [OpKind::ReduceSumTo, OpKind::ReduceMaxTo];
let plan: Vec<_> = reduce_to
.iter()
.map(|&op| (op, OpSize::ReduceTo { rows: 16, cols: 16 }))
.collect();
let judge = Judge {
iterations: 3,
warmup: 1,
size_plan_override: Some(plan),
fixtures: None,
};
let report = judge.run(&probe);
for &op in &reduce_to {
let cpu_entries: Vec<_> = report
.entries
.iter()
.filter(|e| e.op == op && e.backend == BackendId::Cpu)
.collect();
assert!(
!cpu_entries.is_empty(),
"expected >=1 cpu entry for {op}, got 0"
);
let e = cpu_entries[0];
assert!(
e.max_rel_error < 5e-3,
"cpu vs reference disagreement on {op}: rel_err={}",
e.max_rel_error
);
assert!(e.latency_ns > 0);
}
}
#[test]
fn judge_profiles_all_scalar_ops() {
let probe = ProbeReport::probe_all();
let scalar = [
OpKind::Affine,
OpKind::ClampElementwise,
OpKind::PowIElementwise,
];
let plan: Vec<_> = scalar
.iter()
.map(|&op| (op, OpSize::Elementwise(1 << 8)))
.collect();
let judge = Judge {
iterations: 3,
warmup: 1,
size_plan_override: Some(plan),
fixtures: None,
};
let report = judge.run(&probe);
for &op in &scalar {
let cpu_entries: Vec<_> = report
.entries
.iter()
.filter(|e| e.op == op && e.backend == BackendId::Cpu)
.collect();
assert!(
!cpu_entries.is_empty(),
"expected >=1 cpu entry for {op}, got 0"
);
let e = cpu_entries[0];
assert!(
e.max_rel_error < 1e-3,
"cpu vs reference disagreement on {op}: rel_err={}",
e.max_rel_error
);
assert!(e.latency_ns > 0);
}
}
#[test]
fn size_plan_carries_decode_shaped_cells() {
let judge = Judge::default();
let mm = judge.size_plan(OpKind::MatMul);
let sc_of = |s: &OpSize| match *s {
OpSize::MatMul { m, n, k } => SizeClass::matmul(m, n, k),
_ => unreachable!("size_plan(MatMul) yields only MatMul sizes"),
};
let square_scs: Vec<SizeClass> = mm
.iter()
.filter(|s| matches!(s, OpSize::MatMul { m, n, .. } if m == n))
.map(sc_of)
.collect();
assert!(
!square_scs.is_empty(),
"the general square matmul ladder must be preserved",
);
let decode_mm: Vec<&OpSize> = mm
.iter()
.filter(|s| matches!(s, OpSize::MatMul { m, .. } if *m == 1))
.collect();
assert!(
!decode_mm.is_empty(),
"size_plan(MatMul) must include a seq_q=1 decode GEMV cell",
);
for d in &decode_mm {
let sc = sc_of(d);
assert!(
!square_scs.contains(&sc),
"decode matmul cell {d:?} SizeClass {sc:?} collides with a \
square cell — arm-0 costing would read the square latency",
);
}
for op in [OpKind::MaxReduce, OpKind::SumReduce] {
let plan = judge.size_plan(op);
assert!(
plan.iter()
.any(|s| matches!(s, OpSize::Reduce { cols, .. } if *cols >= 128)),
"size_plan({op}) must include a decode-row (wide-cols) reduction cell",
);
}
for op in [OpKind::MinReduce, OpKind::MeanReduce] {
let plan = judge.size_plan(op);
assert!(
plan.iter()
.all(|s| matches!(s, OpSize::Reduce { cols, .. } if *cols == 64)),
"size_plan({op}) is off the decode softmax path — no decode cell expected",
);
}
for op in [
OpKind::SubElementwise,
OpKind::ExpElementwise,
OpKind::DivElementwise,
] {
let plan = judge.size_plan(op);
assert!(
plan.len() > 3,
"size_plan({op}) must append decode cells beyond the 3-size base ladder",
);
}
assert_eq!(
judge.size_plan(OpKind::SinElementwise).len(),
3,
"an off-softmax-path elementwise op must keep only its 3-size base ladder",
);
}
#[test]
fn judge_measures_decode_matmul_distinct_from_square() {
let probe = ProbeReport::probe_all();
let judge = Judge {
iterations: 3,
warmup: 1,
size_plan_override: Some(vec![
(
OpKind::MatMul,
OpSize::MatMul {
m: 256,
n: 256,
k: 256,
},
),
(
OpKind::MatMul,
OpSize::MatMul {
m: 1,
n: 2048,
k: 2048,
},
),
]),
fixtures: None,
};
let report = judge.run(&probe);
let square_sc = SizeClass::matmul(256, 256, 256);
let decode_sc = SizeClass::matmul(1, 2048, 2048);
assert_ne!(
square_sc, decode_sc,
"decode + square matmul must key to different SizeClass",
);
let measured = |sc: SizeClass| {
report.entries.iter().any(|e| {
e.op == OpKind::MatMul
&& e.backend == BackendId::Cpu
&& e.size_class == sc
&& e.latency_ns > 0
})
};
assert!(measured(square_sc), "square matmul cell must be measured");
assert!(
measured(decode_sc),
"decode matmul cell must be measured with latency > 0 at its own SizeClass",
);
}
#[test]
fn ranker_lookup_hits_producer_decode_gemv_cell() {
use fuel_dispatch::ranker::JudgeOracle;
let probe = ProbeReport::probe_all();
let judge = Judge {
iterations: 3,
warmup: 1,
size_plan_override: Some(vec![(
OpKind::MatMul,
OpSize::MatMul {
m: 1,
n: 5632,
k: 2048,
},
)]),
fixtures: None,
};
let report = judge.run(&probe);
let lhs = Shape::from_dims(&[1, 2048]);
let rhs = Shape::from_dims(&[2048, 5632]);
let ranker_key = SizeClass::for_op(OpKind::MatMul, &[lhs, rhs]);
assert_ne!(
SizeClass::from_elem_count(1 * 2048), SizeClass::from_elem_count(1 * 5632), "sanity: non-square shape → the OLD scalar keys disagreed",
);
let cpu_entry = report
.entries
.iter()
.find(|e| {
e.op == OpKind::MatMul && e.backend == BackendId::Cpu && e.dtype == DType::F32
})
.expect("Judge produced a CPU f32 matmul entry for the GEMV");
assert_eq!(
cpu_entry.size_class, ranker_key,
"producer key must EQUAL the consumer's for_op key for the \
non-square GEMV (the reconciliation)",
);
let oracle = ProfileJudgeOracle::from_report(&report);
let hit = oracle.measured_latency_ns(
OpKind::MatMul,
DType::F32,
ranker_key,
BackendId::Cpu,
cpu_entry.kernel_source.as_str(),
cpu_entry.kernel_revision_hash,
);
assert!(
hit.is_some(),
"synthetic ranker lookup at the GEMV shape must HIT the \
produced cell (pre-v4 this missed)",
);
assert!(hit.unwrap() > 0, "measured latency must be positive");
}
#[test]
fn judge_profiles_f16_and_bf16_cells() {
let probe = ProbeReport::probe_all();
let judge = Judge {
iterations: 3,
warmup: 1,
size_plan_override: Some(vec![
(OpKind::AddElementwise, OpSize::Elementwise(1 << 8)),
(OpKind::MulElementwise, OpSize::Elementwise(1 << 8)),
(
OpKind::MatMul,
OpSize::MatMul {
m: 16,
n: 16,
k: 16,
},
),
]),
fixtures: None,
};
let report = judge.run(&probe);
for dtype in [DType::F16, DType::BF16] {
for op in [
OpKind::AddElementwise,
OpKind::MulElementwise,
OpKind::MatMul,
] {
let cells: Vec<_> = report
.entries
.iter()
.filter(|e| e.op == op && e.dtype == dtype && e.backend == BackendId::Cpu)
.collect();
assert!(
!cells.is_empty(),
"dtype-axis: expected ≥1 CPU {dtype:?} entry for {op}, got 0 \
(is the {dtype:?} kernel registered for this op?)",
);
for e in &cells {
assert_eq!(
e.dtype, dtype,
"entry must be keyed by the dtype it was measured at",
);
assert!(
e.latency_ns > 0,
"{op} {dtype:?} latency must be > 0, got {e:?}",
);
assert!(
e.max_rel_error.is_finite(),
"{op} {dtype:?} rel_err must be finite, got {}",
e.max_rel_error,
);
}
}
}
assert!(
report.entries.iter().any(|e| e.dtype == DType::F32
&& e.op == OpKind::AddElementwise
&& e.backend == BackendId::Cpu),
"dtype-axis: f32 coverage regressed — expected f32 AddElementwise entry",
);
let mut dtypes: Vec<DType> = report.entries.iter().map(|e| e.dtype).collect();
dtypes.sort_by_key(|d| format!("{d:?}"));
dtypes.dedup();
assert_eq!(
dtypes.len(),
PROFILED_DTYPES.len(),
"expected exactly the profiled dtype set {PROFILED_DTYPES:?}, saw {dtypes:?}",
);
for d in PROFILED_DTYPES {
assert!(
dtypes.contains(d),
"profiled dtype {d:?} missing from the report",
);
}
}
#[test]
fn judge_profiles_flash_attn_at_decode_shapes() {
let probe = ProbeReport::probe_all();
let small = OpSize::Attention {
b: 1,
hq: 32,
hkv: 4,
d: 64,
k_len: 128,
};
let cap = OpSize::Attention {
b: 1,
hq: 32,
hkv: 4,
d: 64,
k_len: 512,
};
let judge = Judge {
iterations: 3,
warmup: 1,
size_plan_override: Some(vec![(OpKind::FlashAttn, small), (OpKind::FlashAttn, cap)]),
fixtures: None,
};
let report = judge.run(&probe);
let sc_small = SizeClass::attention(32, 128, 64);
let sc_cap = SizeClass::attention(32, 512, 64);
assert_ne!(
sc_small, sc_cap,
"short + capacity decode attention must key to different SizeClass",
);
for (label, sc) in [("short", sc_small), ("capacity", sc_cap)] {
let hit = report.entries.iter().any(|e| {
e.op == OpKind::FlashAttn
&& e.backend == BackendId::Cpu
&& e.dtype == DType::F32
&& e.size_class == sc
&& e.latency_ns > 0
});
assert!(
hit,
"FlashAttn f32 {label} decode cell (SizeClass {sc:?}) must be \
measured with latency > 0 — arm-1 of the flash-vs-decomposed \
comparison",
);
}
let consumer_key = SizeClass::for_op(
OpKind::FlashAttn,
&[
Shape::from_dims(&[1, 32, 1, 64]),
Shape::from_dims(&[1, 4, 128, 64]),
Shape::from_dims(&[1, 4, 128, 64]),
],
);
assert_eq!(
consumer_key, sc_small,
"the flash node's for_op key must equal the producer's attention key",
);
for dtype in [DType::F16, DType::BF16] {
let hit = report.entries.iter().any(|e| {
e.op == OpKind::FlashAttn
&& e.backend == BackendId::Cpu
&& e.dtype == dtype
&& e.size_class == sc_small
&& e.latency_ns > 0
});
assert!(
hit,
"FlashAttn {dtype:?} short decode cell must be measured (the CPU \
flash kernel has an {dtype:?} variant)",
);
}
}
#[test]
fn dispatch_table_built_from_expanded_report_serves_multiple_kinds() {
use fuel_ir::dispatch::{Criterion, DispatchTable};
let probe = ProbeReport::probe_all();
let judge = Judge {
iterations: 3,
warmup: 1,
size_plan_override: Some(vec![
(
OpKind::MatMul,
OpSize::MatMul {
m: 32,
n: 32,
k: 32,
},
),
(OpKind::AddElementwise, OpSize::Elementwise(1 << 8)),
(OpKind::ReluElementwise, OpSize::Elementwise(1 << 8)),
(OpKind::SumReduce, OpSize::Reduce { rows: 16, cols: 16 }),
(OpKind::ReduceSumTo, OpSize::ReduceTo { rows: 16, cols: 16 }),
(OpKind::Affine, OpSize::Elementwise(1 << 8)),
]),
fixtures: None,
};
let report = judge.run(&probe);
let table = DispatchTable::build(&report);
let elementwise_kinds = [
OpKind::AddElementwise,
OpKind::ReluElementwise,
OpKind::SumReduce,
OpKind::ReduceSumTo,
OpKind::Affine,
];
for &op in &elementwise_kinds {
for &crit in &[
Criterion::Fastest,
Criterion::MostAccurate,
Criterion::Balanced,
] {
let pick = table.pick(op, DType::F32, SizeClass(8), crit);
assert!(
pick.is_some(),
"DispatchTable missing pick for {op}@2^8 / {crit:?}"
);
}
}
let matmul_pick = table.pick(
OpKind::MatMul,
DType::F32,
SizeClass::matmul(32, 32, 32),
Criterion::Fastest,
);
assert!(matmul_pick.is_some(), "DispatchTable missing matmul pick");
let keys = table.keys();
assert!(
keys.iter()
.any(|(op, _, sc)| *op == OpKind::ReluElementwise && sc.0 == 8)
);
assert!(
keys.iter()
.any(|(op, _, sc)| *op == OpKind::SumReduce && sc.0 == 8)
);
assert!(
keys.iter()
.any(|(op, _, sc)| *op == OpKind::ReduceSumTo && sc.0 == 8)
);
assert!(
keys.iter()
.any(|(op, _, sc)| *op == OpKind::Affine && sc.0 == 8)
);
}
#[test]
fn profile_report_save_load_roundtrip() {
let report = ProfileReport {
version: PROFILE_REPORT_VERSION,
entries: vec![ProfileEntry {
op: OpKind::MatMul,
dtype: DType::F32,
size_class: SizeClass(8),
backend: BackendId::Cpu,
device_index: 0,
latency_ns: 12_345,
iterations: 7,
max_rel_error: 1e-7,
kernel_source: String::new(),
kernel_revision_hash: 0,
device: test_equiv_key(BackendId::Cpu),
}],
};
let tmp = std::env::temp_dir().join(format!("fuel-judge-test-{}.json", std::process::id()));
report.save(&tmp).expect("save");
let loaded = ProfileReport::load(&tmp, &[test_equiv_key(BackendId::Cpu)])
.expect("load")
.expect("file exists");
assert_eq!(loaded, report);
let _ = std::fs::remove_file(&tmp);
}
#[test]
fn load_refuses_a_report_measured_on_foreign_hardware() {
use fuel_ir::probe::EquivalenceKey;
let device_x = EquivalenceKey {
backend: BackendId::Cuda,
vendor_id: 0x10de, device_id: 0x2820,
compute_capability: Some((8, 9)),
driver_version: "560.0".to_string(),
};
let device_y = EquivalenceKey {
backend: BackendId::Cuda,
vendor_id: 0x1002, device_id: 0x1234,
compute_capability: None,
driver_version: "24.0".to_string(),
};
assert_ne!(device_x, device_y);
let report = ProfileReport {
version: PROFILE_REPORT_VERSION,
entries: vec![ProfileEntry {
op: OpKind::MatMul,
dtype: DType::F32,
size_class: SizeClass(8),
backend: BackendId::Cuda,
device_index: 0,
latency_ns: 5_000,
iterations: 7,
max_rel_error: 1e-7,
kernel_source: String::new(),
kernel_revision_hash: 0,
device: device_x.clone(),
}],
};
let tmp =
std::env::temp_dir().join(format!("fuel-judge-foreign-{}.json", std::process::id()));
report.save(&tmp).expect("save");
let refused = ProfileReport::load(&tmp, std::slice::from_ref(&device_y)).expect("load ok");
assert!(
refused.is_none(),
"a report stamped on device X must be refused on a Y-only machine"
);
let admitted = ProfileReport::load(&tmp, std::slice::from_ref(&device_x)).expect("load ok");
assert_eq!(
admitted.expect("positive control: device X is present, report must load"),
report
);
let _ = std::fs::remove_file(&tmp);
}
#[test]
fn v4_report_yields_no_oracle_entries() {
let report = ProfileReport {
version: PROFILE_REPORT_VERSION,
entries: vec![ProfileEntry {
op: OpKind::MatMul,
dtype: DType::F32,
size_class: SizeClass(8),
backend: BackendId::Cpu,
device_index: 0,
latency_ns: 12_345,
iterations: 7,
max_rel_error: 1e-7,
kernel_source: String::new(),
kernel_revision_hash: 0,
device: test_equiv_key(BackendId::Cpu),
}],
};
let mut v: serde_json::Value = serde_json::to_value(&report).expect("to_value");
v["version"] = serde_json::json!(4);
for e in v["entries"].as_array_mut().unwrap() {
let obj = e.as_object_mut().unwrap();
obj.remove("device");
obj.remove("kernel_revision_hash");
}
let tmp = std::env::temp_dir().join(format!("fuel-judge-v4-{}.json", std::process::id()));
std::fs::write(&tmp, serde_json::to_vec(&v).unwrap()).expect("write");
let loaded = ProfileReport::load(&tmp, &[test_equiv_key(BackendId::Cpu)]);
assert!(
matches!(loaded, Ok(None)),
"a v4-shaped report must load as Ok(None) (version gate), got {loaded:?}"
);
let _ = std::fs::remove_file(&tmp);
}
#[test]
fn dispatched_alt_is_matched_on_source_and_revision() {
assert!(!is_dispatched_alt(
"portable-cpu",
0xAAAA,
"portable-cpu",
0xBBBB
));
assert!(is_dispatched_alt(
"portable-cpu",
0xBBBB,
"portable-cpu",
0xBBBB
));
assert!(!is_dispatched_alt("aocl", 0xBBBB, "portable-cpu", 0xBBBB));
assert!(is_dispatched_alt("", 0, "", 0));
assert!(!is_dispatched_alt("mkl", 0, "", 0));
}
#[test]
fn max_rel_err_matches_known_values() {
assert_eq!(max_rel_err(&[1.0, 2.0], &[1.0, 2.0]), 0.0);
let got = max_rel_err(&[1.0, 2.0], &[1.0, 2.001]);
assert!((got - 5.0e-4).abs() < 1e-6, "expected ~0.0005, got {got}",);
assert!(max_rel_err(&[1.0], &[1.0, 1.0]).is_infinite());
assert!(max_rel_err(&[f32::NAN], &[0.0]).is_infinite());
}
#[test]
fn judge_uses_fixture_when_provided() {
use fuel_correctness_fixtures::{CorrectnessFixture, ToleranceBand};
let probe = ProbeReport::probe_all();
let elem_count = 1 << 10;
let sc = SizeClass::from_elem_count(elem_count);
let zeros: Vec<u8> = vec![0u8; elem_count * 4];
let seed =
fuel_correctness_fixtures::capture::derive_seed(OpKind::AddElementwise, DType::F32, sc);
let input = fuel_correctness_fixtures::capture::deterministic_f32_input(
OpKind::AddElementwise,
2 * elem_count,
);
let input_hash = fuel_correctness_fixtures::capture::hash_f32_input(&input);
let bogus_fixture = CorrectnessFixture {
op: OpKind::AddElementwise,
dtype: DType::F32,
size_class: sc,
input_seed: seed,
input_hash,
expected_output: zeros,
output_element_count: elem_count,
tolerance: ToleranceBand {
max_relative: 1e-9,
max_absolute: 1e-12,
},
};
let mut map: HashMap<(OpKind, DType, SizeClass), Vec<CorrectnessFixture>> = HashMap::new();
map.insert(
(
OpKind::AddElementwise,
DType::F32,
SizeClass::from_elem_count(elem_count),
),
vec![bogus_fixture],
);
let judge = Judge {
iterations: 3,
warmup: 1,
size_plan_override: Some(vec![(
OpKind::AddElementwise,
OpSize::Elementwise(elem_count),
)]),
fixtures: Some(map),
};
let report = judge.run(&probe);
let cpu_entries: Vec<_> = report
.entries
.iter()
.filter(|e| {
e.op == OpKind::AddElementwise
&& e.dtype == DType::F32
&& e.backend == BackendId::Cpu
})
.collect();
assert!(
!cpu_entries.is_empty(),
"expected ≥1 cpu f32 entry for AddElementwise, got 0"
);
for e in &cpu_entries {
assert!(
e.max_rel_error >= 1.0,
"expected fixture-derived rel_err ≥1.0 (vs all-zeros fixture), got {} for entry {e:?}",
e.max_rel_error
);
}
}
#[test]
fn judge_with_fixtures_from_loads_json_file() {
use fuel_correctness_fixtures::{
CorrectnessFixture, FIXTURE_FILE_VERSION, FixtureFile, ToleranceBand,
};
let fixture = CorrectnessFixture {
op: OpKind::MatMul,
dtype: DType::F32,
size_class: SizeClass(10),
input_seed: 42,
input_hash: 0xdeadbeef,
expected_output: vec![0u8; 16],
output_element_count: 4,
tolerance: ToleranceBand::F32_DEFAULT,
};
let file = FixtureFile {
version: FIXTURE_FILE_VERSION,
fixtures: vec![fixture],
};
let dir =
std::env::temp_dir().join(format!("fuel-judge-fixture-load-{}", std::process::id(),));
let _ = std::fs::create_dir_all(&dir);
let path = dir.join("matmul_f32.json");
std::fs::write(&path, serde_json::to_string(&file).unwrap()).unwrap();
let judge = Judge::with_fixtures_from(&dir).expect("load");
let map = judge.fixtures.as_ref().expect("fixtures populated");
let key = (OpKind::MatMul, DType::F32, SizeClass(10));
let bucket = map.get(&key).expect("matmul fixture in bucket");
assert_eq!(bucket.len(), 1);
assert_eq!(bucket[0].input_seed, 42);
let judge2 = Judge::with_fixtures_from(&path).expect("load file");
let map2 = judge2.fixtures.as_ref().expect("fixtures populated");
assert!(map2.contains_key(&key));
let _ = std::fs::remove_dir_all(&dir);
}
#[test]
fn judge_load_skips_mismatched_version() {
use fuel_correctness_fixtures::{CorrectnessFixture, FixtureFile, ToleranceBand};
let good_fixture = CorrectnessFixture {
op: OpKind::MatMul,
dtype: DType::F32,
size_class: SizeClass(10),
input_seed: 1,
input_hash: 2,
expected_output: vec![0u8; 16],
output_element_count: 4,
tolerance: ToleranceBand::F32_DEFAULT,
};
let bad_version_file = FixtureFile {
version: FIXTURE_FILE_VERSION + 999,
fixtures: vec![good_fixture.clone()],
};
let good_version_file = FixtureFile {
version: FIXTURE_FILE_VERSION,
fixtures: vec![CorrectnessFixture {
op: OpKind::AddElementwise,
..good_fixture
}],
};
let dir = std::env::temp_dir()
.join(format!("fuel-judge-fixture-version-{}", std::process::id(),));
let _ = std::fs::create_dir_all(&dir);
let bad_path = dir.join("matmul_f32.json");
let good_path = dir.join("add_f32.json");
std::fs::write(&bad_path, serde_json::to_string(&bad_version_file).unwrap()).unwrap();
std::fs::write(
&good_path,
serde_json::to_string(&good_version_file).unwrap(),
)
.unwrap();
let judge = Judge::with_fixtures_from(&dir).expect("load");
let map = judge.fixtures.as_ref().expect("fixtures populated");
assert!(
!map.contains_key(&(OpKind::MatMul, DType::F32, SizeClass(10))),
"expected mismatched-version file to be skipped",
);
assert!(
map.contains_key(&(OpKind::AddElementwise, DType::F32, SizeClass(10))),
"expected good-version file to be loaded",
);
let _ = std::fs::remove_dir_all(&dir);
}
#[test]
fn lookup_fixture_skips_seed_mismatch() {
use fuel_correctness_fixtures::{CorrectnessFixture, ToleranceBand};
let op = OpKind::AddElementwise;
let dtype = DType::F32;
let elem_count = 1 << 10;
let sc = SizeClass::from_elem_count(elem_count);
let input = fuel_correctness_fixtures::capture::deterministic_f32_input(op, 2 * elem_count);
let input_hash = fuel_correctness_fixtures::capture::hash_f32_input(&input);
let real_seed = fuel_correctness_fixtures::capture::derive_seed(op, dtype, sc);
let wrong_seed = real_seed.wrapping_add(1);
let fixture = CorrectnessFixture {
op,
dtype,
size_class: sc,
input_seed: wrong_seed,
input_hash,
expected_output: vec![0u8; elem_count * 4],
output_element_count: elem_count,
tolerance: ToleranceBand::F32_DEFAULT,
};
let mut map: HashMap<(OpKind, DType, SizeClass), Vec<CorrectnessFixture>> = HashMap::new();
map.insert((op, dtype, sc), vec![fixture]);
let judge = Judge {
fixtures: Some(map),
..Judge::default()
};
let got = judge.lookup_fixture(op, dtype, sc, elem_count, 2 * elem_count);
assert!(
got.is_none(),
"fixture with mismatched seed should be skipped (got {got:?})",
);
}
#[test]
fn lookup_fixture_skips_hash_mismatch() {
use fuel_correctness_fixtures::{CorrectnessFixture, ToleranceBand};
let op = OpKind::AddElementwise;
let dtype = DType::F32;
let elem_count = 1 << 10;
let sc = SizeClass::from_elem_count(elem_count);
let real_seed = fuel_correctness_fixtures::capture::derive_seed(op, dtype, sc);
let input = fuel_correctness_fixtures::capture::deterministic_f32_input(op, 2 * elem_count);
let real_hash = fuel_correctness_fixtures::capture::hash_f32_input(&input);
let wrong_hash = real_hash.wrapping_add(0xfeed_face);
let fixture = CorrectnessFixture {
op,
dtype,
size_class: sc,
input_seed: real_seed,
input_hash: wrong_hash,
expected_output: vec![0u8; elem_count * 4],
output_element_count: elem_count,
tolerance: ToleranceBand::F32_DEFAULT,
};
let mut map: HashMap<(OpKind, DType, SizeClass), Vec<CorrectnessFixture>> = HashMap::new();
map.insert((op, dtype, sc), vec![fixture]);
let judge = Judge {
fixtures: Some(map),
..Judge::default()
};
let got = judge.lookup_fixture(op, dtype, sc, elem_count, 2 * elem_count);
assert!(
got.is_none(),
"fixture with drifted input_hash should be skipped (got {got:?})",
);
}
#[test]
fn lookup_fixture_accepts_triple_agreement() {
use fuel_correctness_fixtures::{CorrectnessFixture, ToleranceBand};
let op = OpKind::AddElementwise;
let dtype = DType::F32;
let elem_count = 1 << 10;
let sc = SizeClass::from_elem_count(elem_count);
let seed = fuel_correctness_fixtures::capture::derive_seed(op, dtype, sc);
let input = fuel_correctness_fixtures::capture::deterministic_f32_input(op, 2 * elem_count);
let hash = fuel_correctness_fixtures::capture::hash_f32_input(&input);
let fixture = CorrectnessFixture {
op,
dtype,
size_class: sc,
input_seed: seed,
input_hash: hash,
expected_output: vec![0u8; elem_count * 4],
output_element_count: elem_count,
tolerance: ToleranceBand::F32_DEFAULT,
};
let mut map: HashMap<(OpKind, DType, SizeClass), Vec<CorrectnessFixture>> = HashMap::new();
map.insert((op, dtype, sc), vec![fixture]);
let judge = Judge {
fixtures: Some(map),
..Judge::default()
};
let got = judge.lookup_fixture(op, dtype, sc, elem_count, 2 * elem_count);
assert!(got.is_some(), "triple-agreement fixture should be returned");
}
}