Every heap ref carries its heap tag, generation checks always on, rooted async values. Splash isolates get a domain table and curated minimal preludes; system modules under `mod` expand lazily on first touch. No wall-clock script caps: isolation and force-stop instead. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
77 lines
3.6 KiB
Rust
77 lines
3.6 KiB
Rust
//! Op-time bench: the worst ns per AIR op over hostile op mixes, and the
|
|
//! cost of a dependent load that misses every cache (the yardstick
|
|
//! `tests/host_hostile.rs` holds host components' declared costs to;
|
|
//! budgets themselves count ops). The cancellation latency at the admitted
|
|
//! ceiling is measured by `tests/admission.rs` (`--nocapture` prints it).
|
|
//!
|
|
//! cargo run --release -p makepad-script-compute --example bench_admission
|
|
|
|
use makepad_script_compute_sched::kernel::KernelExt;
|
|
use makepad_script_compute::kernel::compile_with;
|
|
use makepad_script_compute::Backend;
|
|
use std::time::Instant;
|
|
|
|
const MIXES: &[(&str, &str)] = &[
|
|
("fdiv chain", "let s = float(i) + 1.5\n for a in 0..1000 { s = 1.0 / (s + 1.5) }\n o[i] = s"),
|
|
("fsqrt chain", "let s = float(i) + 1.5\n for a in 0..1000 { s = sqrt(s + 2.0) }\n o[i] = s"),
|
|
("idiv chain", "let s = i + 12345\n for a in 0..1000 { s = s / 3 + s % 7 + 1000003 }\n o[i] = float(s)"),
|
|
("sin chain", "let s = float(i)\n for a in 0..1000 { s = sin(s) + 0.5 }\n o[i] = s"),
|
|
("random loads", "let s = i\n for a in 0..1000 { s = int(src[(s * 7919 + a) % 65536]) + s }\n o[i] = float(s)"),
|
|
("fbm", "let s = 0.0\n for a in 0..100 { s = s + fbm2(vec2(float(i), float(a)) * 0.01, 4, 2.0, 0.5) }\n o[i] = s"),
|
|
];
|
|
|
|
/// Dependent loads over a 64 MiB buffer (a random cycle): every load is a
|
|
/// cache and TLB miss, the slowest thing a kernel op can be.
|
|
fn pointer_chase() {
|
|
let n = 1usize << 24;
|
|
let mut perm: Vec<u32> = (0..n as u32).collect();
|
|
let mut x = 0x2545F491u64;
|
|
for k in (1..n).rev() {
|
|
x ^= x << 13;
|
|
x ^= x >> 7;
|
|
x ^= x << 17;
|
|
perm.swap(k, (x % k as u64) as usize);
|
|
}
|
|
let mut next = vec![0f32; n];
|
|
for k in 0..n {
|
|
next[perm[k] as usize] = perm[(k + 1) % n] as f32;
|
|
}
|
|
drop(perm);
|
|
for backend in [Backend::Native, Backend::Interp] {
|
|
let code = "let src = input(f32)\nlet o = output(f32)\nfn element(i) {\n let s = i * 977\n for a in 0..1000 { s = int(src[s]) }\n o[i] = float(s)\n }";
|
|
let k = compile_with(code, &[], backend).unwrap();
|
|
let m = 200;
|
|
let mut out = vec![0.0f32; m];
|
|
let mut c = k.call();
|
|
c.input("src", &next).unwrap();
|
|
c.output("o", &mut out).unwrap();
|
|
let t0 = Instant::now();
|
|
c.run(m).unwrap();
|
|
let s = t0.elapsed().as_secs_f64();
|
|
println!("{:?} pointer chase 64 MiB: {:6.1} ns/load {:6.3} ns/op (cost {})", backend, s * 1e9 / (m as f64 * 1000.0), s * 1e9 / (m as f64 * k.cost as f64), k.cost);
|
|
}
|
|
}
|
|
|
|
fn main() {
|
|
pointer_chase();
|
|
let src: Vec<f32> = (0..65536).map(|k| (k % 997) as f32).collect();
|
|
for backend in [Backend::Native, Backend::Interp] {
|
|
for (name, body) in MIXES {
|
|
let code = format!("let src = input(f32)\nlet o = output(f32)\nfn element(i) {{\n {}\n }}", body);
|
|
let k = compile_with(&code, &[], backend).unwrap_or_else(|e| panic!("{}: {:?}", name, e));
|
|
let n = if backend == Backend::Native { 2000 } else { 100 };
|
|
let mut out = vec![0.0f32; n];
|
|
let mut best = f64::MAX;
|
|
for _ in 0..3 {
|
|
let mut c = k.call();
|
|
c.input("src", &src).unwrap();
|
|
c.output("o", &mut out).unwrap();
|
|
let t0 = Instant::now();
|
|
c.run(n).unwrap();
|
|
best = best.min(t0.elapsed().as_secs_f64());
|
|
}
|
|
let ns_op = best * 1e9 / (k.cost as f64 * n as f64);
|
|
println!("{:?} {:14} cost {:8} ops/elem {:6.3} ns/op {:8.1} us/elem", backend, name, k.cost, ns_op, best * 1e6 / n as f64);
|
|
}
|
|
}
|
|
}
|