Skip to content

Commit 9e6169a

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 9e6169a

File tree

1 file changed

+120
-25
lines changed

1 file changed

+120
-25
lines changed

src/hyperlight_host/benches/benchmarks.rs

Lines changed: 120 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,18 @@ 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+
sbox.call::<String>("Echo", "hello\n".to_string()).unwrap();
64+
65+
b.iter(|| {
66+
sbox.restore(&snapshot).unwrap();
67+
});
68+
});
69+
4470
// Benchmarks a single guest function call.
4571
// The benchmark does include the time to reset the sandbox memory after the call.
4672
group.bench_function("guest_call_with_restore", |b| {
@@ -75,37 +101,57 @@ fn guest_call_benchmark(c: &mut Criterion) {
75101
group.finish();
76102
}
77103

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

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

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);
112+
for &param_size_mb in PARAM_SIZES_MB {
113+
let benchmark_name = format!("guest_call_restore_{}mb_params", param_size_mb);
114+
group.bench_function(&benchmark_name, |b| {
115+
let param_size_bytes = param_size_mb * 1024 * 1024;
92116

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

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

110156
group.finish();
111157
}
@@ -138,9 +184,58 @@ fn sandbox_benchmark(c: &mut Criterion) {
138184
group.finish();
139185
}
140186

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

0 commit comments

Comments
 (0)