Skip to content

Commit a9516b8

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 a9516b8

File tree

1 file changed

+131
-25
lines changed

1 file changed

+131
-25
lines changed

src/hyperlight_host/benches/benchmarks.rs

Lines changed: 131 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,20 @@ 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

@@ -41,6 +55,29 @@ fn guest_call_benchmark(c: &mut Criterion) {
4155
b.iter(|| sbox.call::<String>("Echo", "hello\n".to_string()).unwrap());
4256
});
4357

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

78-
fn guest_call_benchmark_large_param(c: &mut Criterion) {
79-
let mut group = c.benchmark_group("guest_functions_with_large_parameters");
115+
fn guest_call_benchmark_large_params(c: &mut Criterion) {
116+
let mut group = c.benchmark_group("2_large_parameters");
80117
#[cfg(target_os = "windows")]
81118
group.sample_size(10); // This benchmark is very slow on Windows, so we reduce the sample size to avoid long test runs.
82119

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
120+
// Parameter sizes to test in MB. Each guest call will use two parameters of this size (vec and str).
121+
const PARAM_SIZES_MB: &[u64] = &[5, 20, 60];
88122

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);
123+
for &param_size_mb in PARAM_SIZES_MB {
124+
let benchmark_name = format!("guest_call_restore_{}mb_params", param_size_mb);
125+
group.bench_function(&benchmark_name, |b| {
126+
let param_size_bytes = param_size_mb * 1024 * 1024;
92127

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();
128+
let large_vec = vec![0u8; param_size_bytes as usize];
129+
let large_string = String::from_utf8(large_vec.clone()).unwrap();
99130

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

110167
group.finish();
111168
}
@@ -138,9 +195,58 @@ fn sandbox_benchmark(c: &mut Criterion) {
138195
group.finish();
139196
}
140197

198+
fn sandbox_heap_size_benchmark(c: &mut Criterion) {
199+
let mut group = c.benchmark_group("sandbox_heap_sizes");
200+
201+
const HEAP_SIZES_MB: &[u64] = &[50, 500, 995];
202+
203+
// Benchmark sandbox creation with default heap size
204+
group.bench_function("create_sandbox_default_heap", |b| {
205+
b.iter_with_large_drop(|| create_sandbox_with_heap_size(None));
206+
});
207+
208+
// Benchmark sandbox creation with different heap sizes
209+
for &heap_size_mb in HEAP_SIZES_MB {
210+
let benchmark_name = format!("create_sandbox_{}mb_heap", heap_size_mb);
211+
group.bench_function(&benchmark_name, |b| {
212+
b.iter_with_large_drop(|| create_sandbox_with_heap_size(Some(heap_size_mb)));
213+
});
214+
}
215+
216+
group.finish();
217+
}
218+
219+
fn guest_call_heap_size_benchmark(c: &mut Criterion) {
220+
let mut group = c.benchmark_group("guest_call_restore_heap_sizes");
221+
222+
const HEAP_SIZES_MB: &[Option<u64>] =
223+
&[None, Some(50), Some(100), Some(250), Some(500), Some(995)];
224+
225+
// Benchmark guest function call with different heap sizes (including default)
226+
for &heap_size_mb in HEAP_SIZES_MB {
227+
let benchmark_name = match heap_size_mb {
228+
None => "guest_call_restore_default_mb_heap".to_string(),
229+
Some(size) => format!("guest_call_restore_{}_mb_heap", size),
230+
};
231+
group.bench_function(&benchmark_name, |b| {
232+
let mut sandbox = create_sandbox_with_heap_size(heap_size_mb);
233+
let snapshot = sandbox.snapshot().unwrap();
234+
235+
b.iter(|| {
236+
sandbox
237+
.call_guest_function_by_name::<String>("Echo", "hello\n".to_string())
238+
.unwrap();
239+
sandbox.restore(&snapshot).unwrap();
240+
});
241+
});
242+
}
243+
244+
group.finish();
245+
}
246+
141247
criterion_group! {
142248
name = benches;
143249
config = Criterion::default();
144-
targets = guest_call_benchmark, sandbox_benchmark, guest_call_benchmark_large_param
250+
targets = guest_call_benchmark, sandbox_benchmark, sandbox_heap_size_benchmark, guest_call_benchmark_large_params, guest_call_heap_size_benchmark
145251
}
146252
criterion_main!(benches);

0 commit comments

Comments
 (0)