//! Minimal cold-restore latency probe over the PUBLIC API: restore a baked
//! snapshot into a warm-worker pool and time `acquire()`. The first acquire of
//! a fresh pool is a cold restore; subsequent acquires of a min=max=1
//! restore-on-release pool re-restore each time, so we measure steady-state
//! restore latency.
//!
//!   cargo run --release --example restore_latency -- <snapshot-dir> [iters]

use std::time::Instant;
use supermachine::Image;

fn main() -> Result<(), Box<dyn std::error::Error>> {
    let snap = std::env::args()
        .nth(1)
        .expect("usage: restore_latency <snapshot-dir> [iters]");
    let iters: usize = std::env::args()
        .nth(2)
        .and_then(|s| s.parse().ok())
        .unwrap_or(30);

    let image = Image::from_snapshot(&snap)?;
    // restore_on_release(true): the VM is re-restored from the snapshot each
    // time it's released, so every acquire after the first is a fresh restore.
    let pool = image
        .pool()
        .min(1)
        .max(1)
        .restore_on_release(true)
        .build()?;

    // Warm up (worker spawn, first CoW map, page-cache).
    for _ in 0..3 {
        let vm = pool.acquire()?;
        drop(vm);
    }

    let mut us: Vec<u128> = Vec::with_capacity(iters);
    for _ in 0..iters {
        let t0 = Instant::now();
        let vm = pool.acquire()?;
        us.push(t0.elapsed().as_micros());
        drop(vm);
    }
    us.sort_unstable();
    let p = |q: usize| us[(us.len() * q / 100).min(us.len() - 1)];
    println!(
        "restore acquire() over {} iters: min={}us p50={}us p90={}us p99={}us max={}us",
        iters,
        us[0],
        p(50),
        p(90),
        p(99),
        us[us.len() - 1],
    );
    Ok(())
}
