#![allow(
clippy::panic,
clippy::unwrap_used,
clippy::expect_used,
clippy::as_conversions,
clippy::cast_possible_truncation,
clippy::cast_precision_loss,
clippy::indexing_slicing,
// The module docs above are prose about toolchain behaviour, quoting the
// Unstable Book and naming upstream issues. Backticking "ThreadSanitizer"
// and the bracketed quotation would hurt readability more than it helps.
clippy::doc_markdown
)]
use anamnesis::{RememberOptions, TargetDtype, parse_bytes};
const TENSORS: usize = 17;
const ELEMENTS: usize = 262_144;
const BUDGETS: [usize; 4] = [1, 2, 4, 8];
fn build_fp8_fixture() -> Vec<u8> {
let mut header = serde_json::Map::new();
let mut data: Vec<u8> = Vec::new();
for i in 0..TENSORS {
let w_off = data.len();
data.extend((0..ELEMENTS).map(|j| (j.wrapping_mul(2_654_435_761) & 0xFF) as u8));
let mut w = serde_json::Map::new();
w.insert("dtype".into(), "F8_E4M3".into());
w.insert("shape".into(), serde_json::json!([ELEMENTS]));
w.insert(
"data_offsets".into(),
serde_json::json!([w_off, data.len()]),
);
header.insert(format!("layer.{i}.weight"), w.into());
let s_off = data.len();
data.extend_from_slice(&0.125_f32.to_le_bytes());
let mut s = serde_json::Map::new();
s.insert("dtype".into(), "F32".into());
s.insert("shape".into(), serde_json::json!([1]));
s.insert(
"data_offsets".into(),
serde_json::json!([s_off, data.len()]),
);
header.insert(format!("layer.{i}.weight_scale"), s.into());
}
let header_json = serde_json::to_string(&header).expect("serialize header");
let mut out = Vec::with_capacity(8 + header_json.len() + data.len());
out.extend_from_slice(&(header_json.len() as u64).to_le_bytes());
out.extend_from_slice(header_json.as_bytes());
out.extend_from_slice(&data);
out
}
fn main() {
let bytes = build_fp8_fixture();
println!(
"tsan-harness: fixture {} tensors, {:.2} MiB quantised input",
TENSORS,
(TENSORS * ELEMENTS) as f64 / (1024.0 * 1024.0)
);
let model = parse_bytes(bytes).expect("parse synthetic FP8 fixture");
let baseline = model
.remember_to_bytes_with_options(TargetDtype::BF16, RememberOptions::new().with_threads(1))
.expect("sequential dequant");
println!("tsan-harness: baseline {} bytes", baseline.len());
let mut failures = 0usize;
for threads in BUDGETS {
let out = model
.remember_to_bytes_with_options(
TargetDtype::BF16,
RememberOptions::new().with_threads(threads),
)
.expect("threaded dequant");
if out == baseline {
println!("tsan-harness: {threads:>2} threads -> byte-identical OK");
} else {
eprintln!(
"tsan-harness: {threads:>2} threads -> MISMATCH ({} vs {} bytes)",
out.len(),
baseline.len()
);
failures += 1;
}
}
let default_out = model
.remember_to_bytes(TargetDtype::BF16)
.expect("default-budget dequant");
if default_out == baseline {
println!("tsan-harness: default budget -> byte-identical OK");
} else {
eprintln!("tsan-harness: default budget -> MISMATCH");
failures += 1;
}
if failures > 0 {
eprintln!("tsan-harness: {failures} determinism failure(s)");
std::process::exit(1);
}
println!("tsan-harness: all budgets byte-identical");
}