//! Perf baseline harness (plan Phase 6.3). //! //! Measures world-build and fixed-step tick costs and prints medians. The //! timings are NOT asserted — wall-clock asserts are flaky by nature (HPC //! ch.5.6). The sanity asserts only prove the sim actually ran (tick count, //! phase transition, black-boxed state) so the optimizer cannot hollow the //! loop. Run pinned on one machine and copy the table into //! `docs/PERF_BASELINE.md` by hand: //! //! ```sh //! cargo test -p nigig-traffic --test perf -- --test-threads=1 --nocapture //! ``` use nigig_traffic::traffic::road::build_world_for; use nigig_traffic::traffic::scenario::all; use nigig_traffic::traffic::{Phase, TrafficWorld}; use std::hint::black_box; use std::time::Instant; fn median_us(mut samples: Vec) -> u128 { samples.sort_unstable(); samples[samples.len() / 2] } #[test] fn perf_print_baseline() { let scenarios = all(); println!( "=== nigig-traffic perf baseline ({} scenarios) ===", scenarios.len() ); // 1. World build per category (first scenario of each), 20 reps. println!("--- build_world_for: median µs over 20 reps ---"); let mut seen_cats = Vec::new(); for s in scenarios { if seen_cats.contains(&s.category) { continue; } seen_cats.push(s.category); let mut samples = Vec::with_capacity(20); for _ in 0..20 { let mut world = makepad_game_sim::GameWorld::new(); let mut next_id = 1u64; let t0 = Instant::now(); let meta = build_world_for(&mut world, s, &mut next_id); let us = t0.elapsed().as_micros(); // Touch the outputs so the build cannot be eliminated. black_box(meta.finish_radius); black_box(world.entities.len()); samples.push(us); } println!( " {:<10} {:>6} µs", format!("{:?}", s.category), median_us(samples) ); } // 2. Fixed-step tick: 600 ticks per scenario, default (idle) input. // Reports median per-tick µs across scenarios plus the slowest three. println!("--- tick: 600 ticks/scenario, per-tick µs ---"); let mut per_tick: Vec<(&str, u128)> = Vec::new(); for (i, s) in scenarios.iter().enumerate() { let mut world = TrafficWorld::new(); world.load_scenario(i); let input = makepad_game_blocks::DriveInput::default(); let t0 = Instant::now(); for _ in 0..600 { world.tick(&input); } let us = t0.elapsed().as_micros() / 600; // Sanity: the sim really ran (intro is 60 ticks) and state escaped. assert_eq!(world.run.tick, 600); assert_eq!(world.run.phase, Phase::Driving); black_box(world.run.score.total_points); let _ = s; per_tick.push((s.id, us)); } per_tick.sort_by_key(|(_, us)| *us); let mid = per_tick[per_tick.len() / 2].1; println!(" scenarios: {}, median per-tick: {mid} µs", per_tick.len()); println!(" slowest 3:"); for (id, us) in per_tick.iter().rev().take(3) { println!(" {id:<22} {us:>6} µs"); } println!("=== end baseline ==="); }