makepad/platform/tests/pool_latency.rs
Admin 4945f1873b platform: rename the CPU simulated-GPU backend from headless to gpusim
Squash of 1 work commits (Sep 12–12):
  e74b919  platform: the CPU simulated-GPU backend is `gpusim` — the word "headless" now means only window-less

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-15 13:40:28 +02:00

92 lines
3.7 KiB
Rust

//! Headless, release-only scheduling gate. `--baseline` records without gating.
#[cfg(not(gpusim))]
fn main() {
eprintln!("pool_latency: use MAKEPAD=gpusim cargo test --release -p makepad-platform --test pool_latency");
}
#[cfg(gpusim)]
fn main() {
use makepad_platform::{
thread::{machine_topology, Lane, PriorityStatus, ShutdownMode},
Cx, CxThreadPriority,
};
use std::{
hint::black_box,
sync::{
atomic::{AtomicUsize, Ordering},
Arc,
},
time::{Duration, Instant},
};
assert!(
!cfg!(debug_assertions),
"latency measurement requires --release"
);
let baseline = std::env::args().any(|arg| arg == "--baseline");
let ui_priority = Cx::set_thread_priority(CxThreadPriority::UserInteractive);
println!(
"pool_latency UI UserInteractive={ui_priority:?} topology={:?}",
machine_topology()
);
assert_eq!(ui_priority, PriorityStatus::Applied);
let cx = Cx::new(Box::new(|_, _| {}));
let pool = cx.task_pool();
let ready = Arc::new(AtomicUsize::new(0));
let start = Arc::new(std::sync::OnceLock::<Instant>::new());
let mut jobs = Vec::new();
for seed in 0..pool.heavy_workers() {
let ready = ready.clone();
let start = start.clone();
jobs.push(
pool.submit(Lane::Heavy, move || {
ready.fetch_add(1, Ordering::Release);
while start.get().is_none() {
std::thread::sleep(Duration::from_micros(100));
}
let deadline = *start.get().unwrap() + Duration::from_secs(4);
let mut value = seed as u64 + 1;
while Instant::now() < deadline {
for _ in 0..4096 {
value = black_box(value.wrapping_mul(6364136223846793005).wrapping_add(1));
}
}
black_box(value)
})
.unwrap(),
);
}
let startup_deadline = Instant::now() + Duration::from_secs(10);
while ready.load(Ordering::Acquire) != pool.heavy_workers() {
assert!(Instant::now() < startup_deadline, "workers did not start");
std::thread::sleep(Duration::from_millis(1));
}
let begin = Instant::now();
start.set(begin).unwrap();
let mut samples = Vec::with_capacity(4000);
while begin.elapsed() < Duration::from_secs(4) {
let due = Instant::now() + Duration::from_millis(1);
std::thread::sleep(due.saturating_duration_since(Instant::now()));
samples.push(Instant::now().saturating_duration_since(due).as_secs_f64() * 1000.0);
}
samples.sort_by(f64::total_cmp);
let p95 = samples[(samples.len() * 95 / 100).min(samples.len() - 1)];
println!("pool_latency mode={} logical={} heavy={} light={} duration_s=4 samples={} wake_ms_p50={:.3} wake_ms_p95={p95:.3} wake_ms_max={:.3}",
if baseline { "baseline" } else { "gate" }, std::thread::available_parallelism().unwrap(),
pool.heavy_workers(), pool.light_reserve(), samples.len(), samples[samples.len()/2], samples[samples.len()-1]);
let mut shutdown = pool.shutdown(ShutdownMode::Drain);
let shutdown_deadline = Instant::now() + Duration::from_secs(10);
while shutdown.try_take().is_none() {
assert!(Instant::now() < shutdown_deadline, "pool did not stop");
std::thread::sleep(Duration::from_millis(1));
}
for mut job in jobs {
assert!(job.try_take().unwrap().is_ok());
}
println!("{}", pool.summary());
assert_eq!(pool.stats().priority_applied, pool.worker_count());
assert!(
baseline || p95 <= 2.0,
"main-thread wake p95 {p95:.3} ms exceeds 2 ms"
);
}