1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
//! 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(())
}