use crate::real_forward_types::RealForwardError;
const FP16_MAX: f32 = 65_504.0;
const WARN_FRACTION: f32 = 0.5;
struct BlockPeak {
image: usize,
block: usize,
absmax: f32,
}
pub(crate) struct VisionOverflowCapture {
path: std::path::PathBuf,
image: usize,
peaks: Vec<BlockPeak>,
}
impl VisionOverflowCapture {
pub(crate) fn from_env() -> Option<Self> {
let path = std::env::var_os("TURBOSPARK_VISION_OVERFLOW")?;
Some(Self {
path: path.into(),
image: 0,
peaks: Vec::new(),
})
}
pub(crate) fn note_image_start(&mut self) {
self.image += 1;
}
pub(crate) fn check_block(
&mut self,
block: usize,
x: &gpu::MetalBuffer,
count: usize,
) -> Result<(), RealForwardError> {
let values = gpu::read_buffer_f16(x, 0, count);
self.scan_block(block, &values)
}
fn scan_block(&mut self, block: usize, values: &[half::f16]) -> Result<(), RealForwardError> {
let mut absmax = 0.0f32;
for v in values {
let f = v.to_f32();
if !f.is_finite() {
return Err(RealForwardError::Unsupported(format!(
"vision overflow capture: image {}, block {block}: a non-finite value \
reached the residual stream (AGENTS.md Gotcha 60 -- this is what an FP16 \
overflow looks like downstream, not a NaN from nowhere)",
self.image
)));
}
absmax = absmax.max(f.abs());
}
if absmax > WARN_FRACTION * FP16_MAX {
eprintln!(
"[vision-overflow] image {}, block {block}: absmax {absmax:.1} is {:.1}% of \
FP16's {FP16_MAX} ceiling",
self.image,
100.0 * absmax / FP16_MAX
);
}
self.peaks.push(BlockPeak {
image: self.image,
block,
absmax,
});
Ok(())
}
fn report_json(&self) -> String {
let mut out = String::new();
out.push_str(&format!(
"{{\n \"fp16_max\": {FP16_MAX},\n \"warn_fraction\": {WARN_FRACTION},\n \
\"peaks\": [\n"
));
for (i, p) in self.peaks.iter().enumerate() {
if i > 0 {
out.push_str(",\n");
}
out.push_str(&format!(
" {{\"image\": {}, \"block\": {}, \"absmax\": {}}}",
p.image, p.block, p.absmax
));
}
out.push_str("\n ]\n}\n");
out
}
}
impl Drop for VisionOverflowCapture {
fn drop(&mut self) {
if self.peaks.is_empty() {
eprintln!(
"[vision-overflow] no block ran; wrote nothing to {}",
self.path.display()
);
return;
}
match std::fs::write(&self.path, self.report_json()) {
Ok(()) => eprintln!(
"[vision-overflow] wrote {} block reading(s) to {}",
self.peaks.len(),
self.path.display()
),
Err(e) => eprintln!(
"[vision-overflow] FAILED writing {}: {e}",
self.path.display()
),
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use half::f16;
fn capture() -> VisionOverflowCapture {
VisionOverflowCapture {
path: std::path::PathBuf::from("/dev/null"),
image: 0,
peaks: Vec::new(),
}
}
fn row(values: &[f32]) -> Vec<f16> {
values.iter().map(|&v| f16::from_f32(v)).collect()
}
#[test]
fn a_finite_block_records_its_absmax_and_nothing_else() {
let mut c = capture();
c.note_image_start();
c.scan_block(7, &row(&[1.0, -40.0, 3.5, 12.0]))
.expect("a finite block is accepted");
assert_eq!(c.peaks.len(), 1, "one block, one peak");
assert_eq!(c.peaks[0].image, 1);
assert_eq!(c.peaks[0].block, 7);
assert_eq!(c.peaks[0].absmax, 40.0);
}
#[test]
fn a_non_finite_value_is_refused_by_name_and_locates_itself() {
for bad in [f32::NAN, f32::INFINITY, f32::NEG_INFINITY] {
let mut c = capture();
c.note_image_start();
c.note_image_start();
let Err(err) = c.scan_block(26, &row(&[1.0, bad, 2.0])) else {
panic!("a non-finite value must be refused; {bad} was accepted");
};
let text = err.to_string();
assert!(text.contains("image 2"), "must name the image; got {text}");
assert!(text.contains("block 26"), "must name the block; got {text}");
assert!(
text.contains("Gotcha 60"),
"must point at the reason a NaN here is an overflow; got {text}"
);
assert!(
c.peaks.is_empty(),
"a refused block must not also record a peak"
);
}
}
#[test]
fn a_value_past_the_warn_fraction_still_succeeds_and_still_records() {
let mut c = capture();
c.note_image_start();
let loud = WARN_FRACTION * FP16_MAX + 1_000.0;
assert!(loud < FP16_MAX, "the warn threshold is below the ceiling");
c.scan_block(9, &row(&[loud]))
.expect("a warning is not a failure");
assert_eq!(c.peaks.len(), 1);
assert!(c.peaks[0].absmax > WARN_FRACTION * FP16_MAX);
}
#[test]
fn the_report_is_valid_json_and_carries_one_object_per_peak() {
let mut c = capture();
c.note_image_start();
c.scan_block(0, &row(&[1.0])).expect("finite");
c.scan_block(1, &row(&[-2.0])).expect("finite");
c.note_image_start();
c.scan_block(0, &row(&[3.0])).expect("finite");
let parsed: serde_json::Value =
serde_json::from_str(&c.report_json()).expect("the report parses as JSON");
assert_eq!(parsed["fp16_max"], FP16_MAX);
assert_eq!(parsed["warn_fraction"], WARN_FRACTION);
let peaks = parsed["peaks"].as_array().expect("peaks is an array");
assert_eq!(peaks.len(), 3, "one object per recorded block");
assert_eq!(peaks[0]["image"], 1);
assert_eq!(peaks[1]["block"], 1);
assert_eq!(peaks[1]["absmax"], 2.0);
assert_eq!(peaks[2]["image"], 2, "the counter advances per image");
}
#[test]
fn an_empty_report_is_still_valid_json() {
let parsed: serde_json::Value =
serde_json::from_str(&capture().report_json()).expect("parses");
assert_eq!(
parsed["peaks"].as_array().expect("array").len(),
0,
"no blocks, no peaks"
);
}
}