Skip to content

Commit ee19d8a

Browse files
committed
Add OCI snapshot benchmarks
Signed-off-by: Ludvig Liljenberg <4257730+ludfjig@users.noreply.github.com>
1 parent 0e47f47 commit ee19d8a

1 file changed

Lines changed: 175 additions & 1 deletion

File tree

src/hyperlight_host/benches/benchmarks.rs

Lines changed: 175 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -153,6 +153,15 @@ fn sandbox_lifecycle_benchmark(c: &mut Criterion) {
153153
);
154154
}
155155

156+
// Isolates the cost of building a MultiUseSandbox from an
157+
// already-resident Snapshot. The Snapshot is loaded outside the
158+
// timed region.
159+
for size in SandboxSize::all() {
160+
group.bench_function(format!("sandbox_from_snapshot/{}", size.name()), |b| {
161+
bench_sandbox_from_snapshot(b, size)
162+
});
163+
}
164+
156165
group.finish();
157166
}
158167

@@ -347,6 +356,28 @@ fn bench_snapshot_restore(b: &mut criterion::Bencher, size: SandboxSize) {
347356
});
348357
}
349358

359+
fn bench_sandbox_from_snapshot(b: &mut criterion::Bencher, size: SandboxSize) {
360+
use hyperlight_host::HostFunctions;
361+
use hyperlight_host::sandbox::snapshot::{OciTag, Snapshot};
362+
363+
let dir = tempfile::tempdir().unwrap();
364+
let snap_path = dir.path().join("bench");
365+
let tag = OciTag::new("latest").unwrap();
366+
{
367+
let mut sbox = create_multiuse_sandbox_with_size(size);
368+
let snapshot = sbox.snapshot().unwrap();
369+
snapshot.to_oci(&snap_path, &tag).unwrap();
370+
}
371+
let loaded = std::sync::Arc::new(Snapshot::from_oci(&snap_path, tag).unwrap());
372+
373+
// Drop is not included.
374+
b.iter_batched(
375+
|| (),
376+
|_| MultiUseSandbox::from_snapshot(loaded.clone(), HostFunctions::default(), None).unwrap(),
377+
criterion::BatchSize::PerIteration,
378+
);
379+
}
380+
350381
fn snapshots_benchmark(c: &mut Criterion) {
351382
let mut group = c.benchmark_group("snapshots");
352383

@@ -551,6 +582,148 @@ fn shared_memory_benchmark(c: &mut Criterion) {
551582
group.finish();
552583
}
553584

585+
// ============================================================================
586+
// Benchmark Category: Snapshot Files
587+
// ============================================================================
588+
589+
fn snapshot_file_benchmark(c: &mut Criterion) {
590+
use hyperlight_host::HostFunctions;
591+
use hyperlight_host::sandbox::snapshot::{OciTag, Snapshot};
592+
593+
let mut group = c.benchmark_group("snapshot_files");
594+
595+
// Pre-create OCI snapshot images for all sizes.
596+
let dirs: Vec<_> = SandboxSize::all()
597+
.iter()
598+
.map(|size| {
599+
let dir = tempfile::tempdir().unwrap();
600+
let snap_path = dir.path().join(size.name());
601+
let snapshot = {
602+
let mut sbox = create_multiuse_sandbox_with_size(*size);
603+
sbox.snapshot().unwrap()
604+
};
605+
snapshot
606+
.to_oci(&snap_path, &OciTag::new("latest").unwrap())
607+
.unwrap();
608+
(dir, snapshot, snap_path)
609+
})
610+
.collect();
611+
612+
// Benchmark: save_snapshot. Wipe the layout between iterations
613+
// so each save measures a fresh write rather than a tag-append.
614+
for (i, size) in SandboxSize::all().iter().enumerate() {
615+
let snap_dir = tempfile::tempdir().unwrap();
616+
let path = snap_dir.path().join("bench");
617+
let snapshot = &dirs[i].1;
618+
group.bench_function(format!("save_snapshot/{}", size.name()), |b| {
619+
b.iter_batched(
620+
|| {
621+
let _ = std::fs::remove_dir_all(&path);
622+
},
623+
|_| {
624+
snapshot
625+
.to_oci(&path, &OciTag::new("latest").unwrap())
626+
.unwrap()
627+
},
628+
criterion::BatchSize::PerIteration,
629+
);
630+
});
631+
}
632+
633+
// Benchmark: load_snapshot (parse manifest + config + mmap blob).
634+
for (i, size) in SandboxSize::all().iter().enumerate() {
635+
let snap_path = dirs[i].2.clone();
636+
group.bench_function(format!("load_snapshot/{}", size.name()), |b| {
637+
b.iter(|| {
638+
let _ = Snapshot::from_oci(&snap_path, OciTag::new("latest").unwrap()).unwrap();
639+
});
640+
});
641+
}
642+
643+
// Benchmark: load_snapshot_unchecked (skip blob digest verification).
644+
for (i, size) in SandboxSize::all().iter().enumerate() {
645+
let snap_path = dirs[i].2.clone();
646+
group.bench_function(format!("load_snapshot_unchecked/{}", size.name()), |b| {
647+
b.iter(|| {
648+
// SAFETY: snapshot path produced by `to_oci` in bench setup.
649+
let _ = unsafe {
650+
Snapshot::from_oci_unchecked(&snap_path, OciTag::new("latest").unwrap())
651+
}
652+
.unwrap();
653+
});
654+
});
655+
}
656+
657+
// Benchmark: cold_start_via_evolve (new + evolve + call). Drop is not included.
658+
for size in SandboxSize::all() {
659+
group.bench_function(format!("cold_start_via_evolve/{}", size.name()), |b| {
660+
b.iter_batched(
661+
|| (),
662+
|_| {
663+
let mut sbox = create_multiuse_sandbox_with_size(size);
664+
sbox.call::<String>("Echo", "hello\n".to_string()).unwrap();
665+
sbox
666+
},
667+
criterion::BatchSize::PerIteration,
668+
);
669+
});
670+
}
671+
672+
// Benchmark: cold_start_via_snapshot (load + from_snapshot + call). Drop is not included.
673+
for (i, size) in SandboxSize::all().iter().enumerate() {
674+
let snap_path = dirs[i].2.clone();
675+
group.bench_function(format!("cold_start_via_snapshot/{}", size.name()), |b| {
676+
b.iter_batched(
677+
|| (),
678+
|_| {
679+
let loaded =
680+
Snapshot::from_oci(&snap_path, OciTag::new("latest").unwrap()).unwrap();
681+
let mut sbox = MultiUseSandbox::from_snapshot(
682+
std::sync::Arc::new(loaded),
683+
HostFunctions::default(),
684+
None,
685+
)
686+
.unwrap();
687+
sbox.call::<String>("Echo", "hello\n".to_string()).unwrap();
688+
sbox
689+
},
690+
criterion::BatchSize::PerIteration,
691+
);
692+
});
693+
}
694+
695+
// Benchmark: cold_start_via_snapshot_unchecked (load unchecked + from_snapshot + call). Drop is not included.
696+
for (i, size) in SandboxSize::all().iter().enumerate() {
697+
let snap_path = dirs[i].2.clone();
698+
group.bench_function(
699+
format!("cold_start_via_snapshot_unchecked/{}", size.name()),
700+
|b| {
701+
b.iter_batched(
702+
|| (),
703+
|_| {
704+
// SAFETY: snapshot path produced by `to_oci` in bench setup.
705+
let loaded = unsafe {
706+
Snapshot::from_oci_unchecked(&snap_path, OciTag::new("latest").unwrap())
707+
}
708+
.unwrap();
709+
let mut sbox = MultiUseSandbox::from_snapshot(
710+
std::sync::Arc::new(loaded),
711+
HostFunctions::default(),
712+
None,
713+
)
714+
.unwrap();
715+
sbox.call::<String>("Echo", "hello\n".to_string()).unwrap();
716+
sbox
717+
},
718+
criterion::BatchSize::PerIteration,
719+
);
720+
},
721+
);
722+
}
723+
724+
group.finish();
725+
}
726+
554727
criterion_group! {
555728
name = benches;
556729
config = Criterion::default();
@@ -561,6 +734,7 @@ criterion_group! {
561734
guest_call_benchmark_large_param,
562735
function_call_serialization_benchmark,
563736
sample_workloads_benchmark,
564-
shared_memory_benchmark
737+
shared_memory_benchmark,
738+
snapshot_file_benchmark
565739
}
566740
criterion_main!(benches);

0 commit comments

Comments
 (0)