@@ -30,19 +30,75 @@ 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+
3347fn 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 after a restore.
80+ group. bench_function ( "guest_call_after_restore" , |b| {
81+ let mut sbox = create_multiuse_sandbox ( ) ;
82+ let snapshot = sbox. snapshot ( ) . unwrap ( ) ;
83+
84+ b. iter_custom ( |iters| {
85+ let mut total_duration = std:: time:: Duration :: ZERO ;
86+
87+ for _ in 0 ..iters {
88+ // Restore (not timed)
89+ sbox. restore ( & snapshot) . unwrap ( ) ;
90+
91+ // Measure only the guest function call
92+ let start = std:: time:: Instant :: now ( ) ;
93+ sbox. call :: < String > ( "Echo" , "hello\n " . to_string ( ) ) . unwrap ( ) ;
94+ total_duration += start. elapsed ( ) ;
95+ }
96+
97+ total_duration
98+ } ) ;
99+ } ) ;
100+
101+ // Single guest function call with a snapshot restore after
46102 group. bench_function ( "guest_call_with_restore" , |b| {
47103 let mut sbox = create_multiuse_sandbox ( ) ;
48104 let snapshot = sbox. snapshot ( ) . unwrap ( ) ;
@@ -53,8 +109,7 @@ fn guest_call_benchmark(c: &mut Criterion) {
53109 } ) ;
54110 } ) ;
55111
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.
112+ // Single guest function call which includes a call to a host function.
58113 group. bench_function ( "guest_call_with_call_to_host_function" , |b| {
59114 let mut uninitialized_sandbox = create_uninit_sandbox ( ) ;
60115
@@ -75,37 +130,58 @@ fn guest_call_benchmark(c: &mut Criterion) {
75130 group. finish ( ) ;
76131}
77132
78- fn guest_call_benchmark_large_param ( c : & mut Criterion ) {
79- let mut group = c. benchmark_group ( "guest_functions_with_large_parameters" ) ;
133+ // Guest function call and restore, with large parameters passed as arguments.
134+ fn guest_call_benchmark_large_params ( c : & mut Criterion ) {
135+ let mut group = c. benchmark_group ( "2_large_parameters" ) ;
80136 #[ cfg( target_os = "windows" ) ]
81137 group. sample_size ( 10 ) ; // This benchmark is very slow on Windows, so we reduce the sample size to avoid long test runs.
82138
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
139+ // Parameter sizes to test in MB. Each guest call will use two parameters of this size (vec and str).
140+ const PARAM_SIZES_MB : & [ u64 ] = & [ 5 , 20 , 60 ] ;
88141
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 ) ;
142+ for & param_size_mb in PARAM_SIZES_MB {
143+ let benchmark_name = format ! ( "guest_call_restore_{}mb_params" , param_size_mb) ;
144+ group. bench_function ( & benchmark_name, |b| {
145+ let param_size_bytes = param_size_mb * 1024 * 1024 ;
92146
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 ( ) ;
147+ let large_vec = vec ! [ 0u8 ; param_size_bytes as usize ] ;
148+ let large_string = String :: from_utf8 ( large_vec. clone ( ) ) . unwrap ( ) ;
99149
100- b. iter ( || {
101- sandbox
102- . call_guest_function_by_name :: < ( ) > (
103- "LargeParameters" ,
104- ( large_vec. clone ( ) , large_string. clone ( ) ) ,
105- )
106- . unwrap ( )
150+ let mut config = SandboxConfiguration :: default ( ) ;
151+ config. set_heap_size ( 600 * 1024 * 1024 ) ;
152+ config. set_input_data_size ( 300 * 1024 * 1024 ) ;
153+
154+ let sandbox = UninitializedSandbox :: new (
155+ GuestBinary :: FilePath ( simple_guest_as_string ( ) . unwrap ( ) ) ,
156+ Some ( config) ,
157+ )
158+ . unwrap ( ) ;
159+ let mut sandbox = sandbox. evolve ( ) . unwrap ( ) ;
160+ let snapshot = sandbox. snapshot ( ) . unwrap ( ) ;
161+
162+ // Iter_custom to avoid measure clone time of params
163+ b. iter_custom ( |iters| {
164+ let mut total_duration = std:: time:: Duration :: ZERO ;
165+
166+ for _ in 0 ..iters {
167+ let vec_clone = large_vec. clone ( ) ;
168+ let string_clone = large_string. clone ( ) ;
169+
170+ let start = std:: time:: Instant :: now ( ) ;
171+ sandbox
172+ . call_guest_function_by_name :: < ( ) > (
173+ "LargeParameters" ,
174+ ( vec_clone, string_clone) ,
175+ )
176+ . unwrap ( ) ;
177+ sandbox. restore ( & snapshot) . unwrap ( ) ;
178+ total_duration += start. elapsed ( ) ;
179+ }
180+
181+ total_duration
182+ } ) ;
107183 } ) ;
108- } ) ;
184+ }
109185
110186 group. finish ( ) ;
111187}
@@ -138,9 +214,98 @@ fn sandbox_benchmark(c: &mut Criterion) {
138214 group. finish ( ) ;
139215}
140216
217+ // Sandbox creation with different heap sizes
218+ fn sandbox_heap_size_benchmark ( c : & mut Criterion ) {
219+ let mut group = c. benchmark_group ( "sandbox_heap_sizes" ) ;
220+
221+ const HEAP_SIZES_MB : & [ Option < u64 > ] = & [ None , Some ( 50 ) , Some ( 500 ) , Some ( 995 ) ] ;
222+
223+ // Benchmark sandbox creation with different heap sizes (including default)
224+ for & heap_size_mb in HEAP_SIZES_MB {
225+ let benchmark_name = match heap_size_mb {
226+ None => "create_sandbox_default_heap" . to_string ( ) ,
227+ Some ( size) => format ! ( "create_sandbox_{}mb_heap" , size) ,
228+ } ;
229+ group. bench_function ( & benchmark_name, |b| {
230+ b. iter_with_large_drop ( || create_sandbox_with_heap_size ( heap_size_mb) ) ;
231+ } ) ;
232+ }
233+
234+ group. finish ( ) ;
235+ }
236+
237+ // Guest function call and restore with different heap sizes
238+ fn guest_call_heap_size_benchmark ( c : & mut Criterion ) {
239+ let mut group = c. benchmark_group ( "guest_call_restore_heap_sizes" ) ;
240+
241+ const HEAP_SIZES_MB : & [ Option < u64 > ] = & [ None , Some ( 50 ) , Some ( 500 ) , Some ( 995 ) ] ;
242+
243+ // Benchmark guest function call with different heap sizes (including default)
244+ for & heap_size_mb in HEAP_SIZES_MB {
245+ let benchmark_name = match heap_size_mb {
246+ None => "guest_call_restore_default_heap" . to_string ( ) ,
247+ Some ( size) => format ! ( "guest_call_restore_{}mb_heap" , size) ,
248+ } ;
249+ group. bench_function ( & benchmark_name, |b| {
250+ let mut sandbox = create_sandbox_with_heap_size ( heap_size_mb) ;
251+ let snapshot = sandbox. snapshot ( ) . unwrap ( ) ;
252+
253+ b. iter ( || {
254+ sandbox
255+ . call_guest_function_by_name :: < String > ( "Echo" , "hello\n " . to_string ( ) )
256+ . unwrap ( ) ;
257+ sandbox. restore ( & snapshot) . unwrap ( ) ;
258+ } ) ;
259+ } ) ;
260+ }
261+
262+ group. finish ( ) ;
263+ }
264+
265+ // Snapshot creation with varying heap size
266+ fn snapshot_benchmark ( c : & mut Criterion ) {
267+ let mut group = c. benchmark_group ( "snapshot" ) ;
268+
269+ const HEAP_SIZES_MB : & [ Option < u64 > ] = & [ None , Some ( 50 ) , Some ( 500 ) , Some ( 995 ) ] ;
270+
271+ for & heap_size_mb in HEAP_SIZES_MB {
272+ let benchmark_name = match heap_size_mb {
273+ None => "default_heap" . to_string ( ) ,
274+ Some ( size) => format ! ( "{}_mb_heap" , size) ,
275+ } ;
276+ group. bench_function ( & benchmark_name, |b| {
277+ let mut sandbox = create_sandbox_with_heap_size ( heap_size_mb) ;
278+ let original_state = sandbox. snapshot ( ) . unwrap ( ) ;
279+
280+ b. iter_custom ( |iters| {
281+ let mut total_duration = std:: time:: Duration :: ZERO ;
282+
283+ for _ in 0 ..iters {
284+ // Dirty some pages
285+ sandbox
286+ . call :: < String > ( "Echo" , "hello\n " . to_string ( ) )
287+ . unwrap ( ) ;
288+
289+ // Measure only the snapshot operation
290+ let start = std:: time:: Instant :: now ( ) ;
291+ let _snapshot = sandbox. snapshot ( ) . unwrap ( ) ;
292+ total_duration += start. elapsed ( ) ;
293+
294+ // Restore the original state to avoid accumulating snapshots
295+ sandbox. restore ( & original_state) . unwrap ( ) ;
296+ }
297+
298+ total_duration
299+ } ) ;
300+ } ) ;
301+ }
302+
303+ group. finish ( ) ;
304+ }
305+
141306criterion_group ! {
142307 name = benches;
143308 config = Criterion :: default ( ) ;
144- targets = guest_call_benchmark, sandbox_benchmark, guest_call_benchmark_large_param
309+ targets = guest_call_benchmark, sandbox_benchmark, sandbox_heap_size_benchmark , guest_call_benchmark_large_params , guest_call_heap_size_benchmark , snapshot_benchmark
145310}
146311criterion_main ! ( benches) ;
0 commit comments