facett-graph3d/examples/scene_probe.rs
//! **Is the GPU instance build worth fanning out?** — gpu_scene, serial vs pooled.
//!
//! Run: cargo run --release -p facett-graph3d --example scene_probe --features wgpu
//!
//! VERDICT (oden, 2026-09-05): the instance build stays SERIAL. Fanning it across the
//! parked pool loses at every size a host draws — 30k 0.488 -> 4.269 ms, 100k 2.016 ->
//! 4.239, 300k 10.825 -> 17.486 — and wins only 1.18x at a million nodes. It is
//! WRITE-BANDWIDTH bound (96 MB of instances per frame at 1M), so cores do not help.
use std::time::Instant;
use facett_graph3d::{dataset, Graph3D, Layout3D};
fn main() {
const FRAMES: usize = 40;
println!("# gpu_scene() CPU-side build — the instance loops dominate once projection is fixed");
println!("threshold GPU_INSTANCE_PARALLEL_MIN is compiled in; run twice to compare");
println!("{:>9} {:>9} {:>14} {:>16}", "nodes", "edges", "gpu_scene ms/f", "bytes written/f");
for &want in &[30_000usize, 100_000, 300_000, 1_000_000] {
let d = dataset::generate(&dataset::GraphSpec::sized(want));
let g = Graph3D::new(d.nodes, d.edges, Layout3D::Sphere);
let (n, e) = (g.nodes.len(), g.edges.len());
// warm
let _ = g.gpu_scene(1280.0, 800.0, 3.0);
let t = Instant::now();
for f in 0..FRAMES {
let s = g.gpu_scene(1280.0, 800.0, 3.0);
std::hint::black_box(&s);
let _ = f;
}
let ms = t.elapsed().as_secs_f64() * 1000.0 / FRAMES as f64;
// 32 B per NodeInstance + 40 B per EdgeInstance, plus the (Pos2,f32) projection.
let bytes = n * 32 + e * 40 + n * 12;
println!("{:>9} {:>9} {:>11.3} ms {:>13.1} MB", n, e, ms, bytes as f64 / 1e6);
}
}