metrale_model_layers/layers/ops/
e4m3_range.rs1use std::cell::RefCell;
28use std::collections::BTreeMap;
29
30use anyhow::Result;
31use metrale_gpu_runtime::gpu::{DevicePtr, GpuBackend, KernelHandle};
32use parking_lot::Mutex;
33
34pub const E4M3_MAX: f32 = 448.0;
36
37#[derive(Debug, Clone, Copy, PartialEq)]
40pub struct E4m3Saturation {
41 pub site: &'static str,
43 pub why: &'static str,
45 pub bound: f32,
48}
49
50pub const NEMOTRON_SHARED_EXPERT_E4M3: E4m3Saturation = E4m3Saturation {
54 site: "Nemotron-H shared expert, --nemotron-shared-expert-e4m3",
55 why: "opted in on the command line; the flag discloses the saturating cast",
56 bound: f32::INFINITY,
57};
58
59pub const MOE_ROUTED_DOWN_KNOWN: E4m3Saturation = E4m3Saturation {
64 site: "MoE routed-down input",
65 why: "known: Qwen3.6-35B-A3B-NVFP4 reaches 510 at position 0 of its last layer",
66 bound: 512.0,
67};
68
69thread_local! {
70 static SCOPES: RefCell<Vec<E4m3Saturation>> = const { RefCell::new(Vec::new()) };
71}
72
73static SEEN: Mutex<BTreeMap<&'static str, u64>> = Mutex::new(BTreeMap::new());
75
76#[must_use = "the allowance ends when the scope is dropped"]
80pub fn allow_e4m3_saturation(saturation: E4m3Saturation) -> E4m3SaturationScope {
81 SCOPES.with(|s| s.borrow_mut().push(saturation));
82 E4m3SaturationScope { _private: () }
83}
84
85pub struct E4m3SaturationScope {
87 _private: (),
88}
89
90impl Drop for E4m3SaturationScope {
91 fn drop(&mut self) {
92 SCOPES.with(|s| s.borrow_mut().pop());
93 }
94}
95
96pub fn e4m3_saturations(site: &str) -> u64 {
98 SEEN.lock().get(site).copied().unwrap_or(0)
99}
100
101fn judge(amax: f32) {
103 if amax <= E4M3_MAX {
104 return;
105 }
106 match SCOPES.with(|s| s.borrow().last().copied()) {
107 Some(allowed) if amax <= allowed.bound => {
108 let mut seen = SEEN.lock();
109 let count = seen.entry(allowed.site).or_insert(0);
110 if *count == 0 {
111 tracing::warn!(
112 site = allowed.site,
113 why = allowed.why,
114 amax,
115 "unscaled E4M3 cast saturates (|x| = {amax} > {E4M3_MAX}) at a disclosed \
116 site; counted, logged once"
117 );
118 }
119 *count += 1;
120 }
121 Some(allowed) => panic!(
122 "unscaled E4M3 cast would saturate: activation |x| = {amax} > {E4M3_MAX}, past the \
123 {} that `{}` discloses",
124 allowed.bound, allowed.site
125 ),
126 None => panic!("unscaled E4M3 cast would saturate: activation |x| = {amax} > {E4M3_MAX}"),
127 }
128}
129
130fn bf16_amax<'a>(rows: impl Iterator<Item = &'a [u8]>) -> f32 {
132 rows.flat_map(|r| r.chunks_exact(2))
133 .map(|b| f32::from_bits(u32::from(u16::from_le_bytes([b[0], b[1]])) << 16).abs())
134 .filter(|v| !v.is_nan())
135 .fold(0.0f32, f32::max)
136}
137
138pub fn check_e4m3_activation_range(
142 gpu: &dyn GpuBackend,
143 kernel: KernelHandle,
144 input: DevicePtr,
145 m: u32,
146 k: u32,
147 stream: u64,
148) -> Result<()> {
149 if cfg!(debug_assertions) && gpu.kernel_casts_a_to_e4m3(kernel) {
150 check_e4m3_range(gpu, input, m as usize * k as usize, stream)?;
151 }
152 Ok(())
153}
154
155pub fn check_e4m3_range(
160 gpu: &dyn GpuBackend,
161 input: DevicePtr,
162 elements: usize,
163 stream: u64,
164) -> Result<()> {
165 if !cfg!(debug_assertions) || elements == 0 || gpu.stream_is_capturing(stream) {
166 return Ok(());
167 }
168 let mut buf = vec![0u8; elements * 2];
169 gpu.copy_d2h_on_stream(input, &mut buf, stream)?;
170 judge(bf16_amax(std::iter::once(buf.as_slice())));
171 Ok(())
172}
173
174#[allow(clippy::too_many_arguments)]
180pub fn check_e4m3_grouped(
181 gpu: &dyn GpuBackend,
182 kernel: KernelHandle,
183 a: DevicePtr,
184 expert_offsets: DevicePtr,
185 sorted_token_ids: DevicePtr,
186 num_experts: u32,
187 k: u32,
188 stream: u64,
189) -> Result<()> {
190 if !cfg!(debug_assertions)
191 || !gpu.kernel_casts_a_to_e4m3(kernel)
192 || gpu.stream_is_capturing(stream)
193 {
194 return Ok(());
195 }
196 let offsets = read_i32s(gpu, expert_offsets, num_experts as usize + 1, stream)?;
197 let positions: Vec<usize> = offsets
198 .windows(2)
199 .flat_map(|w| w[0].max(0) as usize..w[1].max(0) as usize)
200 .collect();
201 let Some(&last) = positions.iter().max() else {
202 return Ok(());
203 };
204 let rows: Vec<usize> = if sorted_token_ids.is_null() {
205 positions
206 } else {
207 let ids = read_i32s(gpu, sorted_token_ids, last + 1, stream)?;
208 positions
209 .iter()
210 .map(|&p| usize::try_from(ids[p]))
211 .collect::<Result<_, _>>()
212 .map_err(|_| {
213 anyhow::anyhow!("a negative sorted token id at a position the kernel reads")
214 })?
215 };
216 let Some(&top) = rows.iter().max() else {
217 return Ok(());
218 };
219 let width = k as usize * 2;
220 let mut buf = vec![0u8; (top + 1) * width];
221 gpu.copy_d2h_on_stream(a, &mut buf, stream)?;
222 judge(bf16_amax(
223 rows.iter().map(|&r| &buf[r * width..(r + 1) * width]),
224 ));
225 Ok(())
226}
227
228fn read_i32s(gpu: &dyn GpuBackend, ptr: DevicePtr, n: usize, stream: u64) -> Result<Vec<i32>> {
229 let mut buf = vec![0u8; n * 4];
230 gpu.copy_d2h_on_stream(ptr, &mut buf, stream)?;
231 Ok(buf
232 .chunks_exact(4)
233 .map(|b| i32::from_le_bytes([b[0], b[1], b[2], b[3]]))
234 .collect())
235}
236
237#[cfg(test)]
238#[path = "e4m3_range_tests.rs"]
239mod tests;