@@ -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+
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 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+
141284criterion_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}
146289criterion_main ! ( benches) ;
0 commit comments