use std::hint::black_box;
use std::time::Instant;
pub fn median_ms(warmup: usize, runs: usize, mut f: impl FnMut()) -> f64 {
for _ in 0..warmup {
f();
}
let runs = runs.max(1);
let mut ts = Vec::with_capacity(runs);
for _ in 0..runs {
let t = Instant::now();
f();
ts.push(t.elapsed().as_secs_f64() * 1e3);
}
ts.sort_by(|a, b| a.partial_cmp(b).unwrap());
ts[ts.len() / 2]
}
pub fn measure_bandwidth_gbps() -> f64 {
let n = (96usize << 20) / 4; let mut a = vec![1.0f32; n];
let mut b = vec![0.0f32; n];
b.copy_from_slice(&a); black_box(b.as_ptr());
best_copy_gbps(n as f64 * 4.0 * 2.0, |it| {
a[it] = it as f32; let src = black_box(&a); b.copy_from_slice(src);
black_box(b.as_ptr());
})
}
#[derive(Clone, Debug)]
pub struct Roofline {
pub ms: f64,
pub achieved_gflops: f64,
pub achieved_gbps: f64,
pub intensity: f64,
pub bw_frac: f64,
pub bound: &'static str,
}
pub fn empirical_roofline(ms: f64, flops: u64, bytes: u64, peak_gbps: f64) -> Roofline {
let s = (ms / 1e3).max(1e-12);
let achieved_gflops = flops as f64 / 1e9 / s;
let achieved_gbps = bytes as f64 / 1e9 / s;
let bw_frac = if peak_gbps > 0.0 {
achieved_gbps / peak_gbps
} else {
0.0
};
let intensity = if bytes > 0 {
flops as f64 / bytes as f64
} else {
0.0
};
let bound = if bw_frac >= 0.5 {
"bandwidth-bound (measured)"
} else if intensity >= crate::shapes::DEFAULT_RIDGE {
"compute-bound (measured)"
} else {
"latency/overhead-bound (measured)"
};
Roofline {
ms,
achieved_gflops,
achieved_gbps,
intensity,
bw_frac,
bound,
}
}
pub fn measure_memcpy_gbps() -> f64 {
let n = 96usize << 20; let mut src = vec![7u8; n];
let mut dst = vec![0u8; n];
dst.copy_from_slice(&src); black_box(dst.as_ptr());
best_copy_gbps(n as f64 * 2.0, |it| {
src[it] = it as u8; let s = black_box(&src); dst.copy_from_slice(s);
black_box(dst.as_ptr());
})
}
fn best_copy_gbps(bytes: f64, mut copy: impl FnMut(usize)) -> f64 {
let mut best = 0.0f64;
for it in 0..8 {
let t = Instant::now();
copy(it);
let secs = t.elapsed().as_secs_f64();
let gbps = bytes / 1e9 / secs.max(1e-9);
if gbps < 2000.0 {
best = best.max(gbps);
}
}
best.max(1.0)
}
#[derive(Clone, Debug)]
pub struct OffloadRoofline {
pub device_step_ms: f64,
pub host_step_ms: f64,
pub per_step_transfer_ms: f64,
pub weight_upload_ms: f64,
pub steady_speedup: f64,
pub break_even_steps: f64,
}
pub fn offload_roofline(
host_step_ms: f64,
device_compute_ms: f64,
weight_bytes: u64,
per_step_bytes: u64,
transfer_gbps: f64,
) -> OffloadRoofline {
let bps = (transfer_gbps * 1e9).max(1.0);
let per_step_transfer_ms = per_step_bytes as f64 / bps * 1e3;
let weight_upload_ms = weight_bytes as f64 / bps * 1e3;
let device_step_ms = device_compute_ms + per_step_transfer_ms;
let steady_speedup = if device_step_ms > 0.0 {
host_step_ms / device_step_ms
} else {
0.0
};
let per_step_gain = host_step_ms - device_step_ms;
let break_even_steps = if per_step_gain > 0.0 {
weight_upload_ms / per_step_gain
} else {
f64::INFINITY
};
OffloadRoofline {
device_step_ms,
host_step_ms,
per_step_transfer_ms,
weight_upload_ms,
steady_speedup,
break_even_steps,
}
}
pub fn region_pct(region_ms: f64, total_ms: f64) -> f64 {
if total_ms <= 0.0 {
0.0
} else {
(region_ms / total_ms * 100.0).clamp(0.0, 100.0)
}
}
#[derive(Clone, Debug)]
pub struct RegionTime {
pub name: String,
pub ms: f64,
pub time_pct: f64,
pub byte_pct: f64,
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn median_ms_is_positive_and_ignores_warmup() {
let ms = median_ms(2, 5, || {
let mut acc = 0u64;
for i in 0..50_000u64 {
acc = acc.wrapping_add(i);
}
black_box(acc);
});
assert!(ms >= 0.0 && ms.is_finite());
}
#[test]
fn bandwidth_probe_is_plausible() {
let gbps = measure_bandwidth_gbps();
assert!(
gbps > 1.0 && gbps < 100_000.0,
"implausible bandwidth {gbps}"
);
}
#[test]
fn empirical_roofline_arithmetic() {
let r = empirical_roofline(100.0, 2_000_000_000, 8_000_000_000, 100.0);
assert!((r.achieved_gflops - 20.0).abs() < 1e-6);
assert!((r.achieved_gbps - 80.0).abs() < 1e-6);
assert!((r.bw_frac - 0.8).abs() < 1e-6);
assert!((r.intensity - 0.25).abs() < 1e-9);
assert_eq!(r.bound, "bandwidth-bound (measured)");
}
#[test]
fn empirical_roofline_compute_vs_latency() {
let hi = empirical_roofline(10.0, 1_000_000_000_000, 1_000_000, 100.0);
assert_eq!(hi.bound, "compute-bound (measured)");
let lo = empirical_roofline(1000.0, 10, 1_000_000, 100.0);
assert_eq!(lo.bound, "latency/overhead-bound (measured)");
}
#[test]
fn region_pct_clamps() {
assert_eq!(region_pct(5.0, 10.0), 50.0);
assert_eq!(region_pct(-1.0, 10.0), 0.0); assert_eq!(region_pct(20.0, 10.0), 100.0);
assert_eq!(region_pct(1.0, 0.0), 0.0);
}
#[test]
fn memcpy_probe_is_plausible() {
let gbps = measure_memcpy_gbps();
assert!(
gbps > 1.0 && gbps < 100_000.0,
"implausible memcpy BW {gbps}"
);
}
#[test]
fn offload_pays_when_device_is_faster() {
let r = offload_roofline(100.0, 10.0, 1_000_000_000, 1_000_000, 10.0);
assert!((r.weight_upload_ms - 100.0).abs() < 1e-6);
assert!((r.per_step_transfer_ms - 0.1).abs() < 1e-6);
assert!((r.device_step_ms - 10.1).abs() < 1e-6);
assert!(r.steady_speedup > 9.0); assert!(r.break_even_steps > 1.0 && r.break_even_steps < 2.0);
}
#[test]
fn offload_never_pays_when_device_slower() {
let r = offload_roofline(10.0, 100.0, 1_000_000_000, 1_000_000, 10.0);
assert!(r.steady_speedup < 1.0);
assert!(r.break_even_steps.is_infinite());
}
}