Skip to content

Commit ab957f7

Browse files
committed
Add new benchmarks for varying sizes of sandboxes and guest function parameters
Signed-off-by: Ludvig Liljenberg <[email protected]>
1 parent e4a661a commit ab957f7

File tree

1 file changed

+174
-31
lines changed

1 file changed

+174
-31
lines changed

src/hyperlight_host/benches/benchmarks.rs

Lines changed: 174 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -30,19 +30,53 @@ fn create_multiuse_sandbox() -> MultiUseSandbox {
3030
create_uninit_sandbox().evolve().unwrap()
3131
}
3232

33+
fn create_sandbox_with_heap_size(heap_size_mb: Option<u64>) -> MultiUseSandbox {
34+
let path = simple_guest_as_string().unwrap();
35+
let config = if let Some(size_mb) = heap_size_mb {
36+
let mut config = SandboxConfiguration::default();
37+
config.set_heap_size(size_mb * 1024 * 1024); // Convert MB to bytes
38+
Some(config)
39+
} else {
40+
None
41+
};
42+
43+
let uninit_sandbox = UninitializedSandbox::new(GuestBinary::FilePath(path), config).unwrap();
44+
uninit_sandbox.evolve().unwrap()
45+
}
46+
3347
fn guest_call_benchmark(c: &mut Criterion) {
3448
let mut group = c.benchmark_group("guest_functions");
3549

36-
// Benchmarks a single guest function call.
37-
// The benchmark does **not** include the time to reset the sandbox memory after the call.
50+
// Single guest function call.
3851
group.bench_function("guest_call", |b| {
3952
let mut sbox = create_multiuse_sandbox();
4053

4154
b.iter(|| sbox.call::<String>("Echo", "hello\n".to_string()).unwrap());
4255
});
4356

44-
// Benchmarks a single guest function call.
45-
// The benchmark does include the time to reset the sandbox memory after the call.
57+
// Single snapshot restore after a guest function call.
58+
group.bench_function("guest_restore", |b| {
59+
let mut sbox = create_multiuse_sandbox();
60+
let snapshot = sbox.snapshot().unwrap();
61+
62+
b.iter_custom(|iters| {
63+
let mut total_duration = std::time::Duration::ZERO;
64+
65+
for _ in 0..iters {
66+
// Dirty some pages
67+
sbox.call::<String>("Echo", "hello\n".to_string()).unwrap();
68+
69+
// Measure only the restore operation
70+
let start = std::time::Instant::now();
71+
sbox.restore(&snapshot).unwrap();
72+
total_duration += start.elapsed();
73+
}
74+
75+
total_duration
76+
});
77+
});
78+
79+
// Single guest function call with a snapshot restore after
4680
group.bench_function("guest_call_with_restore", |b| {
4781
let mut sbox = create_multiuse_sandbox();
4882
let snapshot = sbox.snapshot().unwrap();
@@ -53,8 +87,7 @@ fn guest_call_benchmark(c: &mut Criterion) {
5387
});
5488
});
5589

56-
// Benchmarks a guest function call calling into the host.
57-
// The benchmark does **not** include the time to reset the sandbox memory after the call.
90+
// Single guest function call which includes a call to a host function.
5891
group.bench_function("guest_call_with_call_to_host_function", |b| {
5992
let mut uninitialized_sandbox = create_uninit_sandbox();
6093

@@ -75,37 +108,58 @@ fn guest_call_benchmark(c: &mut Criterion) {
75108
group.finish();
76109
}
77110

78-
fn guest_call_benchmark_large_param(c: &mut Criterion) {
79-
let mut group = c.benchmark_group("guest_functions_with_large_parameters");
111+
// Guest function call and restore, with large parameters passed as arguments.
112+
fn guest_call_benchmark_large_params(c: &mut Criterion) {
113+
let mut group = c.benchmark_group("2_large_parameters");
80114
#[cfg(target_os = "windows")]
81115
group.sample_size(10); // This benchmark is very slow on Windows, so we reduce the sample size to avoid long test runs.
82116

83-
// This benchmark includes time to first clone a vector and string, so it is not a "pure' benchmark of the guest call, but it's still useful
84-
group.bench_function("guest_call_with_large_parameters", |b| {
85-
const SIZE: usize = 50 * 1024 * 1024; // 50 MB
86-
let large_vec = vec![0u8; SIZE];
87-
let large_string = unsafe { String::from_utf8_unchecked(large_vec.clone()) }; // Safety: indeed above vec is valid utf8
117+
// Parameter sizes to test in MB. Each guest call will use two parameters of this size (vec and str).
118+
const PARAM_SIZES_MB: &[u64] = &[5, 20, 60];
88119

89-
let mut config = SandboxConfiguration::default();
90-
config.set_input_data_size(2 * SIZE + (1024 * 1024)); // 2 * SIZE + 1 MB, to allow 1MB for the rest of the serialized function call
91-
config.set_heap_size(SIZE as u64 * 15);
120+
for &param_size_mb in PARAM_SIZES_MB {
121+
let benchmark_name = format!("guest_call_restore_{}mb_params", param_size_mb);
122+
group.bench_function(&benchmark_name, |b| {
123+
let param_size_bytes = param_size_mb * 1024 * 1024;
92124

93-
let sandbox = UninitializedSandbox::new(
94-
GuestBinary::FilePath(simple_guest_as_string().unwrap()),
95-
Some(config),
96-
)
97-
.unwrap();
98-
let mut sandbox = sandbox.evolve().unwrap();
125+
let large_vec = vec![0u8; param_size_bytes as usize];
126+
let large_string = String::from_utf8(large_vec.clone()).unwrap();
99127

100-
b.iter(|| {
101-
sandbox
102-
.call_guest_function_by_name::<()>(
103-
"LargeParameters",
104-
(large_vec.clone(), large_string.clone()),
105-
)
106-
.unwrap()
128+
let mut config = SandboxConfiguration::default();
129+
config.set_heap_size(600 * 1024 * 1024);
130+
config.set_input_data_size(300 * 1024 * 1024);
131+
132+
let sandbox = UninitializedSandbox::new(
133+
GuestBinary::FilePath(simple_guest_as_string().unwrap()),
134+
Some(config),
135+
)
136+
.unwrap();
137+
let mut sandbox = sandbox.evolve().unwrap();
138+
let snapshot = sandbox.snapshot().unwrap();
139+
140+
// Iter_custom to avoid measure clone time of params
141+
b.iter_custom(|iters| {
142+
let mut total_duration = std::time::Duration::ZERO;
143+
144+
for _ in 0..iters {
145+
let vec_clone = large_vec.clone();
146+
let string_clone = large_string.clone();
147+
148+
let start = std::time::Instant::now();
149+
sandbox
150+
.call_guest_function_by_name::<()>(
151+
"LargeParameters",
152+
(vec_clone, string_clone),
153+
)
154+
.unwrap();
155+
sandbox.restore(&snapshot).unwrap();
156+
total_duration += start.elapsed();
157+
}
158+
159+
total_duration
160+
});
107161
});
108-
});
162+
}
109163

110164
group.finish();
111165
}
@@ -138,9 +192,98 @@ fn sandbox_benchmark(c: &mut Criterion) {
138192
group.finish();
139193
}
140194

195+
// Sandbox creation with different heap sizes
196+
fn sandbox_heap_size_benchmark(c: &mut Criterion) {
197+
let mut group = c.benchmark_group("sandbox_heap_sizes");
198+
199+
const HEAP_SIZES_MB: &[Option<u64>] = &[None, Some(50), Some(500), Some(995)];
200+
201+
// Benchmark sandbox creation with different heap sizes (including default)
202+
for &heap_size_mb in HEAP_SIZES_MB {
203+
let benchmark_name = match heap_size_mb {
204+
None => "create_sandbox_default_heap".to_string(),
205+
Some(size) => format!("create_sandbox_{}mb_heap", size),
206+
};
207+
group.bench_function(&benchmark_name, |b| {
208+
b.iter_with_large_drop(|| create_sandbox_with_heap_size(heap_size_mb));
209+
});
210+
}
211+
212+
group.finish();
213+
}
214+
215+
// Guest function call and restore with different heap sizes
216+
fn guest_call_heap_size_benchmark(c: &mut Criterion) {
217+
let mut group = c.benchmark_group("guest_call_restore_heap_sizes");
218+
219+
const HEAP_SIZES_MB: &[Option<u64>] = &[None, Some(50), Some(500), Some(995)];
220+
221+
// Benchmark guest function call with different heap sizes (including default)
222+
for &heap_size_mb in HEAP_SIZES_MB {
223+
let benchmark_name = match heap_size_mb {
224+
None => "guest_call_restore_default_heap".to_string(),
225+
Some(size) => format!("guest_call_restore_{}mb_heap", size),
226+
};
227+
group.bench_function(&benchmark_name, |b| {
228+
let mut sandbox = create_sandbox_with_heap_size(heap_size_mb);
229+
let snapshot = sandbox.snapshot().unwrap();
230+
231+
b.iter(|| {
232+
sandbox
233+
.call_guest_function_by_name::<String>("Echo", "hello\n".to_string())
234+
.unwrap();
235+
sandbox.restore(&snapshot).unwrap();
236+
});
237+
});
238+
}
239+
240+
group.finish();
241+
}
242+
243+
// Snapshot creation with varying heap size
244+
fn snapshot_benchmark(c: &mut Criterion) {
245+
let mut group = c.benchmark_group("snapshot");
246+
247+
const HEAP_SIZES_MB: &[Option<u64>] = &[None, Some(50), Some(500), Some(995)];
248+
249+
for &heap_size_mb in HEAP_SIZES_MB {
250+
let benchmark_name = match heap_size_mb {
251+
None => "default_heap".to_string(),
252+
Some(size) => format!("{}_mb_heap", size),
253+
};
254+
group.bench_function(&benchmark_name, |b| {
255+
let mut sandbox = create_sandbox_with_heap_size(heap_size_mb);
256+
let original_state = sandbox.snapshot().unwrap();
257+
258+
b.iter_custom(|iters| {
259+
let mut total_duration = std::time::Duration::ZERO;
260+
261+
for _ in 0..iters {
262+
// Dirty some pages
263+
sandbox
264+
.call::<String>("Echo", "hello\n".to_string())
265+
.unwrap();
266+
267+
// Measure only the snapshot operation
268+
let start = std::time::Instant::now();
269+
let _snapshot = sandbox.snapshot().unwrap();
270+
total_duration += start.elapsed();
271+
272+
// Restore the original state to avoid accumulating snapshots
273+
sandbox.restore(&original_state).unwrap();
274+
}
275+
276+
total_duration
277+
});
278+
});
279+
}
280+
281+
group.finish();
282+
}
283+
141284
criterion_group! {
142285
name = benches;
143286
config = Criterion::default();
144-
targets = guest_call_benchmark, sandbox_benchmark, guest_call_benchmark_large_param
287+
targets = guest_call_benchmark, sandbox_benchmark, sandbox_heap_size_benchmark, guest_call_benchmark_large_params, guest_call_heap_size_benchmark, snapshot_benchmark
145288
}
146289
criterion_main!(benches);

0 commit comments

Comments
 (0)